增加dashboard

This commit is contained in:
zhouhao 2020-02-03 10:35:53 +08:00
parent aa997899b4
commit 76d8411139
36 changed files with 1197 additions and 0 deletions

View File

@ -0,0 +1,95 @@
# 仪表盘组件
获取所有类型
/dashboard/defs
[
{
"id":"system",
"name":"系统",
"objects":[
{
"id":"memory",
"name":"内存""
},
{
"id":"cpu",
"name":"CPU""
}
]
}
]
获取类型支持的指标和维度
/dashboard/类型/对象/指标
/dashboard/system/memory/measurements
[
{
"id":"max",
"name":"最大值",
"dimensions":[ //指标支持的维度
{"id":"time-interval","name":"历史数据","params":{"properties":[ ... ]}},
{"id":"real-time","name":"实时数据","params":{"properties":[ ... ]}}
]
},
{
"id":"usage",
"name":"已使用"
}
]
POST /dashboard/system/memory/_batch
[
{
"measurement":"usage",
"dimension":"time-interval",
"params":{"interval":"1s"}
},
{
"measurement":"max",
"dimension":"time-interval",
"params":{"interval":"1s"}
}
]
{
"usage":[
{
"time":"15:00",
"value":1.2
},
{
"time":"16:00",
"value":1.3
}
]
,
"max":[
{
"time":"15:00",
"value":2
},
{
"time":"16:00",
"value":2
}
]
}
/dashboard/类型/对象/维度/指标?参数
GET /dashboard/device/property/temp/realTime?history=10
{
"timestamp":1578914267417,
"value":5.6
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jetlinks-components</artifactId>
<groupId>org.jetlinks.community</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dashboard-components</artifactId>
<dependencies>
<dependency>
<groupId>org.jetlinks</groupId>
<artifactId>jetlinks-core</artifactId>
<version>${jetlinks.version}</version>
</dependency>
<dependency>
<groupId>org.hswebframework</groupId>
<artifactId>hsweb-easy-orm-rdb</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,23 @@
package org.jetlinks.community.dashboard;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 通用维度定义
*
* @author zhouhao
*/
@AllArgsConstructor
@Getter
public enum CommonDimensionDefinition implements DimensionDefinition {
realTime("实时"),
history("历史");
private String name;
@Override
public String getId() {
return name();
}
}

View File

@ -0,0 +1,27 @@
package org.jetlinks.community.dashboard;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 通用指标定义
*
* @author zhouhao
*/
@AllArgsConstructor
@Getter
public enum CommonMeasurementDefinition implements MeasurementDefinition {
usage("使用率"),
used("已使用"),
info("明细"),
max("最大值"),
min("最小值"),
avg("平均值");
private String name;
@Override
public String getId() {
return name();
}
}

View File

@ -0,0 +1,14 @@
package org.jetlinks.community.dashboard;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface Dashboard {
DashboardDefinition getDefinition();
Flux<DashboardObject> getObjects();
Mono<DashboardObject> getObject(String id);
}

View File

@ -0,0 +1,5 @@
package org.jetlinks.community.dashboard;
public interface DashboardDefinition extends Definition {
}

View File

@ -0,0 +1,12 @@
package org.jetlinks.community.dashboard;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface DashboardManager {
Flux<Dashboard> getDashboards();
Mono<Dashboard> getDashboard(String id);
}

View File

@ -0,0 +1,17 @@
package org.jetlinks.community.dashboard;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* 仪表对象: CPU,内存
*/
public interface DashboardObject {
ObjectDefinition getDefinition();
Flux<Measurement> getMeasurements();
Mono<Measurement> getMeasurement(String id);
}

View File

@ -0,0 +1,31 @@
package org.jetlinks.community.dashboard;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.hswebframework.web.dict.EnumDict;
@AllArgsConstructor
@Getter
public enum DefaultDashboardDefinition implements DashboardDefinition, EnumDict<String> {
systemMonitor("系统监控")
;
private String name;
@Override
public String getId() {
return name();
}
@Override
public String getValue() {
return getId();
}
@Override
public String getText() {
return name;
}
}

View File

@ -0,0 +1,10 @@
package org.jetlinks.community.dashboard;
public interface Definition {
String getId();
String getName();
}

View File

@ -0,0 +1,5 @@
package org.jetlinks.community.dashboard;
public interface DimensionDefinition extends Definition {
}

View File

@ -0,0 +1,31 @@
package org.jetlinks.community.dashboard;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* 度量,指标. : 使用率
*
* @author zhouhao
* @since 1.0
*/
public interface Measurement {
MeasurementDefinition getDefinition();
/**
* 获取所有指标维度
*
* @return 维度
*/
Flux<MeasurementDimension> getDimensions();
/**
* 获取指定ID的维度
*
* @param id 维度定义ID
* @return 指定的维度, 不存在则返回 {@link Mono#empty()}
*/
Mono<MeasurementDimension> getDimension(String id);
}

View File

@ -0,0 +1,5 @@
package org.jetlinks.community.dashboard;
public interface MeasurementDefinition extends Definition {
}

View File

@ -0,0 +1,23 @@
package org.jetlinks.community.dashboard;
import org.jetlinks.core.metadata.ConfigMetadata;
import org.jetlinks.core.metadata.DataType;
import reactor.core.publisher.Flux;
/**
* 指标维度,: 每小时,服务器1
* @author zhouhao
*/
public interface MeasurementDimension {
DimensionDefinition getDefinition();
DataType getValueType();
ConfigMetadata getParams();
boolean isRealTime();
Flux<MeasurementValue> getValue(MeasurementParameter parameter);
}

View File

@ -0,0 +1,19 @@
package org.jetlinks.community.dashboard;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hswebframework.ezorm.core.param.QueryParam;
import java.util.HashMap;
import java.util.Map;
@Getter
@Setter
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class MeasurementParameter {
private Map<String,Object> params=new HashMap<>();
}

View File

@ -0,0 +1,11 @@
package org.jetlinks.community.dashboard;
public interface MeasurementValue {
Object getValue();
String getTimeString();
long getTimestamp();
}

View File

@ -0,0 +1,5 @@
package org.jetlinks.community.dashboard;
public interface ObjectDefinition extends Definition {
}

View File

@ -0,0 +1,18 @@
package org.jetlinks.community.dashboard;
import lombok.*;
@Getter
@Setter
@Builder
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class SimpleMeasurementValue implements MeasurementValue {
private Object value;
private String timeString;
private long timestamp;
}

View File

@ -0,0 +1,121 @@
package org.jetlinks.community.dashboard.measurements;
import lombok.SneakyThrows;
import org.hswebframework.utils.time.DateFormatter;
import org.jetlinks.community.dashboard.*;
import org.jetlinks.community.dashboard.supports.StaticMeasurement;
import org.jetlinks.community.dashboard.supports.StaticMeasurementProvider;
import org.jetlinks.core.metadata.ConfigMetadata;
import org.jetlinks.core.metadata.DataType;
import org.jetlinks.core.metadata.types.DoubleType;
import org.jetlinks.core.metadata.unit.UnifyUnit;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.Duration;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import static java.math.BigDecimal.ROUND_HALF_UP;
/**
* 实时CPU 使用率监控
* <pre>
* /dashboard/systemMonitor/cpu/usage/realTime
* </pre>
* @author zhouhao
*/
@Component
public class SystemCpuMeasurementProvider
extends StaticMeasurementProvider {
public SystemCpuMeasurementProvider() {
super(DefaultDashboardDefinition.systemMonitor, SystemObjectDefinition.cpu);
addMeasurement(cpuUseAgeMeasurement);
}
static DataType type = new DoubleType().scale(1).min(0).max(100).unit(UnifyUnit.percent);
static StaticMeasurement cpuUseAgeMeasurement = new StaticMeasurement(CommonMeasurementDefinition.usage)
.addDimension(new CpuRealTimeMeasurementDimension());
static OperatingSystemMXBean osMxBean = ManagementFactory.getOperatingSystemMXBean();
static Callable<Double> processCpuUsage;
static Callable<Double> systemCpuUsage;
private static final List<String> OPERATING_SYSTEM_BEAN_CLASS_NAMES = Arrays.asList(
"com.sun.management.OperatingSystemMXBean", // HotSpot
"com.ibm.lang.management.OperatingSystemMXBean" // J9
);
static {
Class<?> mxBeanClass = null;
for (String s : OPERATING_SYSTEM_BEAN_CLASS_NAMES) {
try {
mxBeanClass = Class.forName(s);
} catch (Exception ignore) {
}
}
try {
if (mxBeanClass != null) {
Method method = mxBeanClass.getMethod("getProcessCpuLoad");
Method system = mxBeanClass.getMethod("getSystemCpuLoad");
processCpuUsage = () -> (double) method.invoke(osMxBean);
systemCpuUsage = () -> (double) system.invoke(osMxBean);
} else {
processCpuUsage = () -> 0D;
systemCpuUsage = () -> 0D;
}
} catch (Exception e) {
e.printStackTrace();
}
}
static class CpuRealTimeMeasurementDimension implements MeasurementDimension {
@Override
public DimensionDefinition getDefinition() {
return CommonDimensionDefinition.realTime;
}
@Override
public DataType getValueType() {
return type;
}
@Override
public ConfigMetadata getParams() {
return null;
}
@Override
public boolean isRealTime() {
return true;
}
@SneakyThrows
public double getProcessCpu() {
return processCpuUsage.call() * 100;
}
@Override
public Flux<MeasurementValue> getValue(MeasurementParameter parameter) {
// TODO: 2020/1/15 性能优化
return Flux.interval(Duration.ofSeconds(1))
.map(t -> SimpleMeasurementValue.of(BigDecimal.valueOf(getProcessCpu()).setScale(1, ROUND_HALF_UP),
DateFormatter.toString(new Date(), "HH:mm:ss"),
System.currentTimeMillis()))
.cast(MeasurementValue.class);
}
}
}

View File

@ -0,0 +1,126 @@
package org.jetlinks.community.dashboard.measurements;
import lombok.Getter;
import lombok.Setter;
import org.hswebframework.utils.time.DateFormatter;
import org.jetlinks.community.dashboard.*;
import org.jetlinks.community.dashboard.supports.StaticMeasurement;
import org.jetlinks.community.dashboard.supports.StaticMeasurementProvider;
import org.jetlinks.core.metadata.ConfigMetadata;
import org.jetlinks.core.metadata.DataType;
import org.jetlinks.core.metadata.SimplePropertyMetadata;
import org.jetlinks.core.metadata.types.DoubleType;
import org.jetlinks.core.metadata.types.LongType;
import org.jetlinks.core.metadata.types.ObjectType;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import java.lang.management.*;
import java.math.BigDecimal;
import java.time.Duration;
import java.util.Date;
import static java.math.BigDecimal.ROUND_HALF_UP;
/**
* 实时内存使用率监控
* <pre>
* /dashboard/systemMonitor/memory/info/realTime
* </pre>
*
* @author zhouhao
*/
@Component
public class SystemMemoryMeasurementProvider extends StaticMeasurementProvider {
public SystemMemoryMeasurementProvider() {
super(DefaultDashboardDefinition.systemMonitor, SystemObjectDefinition.memory);
addMeasurement(jvmMemoryInfo);
}
static ObjectType type = new ObjectType();
static {
{
SimplePropertyMetadata metadata = new SimplePropertyMetadata();
metadata.setId("max");
metadata.setName("最大值");
metadata.setValueType(new LongType());
type.addPropertyMetadata(metadata);
}
{
SimplePropertyMetadata metadata = new SimplePropertyMetadata();
metadata.setId("used");
metadata.setName("已使用");
metadata.setValueType(new LongType());
type.addPropertyMetadata(metadata);
}
{
SimplePropertyMetadata metadata = new SimplePropertyMetadata();
metadata.setId("usage");
metadata.setName("使用率");
metadata.setValueType(new DoubleType());
type.addPropertyMetadata(metadata);
}
}
static StaticMeasurement jvmMemoryInfo = new StaticMeasurement(CommonMeasurementDefinition.info)
.addDimension(new JvmMemoryInfoDimension());
static class JvmMemoryInfoDimension implements MeasurementDimension {
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
@Override
public DimensionDefinition getDefinition() {
return CommonDimensionDefinition.realTime;
}
@Override
public DataType getValueType() {
return type;
}
@Override
public ConfigMetadata getParams() {
return null;
}
@Override
public boolean isRealTime() {
return true;
}
@Override
public Flux<MeasurementValue> getValue(MeasurementParameter parameter) {
// TODO: 2020/1/15 性能优化
return Flux.interval(Duration.ofSeconds(1))
.map(t -> SimpleMeasurementValue.of(MemoryInfo.of(memoryMXBean.getHeapMemoryUsage()),
DateFormatter.toString(new Date(), "HH:mm:ss"),
System.currentTimeMillis()))
.cast(MeasurementValue.class);
}
}
@Getter
@Setter
public static class MemoryInfo {
private long max;
private long used;
private double usage;
public static MemoryInfo of(MemoryUsage usage) {
MemoryInfo info = new MemoryInfo();
info.max = (usage.getMax()) / 1000 / 1000;
info.used = usage.getUsed() / 1000 / 1000;
info.usage = BigDecimal.valueOf(((double) usage.getMax() / usage.getUsed()) / 100D).setScale(2, ROUND_HALF_UP)
.doubleValue();
return info;
}
}
}

View File

@ -0,0 +1,20 @@
package org.jetlinks.community.dashboard.measurements;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.jetlinks.community.dashboard.ObjectDefinition;
@Getter
@AllArgsConstructor
public enum SystemObjectDefinition implements ObjectDefinition {
cpu("CPU"),
memory("内存");
private String name;
@Override
public String getId() {
return name();
}
}

View File

@ -0,0 +1,47 @@
package org.jetlinks.community.dashboard.supports;
import lombok.Getter;
import org.jetlinks.community.dashboard.Dashboard;
import org.jetlinks.community.dashboard.DashboardDefinition;
import org.jetlinks.community.dashboard.DashboardObject;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
class CompositeDashboard implements Dashboard {
@Getter
private DashboardDefinition definition;
public CompositeDashboard(DashboardDefinition definition) {
this.definition = definition;
}
private Map<String, DashboardObject> staticObjects = new ConcurrentHashMap<>();
public void addProvider(MeasurementProvider provider) {
DashboardObject object = staticObjects.computeIfAbsent(provider.getObjectDefinition().getId(), __ -> new CompositeDashboardObject());
if(object instanceof CompositeDashboardObject){
CompositeDashboardObject compose = ((CompositeDashboardObject) object);
compose.addProvider(provider);
}
}
public void addObject(DashboardObject object) {
staticObjects.put(object.getDefinition().getId(), object);
}
@Override
public Flux<DashboardObject> getObjects() {
return Flux.fromIterable(staticObjects.values());
}
@Override
public Mono<DashboardObject> getObject(String id) {
return Mono.justOrEmpty(staticObjects.get(id));
}
}

View File

@ -0,0 +1,45 @@
package org.jetlinks.community.dashboard.supports;
import org.apache.commons.collections.CollectionUtils;
import org.jetlinks.community.dashboard.DashboardObject;
import org.jetlinks.community.dashboard.Measurement;
import org.jetlinks.community.dashboard.ObjectDefinition;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
class CompositeDashboardObject implements DashboardObject {
private ObjectDefinition definition;
private List<MeasurementProvider> providers = new CopyOnWriteArrayList<>();
public void addProvider(MeasurementProvider provider) {
if (definition == null) {
definition = provider.getObjectDefinition();
}
providers.add(provider);
}
@Override
public ObjectDefinition getDefinition() {
return definition;
}
@Override
public Flux<Measurement> getMeasurements() {
return Flux.fromIterable(providers)
.flatMap(MeasurementProvider::getMeasurements);
}
@Override
public Mono<Measurement> getMeasurement(String id) {
return Flux.fromIterable(providers)
.flatMap(provider -> provider.getMeasurement(id))
.collectList()
.filter(CollectionUtils::isNotEmpty)
.map(CompositeMeasurement::new);
}
}

View File

@ -0,0 +1,42 @@
package org.jetlinks.community.dashboard.supports;
import org.jetlinks.community.dashboard.MeasurementDimension;
import org.jetlinks.community.dashboard.Measurement;
import org.jetlinks.community.dashboard.MeasurementDefinition;
import org.springframework.util.Assert;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.List;
class CompositeMeasurement implements Measurement {
private List<Measurement> measurements;
private Measurement main;
public CompositeMeasurement(List<Measurement> measurements) {
Assert.notEmpty(measurements, "measurements can not be empty");
this.measurements = measurements;
this.main = measurements.get(0);
}
@Override
public MeasurementDefinition getDefinition() {
return main.getDefinition();
}
@Override
public Flux<MeasurementDimension> getDimensions() {
return Flux.fromIterable(measurements)
.flatMap(Measurement::getDimensions);
}
@Override
public Mono<MeasurementDimension> getDimension(String id) {
return Flux.fromIterable(measurements)
.flatMap(measurement -> measurement.getDimension(id))
.next();
}
}

View File

@ -0,0 +1,59 @@
package org.jetlinks.community.dashboard.supports;
import org.jetlinks.community.dashboard.DashboardDefinition;
import org.jetlinks.community.dashboard.DashboardManager;
import org.jetlinks.community.dashboard.Dashboard;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class DefaultDashboardManager implements DashboardManager, BeanPostProcessor {
private Map<String, Dashboard> dashboards = new ConcurrentHashMap<>();
@Override
public Flux<Dashboard> getDashboards() {
return Flux.fromIterable(dashboards.values());
}
@Override
public Mono<Dashboard> getDashboard(String id) {
return Mono.justOrEmpty(dashboards.get(id));
}
private void addProvider(MeasurementProvider provider) {
DashboardDefinition definition = provider.getDashboardDefinition();
Dashboard dashboard = dashboards.computeIfAbsent(definition.getId(), __ -> new CompositeDashboard(definition));
if (dashboard instanceof CompositeDashboard) {
CompositeDashboard compose = ((CompositeDashboard) dashboard);
compose.addProvider(provider);
} else {
throw new UnsupportedOperationException("unsupported register dashboard object : " + provider);
}
}
private void addDashboard(Dashboard dashboard) {
dashboards.put(dashboard.getDefinition().getId(), dashboard);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MeasurementProvider) {
addProvider(((MeasurementProvider) bean));
} else if (bean instanceof Dashboard) {
addDashboard(((Dashboard) bean));
}
return bean;
}
}

