package org.jetlinks.community.device.measurements; import org.hswebframework.web.api.crud.entity.QueryParamEntity; import org.jetlinks.core.message.property.ReadPropertyMessageReply; import org.jetlinks.core.message.property.ReportPropertyMessage; import org.jetlinks.core.message.property.WritePropertyMessageReply; import org.jetlinks.core.metadata.*; import org.jetlinks.core.metadata.types.ObjectType; import org.jetlinks.core.metadata.types.StringType; import org.jetlinks.community.dashboard.*; import org.jetlinks.community.dashboard.supports.StaticMeasurement; import org.jetlinks.community.device.message.DeviceMessageUtils; import org.jetlinks.community.gateway.MessageGateway; import org.jetlinks.community.gateway.Subscription; import org.jetlinks.community.timeseries.TimeSeriesService; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; class DevicePropertiesMeasurement extends StaticMeasurement { private final MessageGateway messageGateway; private final TimeSeriesService timeSeriesService; private final DeviceMetadata metadata; private final String productId; public DevicePropertiesMeasurement(String productId, MessageGateway messageGateway, DeviceMetadata deviceMetadata, TimeSeriesService timeSeriesService) { super(MeasurementDefinition.of("properties", "属性记录")); this.productId = productId; this.messageGateway = messageGateway; this.timeSeriesService = timeSeriesService; this.metadata = deviceMetadata; addDimension(new RealTimeDevicePropertyDimension()); addDimension(new HistoryDevicePropertyDimension()); } static AtomicLong num = new AtomicLong(); Flux<SimpleMeasurementValue> fromHistory(String deviceId, int history) { return history <= 0 ? Flux.empty() : Flux.fromIterable(metadata.getProperties()) .flatMap(propertyMetadata -> QueryParamEntity.newQuery() .doPaging(0, history) .where("deviceId", deviceId) .and("property", propertyMetadata.getId()) .execute(timeSeriesService::query) .map(data -> SimpleMeasurementValue.of(createValue(propertyMetadata.getId(), data.get("value").orElse(null)), data.getTimestamp())) .sort(MeasurementValue.sort())); } Map<String, Object> createValue(String property, Object value) { return metadata.getProperty(property) .map(meta -> { Map<String, Object> values = new HashMap<>(); DataType type = meta.getValueType(); Object val = type instanceof Converter ? ((Converter<?>) type).convert(value) : value; values.put("formatValue", type.format(val)); values.put("value", val); values.put("property", property); return values; }) .orElseGet(() -> { Map<String, Object> values = new HashMap<>(); values.put("formatValue", value); values.put("value", value); values.put("property", property); return values; }); } Flux<MeasurementValue> fromRealTime(String deviceId) { return messageGateway .subscribe(Subscription.asList( "/device/" + productId + "/" + deviceId + "/message/property/report", "/device/" + productId + "/" + deviceId + "/message/property/*/reply") , "realtime-device-properties-measurement:" + Math.abs(num.incrementAndGet()) , true) .flatMap(val -> Mono.justOrEmpty(DeviceMessageUtils.convert(val))) .flatMap(msg -> { if (msg instanceof ReportPropertyMessage) { return Mono.justOrEmpty(((ReportPropertyMessage) msg).getProperties()); } if (msg instanceof ReadPropertyMessageReply) { return Mono.justOrEmpty(((ReadPropertyMessageReply) msg).getProperties()); } if (msg instanceof WritePropertyMessageReply) { return Mono.justOrEmpty(((WritePropertyMessageReply) msg).getProperties()); } return Mono.empty(); }) .flatMap(map -> Flux.fromIterable(map.entrySet())) .map(kv -> SimpleMeasurementValue.of(createValue(kv.getKey(), kv.getValue()), System.currentTimeMillis())); } static ConfigMetadata configMetadata = new DefaultConfigMetadata() .add("deviceId", "设备", "指定设备", new StringType().expand("selector", "device-selector")); /** * 历史设备事件 */ private class HistoryDevicePropertyDimension implements MeasurementDimension { @Override public DimensionDefinition getDefinition() { return CommonDimensionDefinition.history; } @Override public DataType getValueType() { return new ObjectType() .addProperty("property","属性", StringType.GLOBAL) .addProperty("value","值", StringType.GLOBAL) .addProperty("formatValue","格式化值", StringType.GLOBAL); } @Override public ConfigMetadata getParams() { return configMetadata; } @Override public boolean isRealTime() { return false; } @Override public Flux<MeasurementValue> getValue(MeasurementParameter parameter) { return Mono.justOrEmpty(parameter.getString("deviceId")) .flatMapMany(deviceId -> { int history = parameter.getInt("history").orElse(1); //合并历史数据和实时数据 return fromHistory(deviceId, history); }); } } /** * 实时设备事件 */ private class RealTimeDevicePropertyDimension implements MeasurementDimension { @Override public DimensionDefinition getDefinition() { return CommonDimensionDefinition.realTime; } @Override public DataType getValueType() { return new ObjectType() .addProperty("property","属性", StringType.GLOBAL) .addProperty("value","值", StringType.GLOBAL) .addProperty("formatValue","格式化值", StringType.GLOBAL); } @Override public ConfigMetadata getParams() { return configMetadata; } @Override public boolean isRealTime() { return true; } @Override public Flux<MeasurementValue> getValue(MeasurementParameter parameter) { return Mono.justOrEmpty(parameter.getString("deviceId")) .flatMapMany(deviceId -> { int history = parameter.getInt("history").orElse(0); //合并历史数据和实时数据 return Flux.concat( //查询历史数据 fromHistory(deviceId, history) , //从消息网关订阅实时事件消息 fromRealTime(deviceId) ); }); } } }