View File

@ -0,0 +1,34 @@
package org.jetlinks.community.dashboard.supports;
import org.jetlinks.community.dashboard.*;
import org.jetlinks.community.dashboard.measurements.SystemObjectDefinition;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface MeasurementProvider {
/**
* @return 仪表定义
* @see DefaultDashboardDefinition
*/
DashboardDefinition getDashboardDefinition();
/**
* @return 对象定义
* @see SystemObjectDefinition
*/
ObjectDefinition getObjectDefinition();
/**
* @return 全部指标
*/
Flux<Measurement> getMeasurements();
/**
* @param id 指标ID {@link Measurement#getDefinition()} {@link MeasurementDefinition#getId()}
* @return 对应等指标, 不存在则返回 {@link Mono#empty()}
* @see MeasurementDefinition
*/
Mono<Measurement> getMeasurement(String id);
}

View File

@ -0,0 +1,42 @@
package org.jetlinks.community.dashboard.supports;
import lombok.Getter;
import org.jetlinks.community.dashboard.Measurement;
import org.jetlinks.community.dashboard.MeasurementDefinition;
import org.jetlinks.community.dashboard.MeasurementDimension;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class StaticMeasurement implements Measurement {
@Getter
private MeasurementDefinition definition;
public StaticMeasurement(MeasurementDefinition definition) {
this.definition = definition;
}
private Map<String, MeasurementDimension> dimensions = new ConcurrentHashMap<>();
public StaticMeasurement addDimension(MeasurementDimension dimension) {
dimensions.put(dimension.getDefinition().getId(), dimension);
return this;
}
@Override
public Flux<MeasurementDimension> getDimensions() {
return Flux.fromIterable(dimensions.values());
}
@Override
public Mono<MeasurementDimension> getDimension(String id) {
return Mono.justOrEmpty(dimensions.get(id));
}
}

View File

@ -0,0 +1,41 @@
package org.jetlinks.community.dashboard.supports;
import lombok.Getter;
import org.jetlinks.community.dashboard.DashboardDefinition;
import org.jetlinks.community.dashboard.Measurement;
import org.jetlinks.community.dashboard.ObjectDefinition;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public abstract class StaticMeasurementProvider implements MeasurementProvider {
private Map<String, Measurement> measurements = new ConcurrentHashMap<>();
@Getter
private DashboardDefinition dashboardDefinition;
@Getter
private ObjectDefinition objectDefinition;
public StaticMeasurementProvider(DashboardDefinition dashboardDefinition,
ObjectDefinition objectDefinition) {
this.dashboardDefinition = dashboardDefinition;
this.objectDefinition = objectDefinition;
}
protected void addMeasurement(Measurement measurement) {
measurements.put(measurement.getDefinition().getId(), measurement);
}
@Override
public Flux<Measurement> getMeasurements() {
return Flux.fromIterable(measurements.values());
}
@Override
public Mono<Measurement> getMeasurement(String id) {
return Mono.justOrEmpty(measurements.get(id));
}
}

View File

@ -0,0 +1,73 @@
package org.jetlinks.community.dashboard.web;
import com.alibaba.fastjson.JSON;
import org.hswebframework.web.exception.NotFoundException;
import org.jetlinks.community.dashboard.DashboardManager;
import org.jetlinks.community.dashboard.DashboardObject;
import org.jetlinks.community.dashboard.MeasurementParameter;
import org.jetlinks.community.dashboard.MeasurementValue;
import org.jetlinks.community.dashboard.web.response.DashboardMeasurementResponse;
import org.jetlinks.community.dashboard.web.response.MeasurementInfo;
import org.jetlinks.community.dashboard.web.request.DashboardMeasurementRequest;
import org.jetlinks.community.dashboard.web.response.DashboardInfo;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Map;
@RestController
@RequestMapping("/dashboard")
public class DashboardController {
private final DashboardManager dashboardManager;
public DashboardController(DashboardManager dashboardManager) {
this.dashboardManager = dashboardManager;
}
@GetMapping("/defs")
public Flux<DashboardInfo> getDefinitions() {
return dashboardManager
.getDashboards()
.flatMap(DashboardInfo::of);
}
@GetMapping("/def/{dashboard}/{object}/measurements")
public Flux<MeasurementInfo> getMeasurementDefinitions(@PathVariable String dashboard,
@PathVariable String object) {
return dashboardManager
.getDashboard(dashboard)
.flatMap(dash -> dash.getObject(object))
.flatMapMany(DashboardObject::getMeasurements)
.flatMap(MeasurementInfo::of);
}
@GetMapping(value = "/{dashboard}/{object}/{measurement}/{dimension}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<MeasurementValue> getMeasurementValue(@PathVariable String dashboard,
@PathVariable String object,
@PathVariable String dimension,
@PathVariable String measurement,
@RequestParam Map<String, Object> params) {
return dashboardManager
.getDashboard(dashboard)
.flatMap(dash -> dash.getObject(object))
.flatMap(obj -> obj.getMeasurement(measurement))
.flatMap(meas -> meas.getDimension(dimension))
.switchIfEmpty(Mono.error(() -> new NotFoundException("不支持的仪表盘")))
.flatMapMany(dim -> dim.getValue(MeasurementParameter.of(params)));
}
@GetMapping(value = "/_multi", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<DashboardMeasurementResponse> getMultiMeasurementValue(@RequestParam String requestJson) {
return Flux.fromIterable(JSON.parseArray(requestJson, DashboardMeasurementRequest.class))
.flatMap(request -> dashboardManager
.getDashboard(request.getDashboard())
.flatMap(dash -> dash.getObject(request.getObject()))
.flatMap(obj -> obj.getMeasurement(request.getMeasurement()))
.flatMap(meas -> meas.getDimension(request.getDimension()))
.flatMapMany(dim -> dim.getValue(MeasurementParameter.of(request.getParams())))
.map(val -> DashboardMeasurementResponse.of(request.getGroup(), val)));
}
}

View File

@ -0,0 +1,24 @@
package org.jetlinks.community.dashboard.web.request;
import lombok.Getter;
import lombok.Setter;
import java.util.Map;
@Getter
@Setter
public class DashboardMeasurementRequest {
private String group;
private String dashboard;
private String object;
private String measurement;
private String dimension;
private Map<String,Object> params;
}

View File

@ -0,0 +1,34 @@
package org.jetlinks.community.dashboard.web.response;
import lombok.Getter;
import lombok.Setter;
import org.jetlinks.community.dashboard.Dashboard;
import reactor.core.publisher.Mono;
import java.util.List;
@Getter
@Setter
public class DashboardInfo {
private String id;
private String name;
private List<ObjectInfo> objects;
public static Mono<DashboardInfo> of(Dashboard dashboard) {
return dashboard.getObjects()
.map(ObjectInfo::of)
.collectList()
.map(list -> {
DashboardInfo dashboardInfo = new DashboardInfo();
dashboardInfo.setId(dashboard.getDefinition().getId());
dashboardInfo.setName(dashboard.getDefinition().getName());
dashboardInfo.setObjects(list);
return dashboardInfo;
});
}
}

View File

@ -0,0 +1,20 @@
package org.jetlinks.community.dashboard.web.response;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.jetlinks.community.dashboard.MeasurementValue;
@Getter
@Setter
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class DashboardMeasurementResponse {
private String group;
private MeasurementValue data;
}

View File

@ -0,0 +1,29 @@
package org.jetlinks.community.dashboard.web.response;
import lombok.Getter;
import lombok.Setter;
import org.jetlinks.core.metadata.ConfigMetadata;
import org.jetlinks.core.metadata.DataType;
import org.jetlinks.community.dashboard.MeasurementDimension;
@Getter
@Setter
public class DimensionInfo {
private String id;
private String name;
private DataType type;
private ConfigMetadata params;
public static DimensionInfo of(MeasurementDimension dimension){
DimensionInfo dimensionInfo=new DimensionInfo();
dimensionInfo.setId(dimension.getDefinition().getId());
dimensionInfo.setName(dimension.getDefinition().getName());
dimensionInfo.setParams(dimension.getParams());
dimensionInfo.setType(dimension.getValueType());
return dimensionInfo;
}
}

View File

@ -0,0 +1,35 @@
package org.jetlinks.community.dashboard.web.response;
import lombok.Getter;
import lombok.Setter;
import org.jetlinks.core.metadata.DataType;
import org.jetlinks.community.dashboard.Measurement;
import reactor.core.publisher.Mono;
import java.util.List;
@Getter
@Setter
public class MeasurementInfo {
private String id;
private String name;
private DataType type;
private List<DimensionInfo> dimensions;
public static Mono<MeasurementInfo> of(Measurement measurement){
return measurement.getDimensions()
.map(DimensionInfo::of)
.collectList()
.map(list->{
MeasurementInfo info=new MeasurementInfo();
info.setId(measurement.getDefinition().getId());
info.setName(measurement.getDefinition().getName());
info.setDimensions(list);
return info;
});
}
}

View File

@ -0,0 +1,24 @@
package org.jetlinks.community.dashboard.web.response;
import lombok.Getter;
import lombok.Setter;
import org.jetlinks.community.dashboard.DashboardObject;
@Getter
@Setter
public class ObjectInfo {
private String id;
private String name;
public static ObjectInfo of(DashboardObject object){
ObjectInfo objectInfo=new ObjectInfo();
objectInfo.setName(object.getDefinition().getName());
objectInfo.setId(object.getDefinition().getId());
return objectInfo;
}
}

View File

@ -16,6 +16,8 @@
<module>gateway-component</module>
<module>io-component</module>
<module>elasticsearch-component</module>
<module>timeseries-components</module>
<module>dashboard-components</module>
</modules>
<artifactId>jetlinks-components</artifactId>