This commit is contained in:
zhouhuang 2025-01-10 09:12:14 +08:00
commit 27dca2295d
24 changed files with 504 additions and 365 deletions

View File

@ -32,7 +32,6 @@
<disruptor.version>3.4.4</disruptor.version> <disruptor.version>3.4.4</disruptor.version>
<aviator.version>5.4.3</aviator.version> <aviator.version>5.4.3</aviator.version>
<minio.version>8.4.3</minio.version> <minio.version>8.4.3</minio.version>
<jfreechart.version>1.5.3</jfreechart.version>
</properties> </properties>
<dependencies> <dependencies>
@ -217,11 +216,6 @@
<artifactId>minio</artifactId> <artifactId>minio</artifactId>
<version>${minio.version}</version> <version>${minio.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>${jfreechart.version}</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -34,6 +34,8 @@ public class DeviceInfoCache {
*/ */
private Long iotModelId; private Long iotModelId;
private String iotModelCode;
/** /**
* 制造商 * 制造商
*/ */

View File

@ -1,5 +1,6 @@
package com.das.modules.cache.domain; package com.das.modules.cache.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data; import lombok.Data;
@Data @Data
@ -10,6 +11,8 @@ public class IotFieldInfoCache {
private Integer porder; private Integer porder;
private Integer highspeed; private Integer highspeed;
private String datatype; private String datatype;
private Integer level;
private String stateDesc;
public boolean isHighSpeed() { public boolean isHighSpeed() {
return highspeed.equals(1) && attributeType.equals(138); return highspeed.equals(1) && attributeType.equals(138);

View File

@ -1,7 +1,16 @@
package com.das.modules.cache.service; package com.das.modules.cache.service;
import com.das.modules.cache.domain.IotFieldInfoCache;
import com.das.modules.equipment.entity.SysIotModel;
import com.das.modules.equipment.entity.SysIotModelField;
public interface IotModelCache { public interface IotModelCache {
public boolean isHighSpeed(Long modelId, String attr); public boolean isHighSpeed(Long modelId, String attr);
public boolean isLowSpeed(Long modelId, String attr); public boolean isLowSpeed(Long modelId, String attr);
public boolean isCalculate(Long modelId, String attr); public boolean isCalculate(Long modelId, String attr);
void refreshIotFieldCache(SysIotModelField sysIotModelField);
void removeIotFieldCache(Long modelId, String attributeCode);
void refreshIotModelInfoIdMap(SysIotModel sysIotModel);
void removeIotModelInfoIdMap(Long modelId);
IotFieldInfoCache getIotFiledCache(Long modelId,String attr);
} }

View File

@ -1,11 +1,14 @@
package com.das.modules.cache.service.impl; package com.das.modules.cache.service.impl;
import cn.hutool.core.util.StrUtil;
import com.das.modules.cache.domain.DeviceInfoCache; import com.das.modules.cache.domain.DeviceInfoCache;
import com.das.modules.cache.service.EquipmentCache; import com.das.modules.cache.service.EquipmentCache;
import com.das.modules.equipment.entity.SysEquipment; import com.das.modules.equipment.entity.SysEquipment;
import com.das.modules.equipment.mapper.SysEquipmentMapper; import com.das.modules.equipment.mapper.SysEquipmentMapper;
import com.das.modules.equipment.mapper.SysIotModelMapper;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy; import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -14,11 +17,13 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Service @Service
public class EquipmentCacheImpl implements EquipmentCache { public class EquipmentCacheImpl implements EquipmentCache {
@Autowired @Autowired
SysEquipmentMapper sysEquipmentMapper; SysEquipmentMapper sysEquipmentMapper;
@Autowired
SysIotModelMapper sysIotModelMapper;
/** /**
* 设备CODE索引用于通过设备CODE访问设备缓存信息 * 设备CODE索引用于通过设备CODE访问设备缓存信息
@ -43,6 +48,12 @@ public class EquipmentCacheImpl implements EquipmentCache {
deviceInfoCache.setMadeinFactory(equipment.getMadeinFactory()); deviceInfoCache.setMadeinFactory(equipment.getMadeinFactory());
deviceInfoCache.setParentDeviceId(equipment.getParentEquipmentId()); deviceInfoCache.setParentDeviceId(equipment.getParentEquipmentId());
deviceInfoCache.setIotModelId(equipment.getIotModelId()); deviceInfoCache.setIotModelId(equipment.getIotModelId());
String iotModelCode = sysIotModelMapper.getIodModelCode(equipment.getIotModelId());
log.debug("iotModelId: {}", equipment.getIotModelId());
if (StrUtil.isNotBlank(iotModelCode)){
log.debug("iotModelCode: {}", iotModelCode);
deviceInfoCache.setIotModelCode(iotModelCode);
}
//创建Code索引 //创建Code索引
deviceCodeIndex.put(deviceInfoCache.getDeviceCode(),deviceInfoCache); deviceCodeIndex.put(deviceInfoCache.getDeviceCode(),deviceInfoCache);
//创建Id索引 //创建Id索引

View File

@ -3,6 +3,8 @@ package com.das.modules.cache.service.impl;
import com.das.modules.cache.domain.IotFieldInfoCache; import com.das.modules.cache.domain.IotFieldInfoCache;
import com.das.modules.cache.domain.IotModelInfoCache; import com.das.modules.cache.domain.IotModelInfoCache;
import com.das.modules.cache.service.IotModelCache; import com.das.modules.cache.service.IotModelCache;
import com.das.modules.equipment.entity.SysIotModel;
import com.das.modules.equipment.entity.SysIotModelField;
import com.das.modules.equipment.mapper.SysIotModelMapper; import com.das.modules.equipment.mapper.SysIotModelMapper;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy; import jakarta.annotation.PreDestroy;
@ -38,6 +40,8 @@ public class IotModelCacheImpl implements IotModelCache {
fieldInfoCache.setAttributeType(item.getAttributeType()); fieldInfoCache.setAttributeType(item.getAttributeType());
fieldInfoCache.setHighspeed(item.getHighSpeed()); fieldInfoCache.setHighspeed(item.getHighSpeed());
fieldInfoCache.setDatatype(item.getDataType()); fieldInfoCache.setDatatype(item.getDataType());
fieldInfoCache.setLevel(item.getLevel());
fieldInfoCache.setStateDesc(item.getStateDesc());
iotFieldsMap.put(String.format("%d_%s", k, item.getAttributeCode()), fieldInfoCache); iotFieldsMap.put(String.format("%d_%s", k, item.getAttributeCode()), fieldInfoCache);
}); });
}); });
@ -75,4 +79,42 @@ public class IotModelCacheImpl implements IotModelCache {
} }
return fieldInfoCache.isCalculate(); return fieldInfoCache.isCalculate();
} }
@Override
public void refreshIotFieldCache(SysIotModelField sysIotModelField) {
IotFieldInfoCache fieldInfoCache = new IotFieldInfoCache();
fieldInfoCache.setAttributeCode(sysIotModelField.getAttributeCode());
fieldInfoCache.setPorder(sysIotModelField.getPorder());
fieldInfoCache.setAttributeName(sysIotModelField.getAttributeName());
fieldInfoCache.setAttributeType(sysIotModelField.getAttributeType());
fieldInfoCache.setHighspeed(sysIotModelField.getHighSpeed());
fieldInfoCache.setDatatype(sysIotModelField.getDataType());
fieldInfoCache.setLevel(sysIotModelField.getLevel());
fieldInfoCache.setStateDesc(sysIotModelField.getStateDesc());
iotFieldsMap.put(String.format("%d_%s", sysIotModelField.getIotModelId(), sysIotModelField.getAttributeCode()), fieldInfoCache);
}
@Override
public void removeIotFieldCache(Long modelId, String attributeCode){
iotFieldsMap.remove(String.format("%d_%s", modelId, attributeCode));
}
@Override
public void refreshIotModelInfoIdMap(SysIotModel sysIotModel){
IotModelInfoCache info = new IotModelInfoCache();
info.setIotModelId(sysIotModel.getId());
info.setIodModelCode(sysIotModel.getIotModelCode());
iotModelInfoIdMap.put(sysIotModel.getId(), info);
}
@Override
public void removeIotModelInfoIdMap(Long modelId) {
iotModelInfoIdMap.remove(modelId);
}
@Override
public IotFieldInfoCache getIotFiledCache(Long modelId, String attr) {
return iotFieldsMap.get(String.format("%d_%s", modelId, attr));
}
} }

View File

@ -277,6 +277,9 @@ public class TDEngineService {
@Async @Async
public void updateYCHighValues(List<RTData> values, String iotModelCode) { public void updateYCHighValues(List<RTData> values, String iotModelCode) {
if (values.isEmpty()) {
return;
}
StringBuilder sb = new StringBuilder(1024 * 1024); StringBuilder sb = new StringBuilder(1024 * 1024);
try (Connection conn = hikariDataSource.getConnection(); try (Connection conn = hikariDataSource.getConnection();
Statement pstmt = conn.createStatement()) { Statement pstmt = conn.createStatement()) {
@ -315,6 +318,9 @@ public class TDEngineService {
@Async @Async
public void updateYCLowValues(List<RTData> values, String iotModelCode) { public void updateYCLowValues(List<RTData> values, String iotModelCode) {
if (values.isEmpty()) {
return;
}
StringBuilder sb = new StringBuilder(1024 * 1024); StringBuilder sb = new StringBuilder(1024 * 1024);
try (Connection conn = hikariDataSource.getConnection(); try (Connection conn = hikariDataSource.getConnection();
Statement pstmt = conn.createStatement()) { Statement pstmt = conn.createStatement()) {
@ -353,6 +359,9 @@ public class TDEngineService {
@Async @Async
public void updateCalFieldValues(List<CalculateRTData> values) { public void updateCalFieldValues(List<CalculateRTData> values) {
if (values.isEmpty()) {
return;
}
StringBuilder sb = new StringBuilder(1024 * 1024); StringBuilder sb = new StringBuilder(1024 * 1024);
try (Connection conn = hikariDataSource.getConnection(); try (Connection conn = hikariDataSource.getConnection();
Statement pstmt = conn.createStatement()) { Statement pstmt = conn.createStatement()) {
@ -387,6 +396,9 @@ public class TDEngineService {
@Async @Async
public void updateDeviceEventValues(List<DeviceEventInfo> values) { public void updateDeviceEventValues(List<DeviceEventInfo> values) {
if (values.isEmpty()) {
return;
}
StringBuilder sb = new StringBuilder(1024 * 1024); StringBuilder sb = new StringBuilder(1024 * 1024);
try (Connection conn = hikariDataSource.getConnection(); try (Connection conn = hikariDataSource.getConnection();
Statement pstmt = conn.createStatement()) { Statement pstmt = conn.createStatement()) {

View File

@ -31,6 +31,8 @@ public interface SysIotModelMapper extends BaseMapper<SysIotModel> {
String getIotModel(Long id); String getIotModel(Long id);
String getIodModelCode(Long id);
List<SysIotModelField> getAllIotModelField(Long id); List<SysIotModelField> getAllIotModelField(Long id);
SysIotModelVo selectIotModelByCode(String code); SysIotModelVo selectIotModelByCode(String code);

View File

@ -10,6 +10,7 @@ import com.das.common.config.SessionUtil;
import com.das.common.exceptions.ServiceException; import com.das.common.exceptions.ServiceException;
import com.das.common.utils.*; import com.das.common.utils.*;
import com.das.modules.auth.domain.vo.SysUserVo; import com.das.modules.auth.domain.vo.SysUserVo;
import com.das.modules.cache.service.CacheService;
import com.das.modules.data.service.impl.DataServiceImpl; import com.das.modules.data.service.impl.DataServiceImpl;
import com.das.modules.equipment.domain.dto.SysIotModelDto; import com.das.modules.equipment.domain.dto.SysIotModelDto;
import com.das.modules.equipment.domain.dto.SysIotModelFieldDto; import com.das.modules.equipment.domain.dto.SysIotModelFieldDto;
@ -65,6 +66,9 @@ public class SysIotModelServiceImpl implements SysIotModelService {
@Autowired @Autowired
SysRecordLogService sysRecordLogService; SysRecordLogService sysRecordLogService;
@Autowired
CacheService cacheService;
public SysIotModelVo creatSysIotModel(SysIotModelDto sysIotModelDto) { public SysIotModelVo creatSysIotModel(SysIotModelDto sysIotModelDto) {
SysIotModel sysIotModel = new SysIotModel(); SysIotModel sysIotModel = new SysIotModel();
BeanCopyUtils.copy(sysIotModelDto, sysIotModel); BeanCopyUtils.copy(sysIotModelDto, sysIotModel);
@ -81,7 +85,9 @@ public class SysIotModelServiceImpl implements SysIotModelService {
sysIotModel.setRevision(1); sysIotModel.setRevision(1);
sysIotModelMapper.insert(sysIotModel); sysIotModelMapper.insert(sysIotModel);
//刷新缓存
addModelCache(sysIotModel); addModelCache(sysIotModel);
cacheService.getIotModelCache().refreshIotModelInfoIdMap(sysIotModel);
SysIotModelVo sysIotModelVo = new SysIotModelVo(); SysIotModelVo sysIotModelVo = new SysIotModelVo();
BeanCopyUtils.copy(sysIotModel, sysIotModelVo); BeanCopyUtils.copy(sysIotModel, sysIotModelVo);
sysIotModelVo.setIotModelCode(sysIotModelDto.getIotModelCode()); sysIotModelVo.setIotModelCode(sysIotModelDto.getIotModelCode());
@ -119,6 +125,8 @@ public class SysIotModelServiceImpl implements SysIotModelService {
throw new RuntimeException("该物模型下面有类型,不能删除"); throw new RuntimeException("该物模型下面有类型,不能删除");
} }
sysIotModelMapper.deleteById(sysIotModelDto.getId()); sysIotModelMapper.deleteById(sysIotModelDto.getId());
//刷新缓存
cacheService.getIotModelCache().removeIotModelInfoIdMap(sysIotModelDto.getId());
deleteModelCache(sysIotModelDto.getId()); deleteModelCache(sysIotModelDto.getId());
} }
@ -184,6 +192,7 @@ public class SysIotModelServiceImpl implements SysIotModelService {
sysIotModelFieldMapper.insert(sysIotModelField); sysIotModelFieldMapper.insert(sysIotModelField);
//新增物模型属性缓存 //新增物模型属性缓存
cacheService.getIotModelCache().refreshIotFieldCache(sysIotModelField);
addModelFieldCache(sysIotModelField); addModelFieldCache(sysIotModelField);
SysIotModelFieldVo sysIotModelFieldVo = new SysIotModelFieldVo(); SysIotModelFieldVo sysIotModelFieldVo = new SysIotModelFieldVo();
BeanCopyUtils.copy(sysIotModelField, sysIotModelFieldVo); BeanCopyUtils.copy(sysIotModelField, sysIotModelFieldVo);
@ -216,6 +225,7 @@ public class SysIotModelServiceImpl implements SysIotModelService {
if (!oldSysIotField.getAttributeCode().equals(sysIotModelField.getAttributeCode()) || !oldSysIotField.getDataType().equals(sysIotModelField.getDataType()) || Objects.equals(oldSysIotField.getHighSpeed(), sysIotModelField.getHighSpeed())) { if (!oldSysIotField.getAttributeCode().equals(sysIotModelField.getAttributeCode()) || !oldSysIotField.getDataType().equals(sysIotModelField.getDataType()) || Objects.equals(oldSysIotField.getHighSpeed(), sysIotModelField.getHighSpeed())) {
//更新td表结构 //更新td表结构
updateTDStableOrColumn(sysIotModelField, oldSysIotField); updateTDStableOrColumn(sysIotModelField, oldSysIotField);
cacheService.getIotModelCache().refreshIotFieldCache(sysIotModelField);
updateModelFieldCache(sysIotModelField, oldSysIotField); updateModelFieldCache(sysIotModelField, oldSysIotField);
} }
sysIotModelFieldMapper.updateById(sysIotModelField); sysIotModelFieldMapper.updateById(sysIotModelField);
@ -232,6 +242,7 @@ public class SysIotModelServiceImpl implements SysIotModelService {
sysIotModelFieldMapper.deleteById(sysIotModelFieldDto.getId()); sysIotModelFieldMapper.deleteById(sysIotModelFieldDto.getId());
//删除物模型属性缓存 //删除物模型属性缓存
cacheService.getIotModelCache().removeIotFieldCache(sysIotModelField.getIotModelId(), sysIotModelField.getAttributeCode());
deleteModelFieldCache(sysIotModelField); deleteModelFieldCache(sysIotModelField);
} }
@ -488,6 +499,10 @@ public class SysIotModelServiceImpl implements SysIotModelService {
} }
//新增pg数据库 //新增pg数据库
sysIotModelFieldMapper.insertBatch(sysIotModelFieldList); sysIotModelFieldMapper.insertBatch(sysIotModelFieldList);
//刷新缓存
for (SysIotModelField item : sysIotModelFieldList){
cacheService.getIotModelCache().refreshIotFieldCache(item);
}
} }
if (CollectionUtils.isNotEmpty(updateSysIotModelFieldList)) { if (CollectionUtils.isNotEmpty(updateSysIotModelFieldList)) {
for (SysIotModelField item : updateSysIotModelFieldList) { for (SysIotModelField item : updateSysIotModelFieldList) {
@ -496,6 +511,7 @@ public class SysIotModelServiceImpl implements SysIotModelService {
if (!oldSysIotField.getAttributeCode().equals(item.getAttributeCode()) && oldSysIotField.getDataType().equals(item.getDataType()) && Objects.equals(oldSysIotField.getHighSpeed(), item.getHighSpeed())) { if (!oldSysIotField.getAttributeCode().equals(item.getAttributeCode()) && oldSysIotField.getDataType().equals(item.getDataType()) && Objects.equals(oldSysIotField.getHighSpeed(), item.getHighSpeed())) {
//更新td表结构 //更新td表结构
updateTDStableOrColumn(item, oldSysIotField); updateTDStableOrColumn(item, oldSysIotField);
cacheService.getIotModelCache().refreshIotFieldCache(item);
updateModelFieldCache(item, oldSysIotField); updateModelFieldCache(item, oldSysIotField);
} }
} }
@ -509,6 +525,7 @@ public class SysIotModelServiceImpl implements SysIotModelService {
for (SysIotModelField item : delSysIotModelFieldList) { for (SysIotModelField item : delSysIotModelFieldList) {
deleteTDStableOrColumn(item); deleteTDStableOrColumn(item);
sysIotModelFieldMapper.deleteById(item); sysIotModelFieldMapper.deleteById(item);
cacheService.getIotModelCache().removeIotFieldCache(item.getIotModelId(), item.getAttributeCode());
deleteModelFieldCache(item); deleteModelFieldCache(item);
} }
} }

View File

@ -1,26 +1,110 @@
package com.das.modules.node.command; package com.das.modules.node.command;
import com.das.common.exceptions.ServiceException;
import com.das.modules.cache.domain.DeviceInfoCache;
import com.das.modules.cache.service.CacheService;
import com.das.modules.cache.service.IotModelCache;
import com.das.modules.data.service.TDEngineService;
import com.das.modules.node.constant.NodeConstant; import com.das.modules.node.constant.NodeConstant;
import com.das.modules.node.domain.bo.RTData;
import com.das.modules.node.domain.bo.TerminalMessage; import com.das.modules.node.domain.bo.TerminalMessage;
import com.das.modules.node.service.NodeMessageService; import com.das.modules.node.service.NodeMessageService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*;
@Service(value = NodeConstant.ANALOG_DATA) @Service(value = NodeConstant.ANALOG_DATA)
@Slf4j @Slf4j
public class AnalogDataCommand implements BaseCommand { public class AnalogDataCommand implements BaseCommand {
@Autowired @Autowired
NodeMessageService nodeMessageService; CacheService cacheService;
@Autowired
TDEngineService tdEngineService;
@Autowired
RedisTemplate<String, Object> redisTemplate;
/**
* 执行命令方法
* 当接收到终端消息时此方法被调用以处理数据
* 主要负责处理模拟量数据的接收和处理
*
* @param data 包含模拟量数据的TerminalMessage对象
*/
@Override @Override
public void doCommand(TerminalMessage data) { public void doCommand(TerminalMessage data) {
if (log.isDebugEnabled()){
log.debug("收到实时数据[模拟量]");
log.debug("数据内容: {}", data.toJsonString());
}
try { try {
//analogData值只存入redis processAnalogData(data);
nodeMessageService.handleData(data);
} catch (Exception e) { } catch (Exception e) {
log.error("解析数据异常", e); log.error("处理实时数据时产生异常", e);
} }
} }
/**
* 处理实时数据
* @param data
*/
private void processAnalogData(TerminalMessage data){
JsonNode dataNode = Optional.of(data.getData()).orElseThrow(() -> new ServiceException("data字段缺失"));
String deviceId = Optional.of(dataNode.get("deviceId")).orElseThrow(() -> new ServiceException("deviceId字段缺失")).asText();
Long dataTime = Optional.of(dataNode.get("dataTime")).orElseThrow( () -> new ServiceException("dataTime字段缺失")).asLong();
JsonNode values = dataNode.get("values");
JsonNode archiveValues = dataNode.get("archiveValues");
//排除空数据
if (values == null && archiveValues == null){
return;
}
//如果values有值则存入redis
Map<String,Object> redisValues = new HashMap<>();
if (values != null && values.isObject()){
for (Iterator<String> it = values.fieldNames(); it.hasNext(); ) {
String valueName = it.next();
String key = String.format("RT:%s:%s", deviceId, valueName.toLowerCase());
redisValues.put(key, values.get(valueName));
}
}
DeviceInfoCache dev = cacheService.getEquipmentCache().getDeviceInfoCacheById(Long.parseLong(deviceId));
IotModelCache iotCache = cacheService.getIotModelCache();
//如果archiveValues有值则存入td同时也更新redis
if (archiveValues != null && archiveValues.isObject()){
RTData hiSpeedData = new RTData();
hiSpeedData.setDeviceId(Long.parseLong(deviceId));
hiSpeedData.setDataTime(dataTime);
Map<String,Object> hiSpeedValues = new HashMap<>();
RTData lowSpeedData = new RTData();
lowSpeedData.setDeviceId(Long.parseLong(deviceId));
lowSpeedData.setDataTime(dataTime);
Map<String,Object> lowSpeedValues = new HashMap<>();
for (Iterator<String> it = archiveValues.fieldNames(); it.hasNext(); ) {
//加入redis更新列表
String valueName = it.next();
String key = String.format("RT:%s:%s", deviceId, valueName.toLowerCase());
redisValues.put(key, archiveValues.get(valueName));
//加入td更新列表
if (iotCache.isHighSpeed(dev.getIotModelId(), valueName)) {
hiSpeedValues.put(valueName, archiveValues.get(valueName));
} else if (iotCache.isLowSpeed(dev.getIotModelId(), valueName)) {
lowSpeedValues.put(valueName, archiveValues.get(valueName));
}
}
hiSpeedData.setValues(hiSpeedValues);
lowSpeedData.setValues(lowSpeedValues);
tdEngineService.updateYCHighValues(List.of(hiSpeedData), dev.getIotModelCode());
tdEngineService.updateYCLowValues(List.of(lowSpeedData), dev.getIotModelCode());
}
redisTemplate.opsForValue().multiSet(redisValues);
}
} }

View File

@ -1,25 +1,127 @@
package com.das.modules.node.command; package com.das.modules.node.command;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.das.common.exceptions.ServiceException;
import com.das.common.utils.AdminRedisTemplate;
import com.das.common.utils.StringUtils;
import com.das.modules.cache.domain.DeviceInfoCache;
import com.das.modules.cache.domain.IotFieldInfoCache;
import com.das.modules.cache.service.CacheService;
import com.das.modules.data.domain.DeviceEventInfo;
import com.das.modules.data.service.TDEngineService;
import com.das.modules.node.constant.NodeConstant; import com.das.modules.node.constant.NodeConstant;
import com.das.modules.node.domain.bo.TerminalMessage; import com.das.modules.node.domain.bo.TerminalMessage;
import com.das.modules.node.service.NodeMessageService; import com.das.modules.node.domain.vo.DeviceEventVo;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Service(value = NodeConstant.DEVICE_EVENT) @Service(value = NodeConstant.DEVICE_EVENT)
@Slf4j @Slf4j
public class DeviceEventCommand implements BaseCommand{ public class DeviceEventCommand implements BaseCommand{
@Autowired @Autowired
private NodeMessageService nodeMessageService; AdminRedisTemplate adminRedisTemplate;
@Autowired
TDEngineService tdEngineService;
@Autowired
CacheService cacheService;
@Override @Override
public void doCommand(TerminalMessage data) { public void doCommand(TerminalMessage data) {
log.debug("收到实时[事件告警]");
try { try {
nodeMessageService.handleDeviceEvent(data); handleDeviceEvent(data);
} catch (Exception e) { } catch (Exception e) {
log.error("解析数据异常", e); log.error("解析数据异常", e);
} }
} }
private void handleDeviceEvent(TerminalMessage data) {
JsonNode dataEvent = data.getData();
ObjectMapper objectMapper = new ObjectMapper();
List<DeviceEventInfo> valueList = new ArrayList<>();
// 使用 TypeReference 来指定转换目标类型
List<DeviceEventVo> list = objectMapper.convertValue(dataEvent, new TypeReference<List<DeviceEventVo>>() {
});
log.debug("消息data转化deviceVo,{}", list);
for (DeviceEventVo item : list) {
DeviceInfoCache deviceInfoCache = cacheService.getEquipmentCache().getDeviceInfoCacheById(Long.valueOf(item.getDeviceId()));
Integer firstTriggeredCode = adminRedisTemplate.get(String.format("RT:%s:%s", item.getDeviceId(), "FirstTriggeredCode".toLowerCase()));
DeviceEventInfo deviceEventInfo = new DeviceEventInfo();
deviceEventInfo.setEventTime(item.getEventTime());
deviceEventInfo.setEventId(IdWorker.getId());
deviceEventInfo.setAttributeCode(item.getAttrCode());
deviceEventInfo.setDeviceId(item.getDeviceId());
deviceEventInfo.setDeviceName(deviceInfoCache.getDeviceName());
deviceEventInfo.setDeviceCode(deviceInfoCache.getDeviceCode());
deviceEventInfo.setFirstTriggeredCode(firstTriggeredCode);
String eventType = getEventType(item.getEventType());
Long iotModelId = deviceInfoCache.getIotModelId();
if (iotModelId == null) {
log.debug("未查询到物模型code设备id{}", item.getDeviceId());
}
IotFieldInfoCache iotFiledCache = cacheService.getIotModelCache().getIotFiledCache(iotModelId, item.getAttrCode());
if (iotFiledCache == null) {
log.debug("未查询到物模型属性设备id{},属性AttrCode{}", item.getDeviceId(),item.getAttrCode());
throw new ServiceException("未查询到物模型属性设备id"+item.getDeviceId()+",属性AttrCode"+item.getAttrCode());
}
String fieldName = iotFiledCache.getAttributeName();
deviceEventInfo.setEventType(item.getEventType());
deviceEventInfo.setConfirmed(0);
if (!StringUtils.isEmpty(eventType) && eventType.equals("遥信变位")) {
String stateDesc = iotFiledCache.getStateDesc();
if (item.getAttrValue().equals(0)) {
deviceEventInfo.setEventText(item.getAttrCode() + fieldName + " 复归");
if (StringUtils.isNotEmpty(stateDesc)) {
List<String> descList = Arrays.stream(stateDesc.split("\\|")).toList();
deviceEventInfo.setEventText(item.getAttrCode() + fieldName + descList.get(0));
}
deviceEventInfo.setEventLevel(0);
} else {
deviceEventInfo.setEventText(item.getAttrCode() + fieldName + " 动作");
if (StringUtils.isNotEmpty(stateDesc)) {
List<String> descList = Arrays.stream(stateDesc.split("\\|")).toList();
deviceEventInfo.setEventText(item.getAttrCode() + fieldName + descList.get(1));
}
Integer level = iotFiledCache.getLevel();
log.debug("level:{}", level);
log.debug("fieldname{}", fieldName);
deviceEventInfo.setEventLevel(level == null ? 0 : level);
}
} else {
deviceEventInfo.setEventText(item.getAttrCode() + fieldName + eventType + ",属性值为:" + item.getAttrValue() + ",越限值为:" + item.getLimitValue());
deviceEventInfo.setEventLevel(1);
}
valueList.add(deviceEventInfo);
}
try {
tdEngineService.updateDeviceEventValues(valueList);
} catch (Exception e) {
log.error("事件信息存入Td失败,失败原因", e);
}
}
private String getEventType(int eventType) {
return switch (eventType) {
case 0 -> "遥信变位";
case 1 -> "越上限";
case 2 -> "越下限";
case 3 -> "越上上限";
case 4 -> "越下下限";
default -> "";
};
}
} }

View File

@ -34,7 +34,7 @@ public class HeartbeatCommand implements BaseCommand{
*/ */
@Override @Override
public void doCommand(TerminalMessage data) { public void doCommand(TerminalMessage data) {
log.info("收到[heartbeat]报文"); log.debug("收到[heartbeat]报文");
// 解析心跳报文中的数据信息 // 解析心跳报文中的数据信息
JsonNode dataInfo = data.getData(); JsonNode dataInfo = data.getData();

View File

@ -1,26 +1,63 @@
package com.das.modules.node.command; package com.das.modules.node.command;
import com.das.common.exceptions.ServiceException;
import com.das.modules.cache.domain.DeviceInfoCache;
import com.das.modules.cache.service.CacheService;
import com.das.modules.data.service.TDEngineService;
import com.das.modules.node.constant.NodeConstant; import com.das.modules.node.constant.NodeConstant;
import com.das.modules.node.domain.bo.RTData;
import com.das.modules.node.domain.bo.TerminalMessage; import com.das.modules.node.domain.bo.TerminalMessage;
import com.das.modules.node.service.NodeMessageService; import com.das.modules.node.service.NodeMessageService;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*;
@Service(value = NodeConstant.HIS_HIGH_SPEED_DATA) @Service(value = NodeConstant.HIS_HIGH_SPEED_DATA)
@Slf4j @Slf4j
public class HisHighSpeedCommand implements BaseCommand { public class HisHighSpeedCommand implements BaseCommand {
@Autowired @Autowired
NodeMessageService nodeMessageService; CacheService cacheService;
@Autowired
TDEngineService tdEngineService;
@Override @Override
public void doCommand(TerminalMessage data) { public void doCommand(TerminalMessage data) {
log.debug("收到[历史高频数据]");
try { try {
//analogData值只存入redis processHisHighSpeedData(data);
nodeMessageService.handleHighSpeed(data);
} catch (Exception e) { } catch (Exception e) {
log.error("解析数据异常", e); log.error("解析数据异常", e);
} }
} }
public void processHisHighSpeedData(TerminalMessage data) {
JsonNode jsonNode = Optional.of(data.getData()).orElseThrow(() -> new ServiceException("data字段缺失"));
String deviceId = Optional.of(jsonNode.get("deviceId")).orElseThrow( () -> new ServiceException("deviceId字段缺失")).asText();
Long dataTime = Optional.of(jsonNode.get("dataTime")).orElseThrow( ()-> new ServiceException("dataTime字段缺失")).asLong();
JsonNode values = jsonNode.get("values");
Map<String, Object> keyValueMap = new HashMap<>();
// 根据设备ID获取对应的物模型属性
DeviceInfoCache dev = cacheService.getEquipmentCache().getDeviceInfoCacheById(Long.valueOf(deviceId));
//High数据
Iterator<String> keysHigh = values.fieldNames();
while (keysHigh.hasNext()) {
String fieldName = keysHigh.next();
if (cacheService.getIotModelCache().isHighSpeed(dev.getIotModelId(), fieldName)) {
keyValueMap.put(fieldName, values.get(fieldName));
}
}
RTData rtHighData = RTData.builder()
.dataTime(dataTime)
.deviceId(Long.valueOf(deviceId))
.values(keyValueMap)
.build();
tdEngineService.updateYCHighValues(List.of(rtHighData), dev.getIotModelCode());
}
} }

View File

@ -1,26 +1,72 @@
package com.das.modules.node.command; package com.das.modules.node.command;
import com.das.modules.cache.domain.DeviceInfoCache;
import com.das.modules.cache.service.CacheService;
import com.das.modules.data.service.TDEngineService;
import com.das.modules.node.constant.NodeConstant; import com.das.modules.node.constant.NodeConstant;
import com.das.modules.node.domain.bo.RTData;
import com.das.modules.node.domain.bo.TerminalMessage; import com.das.modules.node.domain.bo.TerminalMessage;
import com.das.modules.node.service.NodeMessageService; import com.das.modules.node.service.NodeMessageService;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*;
@Service(value = NodeConstant.HIS_LOW_SPEED_DATA) @Service(value = NodeConstant.HIS_LOW_SPEED_DATA)
@Slf4j @Slf4j
public class HisLowSpeedCommand implements BaseCommand { public class HisLowSpeedCommand implements BaseCommand {
@Autowired @Autowired
NodeMessageService nodeMessageService; CacheService cacheService;
@Autowired
TDEngineService tdEngineService;
@Override @Override
public void doCommand(TerminalMessage data) { public void doCommand(TerminalMessage data) {
log.debug("收到[历史低频数据]");
try { try {
//analogData值只存入redis //analogData值只存入redis
nodeMessageService.handleLowSpeed(data); processHisLowSpeedData(data);
} catch (Exception e) { } catch (Exception e) {
log.error("解析数据异常", e); log.error("解析数据异常", e);
} }
} }
private void processHisLowSpeedData(TerminalMessage data) {
JsonNode dataNode = data.getData();
if (dataNode == null ) {
log.error("收到无效历史低频数据报文");
return;
}
String deviceId = Optional.of(dataNode.get("deviceId")).orElseThrow(() -> new RuntimeException("设备ID为空")).asText();
Long dataTime = Optional.of(dataNode.get("dataTime")).orElseThrow(() -> new RuntimeException("数据时间为空")).asLong();
JsonNode values = dataNode.get("values");
Map<String, Object> keyValueMap = new HashMap<>();
// 根据设备ID获取对应的物模型属性
DeviceInfoCache device = cacheService.getEquipmentCache().getDeviceInfoCacheById(Long.valueOf(deviceId));
String iotModelCode = device.getIotModelCode();
//Low数据
Iterator<String> keysLow = values.fieldNames();
while (keysLow.hasNext()) {
String fieldName = keysLow.next();
if (cacheService.getIotModelCache().isLowSpeed(device.getIotModelId(), fieldName)){
keyValueMap.put(fieldName, values.get(fieldName));
}
}
RTData rtLowData = RTData.builder()
.dataTime(dataTime)
.deviceId(Long.valueOf(deviceId))
.values(keyValueMap)
.build();
tdEngineService.updateYCLowValues(List.of(rtLowData), iotModelCode);
}
} }

View File

@ -1,26 +1,95 @@
package com.das.modules.node.command; package com.das.modules.node.command;
import com.das.common.exceptions.ServiceException;
import com.das.modules.cache.domain.DeviceInfoCache;
import com.das.modules.cache.service.CacheService;
import com.das.modules.cache.service.IotModelCache;
import com.das.modules.data.service.TDEngineService;
import com.das.modules.node.constant.NodeConstant; import com.das.modules.node.constant.NodeConstant;
import com.das.modules.node.domain.bo.RTData;
import com.das.modules.node.domain.bo.TerminalMessage; import com.das.modules.node.domain.bo.TerminalMessage;
import com.das.modules.node.service.NodeMessageService; import com.das.modules.node.service.NodeMessageService;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*;
@Service(value = NodeConstant.STATE_DATA) @Service(value = NodeConstant.STATE_DATA)
@Slf4j @Slf4j
public class StateDataCommand implements BaseCommand { public class StateDataCommand implements BaseCommand {
@Autowired @Autowired
NodeMessageService nodeMessageService; CacheService cacheService;
@Autowired
RedisTemplate<String,Object> redisTemplate;
@Autowired
TDEngineService tdEngineService;
@Override @Override
public void doCommand(TerminalMessage data) { public void doCommand(TerminalMessage data) {
log.debug("收到实时数据[状态量]");
try { try {
//只存入redis //只存入redis
nodeMessageService.handleData(data); processStateData(data);
} catch (Exception e) { } catch (Exception e) {
log.error("解析数据异常", e); log.error("解析数据异常", e);
} }
} }
private void processStateData(TerminalMessage data) {
JsonNode dataNode = Optional.of(data.getData()).orElseThrow(() -> new ServiceException("data字段缺失"));
String deviceId = Optional.of(dataNode.get("deviceId")).orElseThrow(() -> new ServiceException("deviceId字段缺失")).asText();
Long dataTime = Optional.of(dataNode.get("dataTime")).orElseThrow( () -> new ServiceException("dataTime字段缺失")).asLong();
JsonNode values = dataNode.get("values");
JsonNode archiveValues = dataNode.get("archiveValues");
//排除空数据
if (values == null && archiveValues == null){
return;
}
//如果values有值则存入redis
Map<String,Object> redisValues = new HashMap<>();
if (values != null && values.isObject()){
for (Iterator<String> it = values.fieldNames(); it.hasNext(); ) {
String valueName = it.next();
String key = String.format("RT:%s:%s", deviceId, valueName.toLowerCase());
redisValues.put(key, values.get(valueName));
}
}
DeviceInfoCache dev = cacheService.getEquipmentCache().getDeviceInfoCacheById(Long.parseLong(deviceId));
IotModelCache iotCache = cacheService.getIotModelCache();
//如果archiveValues有值则存入td同时也更新redis
if (archiveValues != null && archiveValues.isObject()){
RTData hiSpeedData = new RTData();
hiSpeedData.setDeviceId(Long.parseLong(deviceId));
hiSpeedData.setDataTime(dataTime);
Map<String,Object> hiSpeedValues = new HashMap<>();
RTData lowSpeedData = new RTData();
lowSpeedData.setDeviceId(Long.parseLong(deviceId));
lowSpeedData.setDataTime(dataTime);
Map<String,Object> lowSpeedValues = new HashMap<>();
for (Iterator<String> it = archiveValues.fieldNames(); it.hasNext(); ) {
//加入redis更新列表
String valueName = it.next();
String key = String.format("RT:%s:%s", deviceId, valueName.toLowerCase());
redisValues.put(key, archiveValues.get(valueName));
//加入td更新列表
if (iotCache.isHighSpeed(dev.getIotModelId(), valueName)) {
hiSpeedValues.put(valueName, archiveValues.get(valueName));
} else if (iotCache.isLowSpeed(dev.getIotModelId(), valueName)) {
lowSpeedValues.put(valueName, archiveValues.get(valueName));
}
}
hiSpeedData.setValues(hiSpeedValues);
lowSpeedData.setValues(lowSpeedValues);
tdEngineService.updateYCHighValues(List.of(hiSpeedData), dev.getIotModelCode());
tdEngineService.updateYCLowValues(List.of(lowSpeedData),dev.getIotModelCode());
}
redisTemplate.opsForValue().multiSet(redisValues);
}
} }

View File

@ -19,6 +19,8 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
/** /**
* 物模型Controller * 物模型Controller
@ -84,7 +86,7 @@ public class SysNodeController {
/** 配置下发 */ /** 配置下发 */
@PostMapping("/configUpdate") @PostMapping("/configUpdate")
public R<?> configUpdate(@RequestBody SysNodeDto sysNodeDto) { public R<?> configUpdate(@RequestBody SysNodeDto sysNodeDto) throws ExecutionException, InterruptedException, TimeoutException {
nodeMessageService.sendTerminalConfig(sysNodeDto.getId()); nodeMessageService.sendTerminalConfig(sysNodeDto.getId());
return R.success(); return R.success();
} }

View File

@ -12,15 +12,7 @@ import java.util.concurrent.TimeoutException;
public interface NodeMessageService { public interface NodeMessageService {
JsonNode sendTerminalConfig(Long nodeId); JsonNode sendTerminalConfig(Long nodeId) throws ExecutionException, InterruptedException, TimeoutException;
void handleData(TerminalMessage data);
void handleHighSpeed(TerminalMessage data);
void handleLowSpeed(TerminalMessage data);
void handleDeviceEvent(TerminalMessage data);
/** /**
* 向指定采集节点发送指令(无返回值) * 向指定采集节点发送指令(无返回值)

View File

@ -75,22 +75,6 @@ public class NodeMessageServiceImpl extends TextWebSocketHandler implements Node
@Resource @Resource
SysImpTabMappingMapper sysImptabmappingMapper; SysImpTabMappingMapper sysImptabmappingMapper;
@Autowired
AdminRedisTemplate adminRedisTemplate;
@Autowired
SysIotModelMapper sysIotModelMapper;
@Autowired
TDEngineService tdEngineService;
@Autowired
private CacheService cacheService;
@Autowired
private DataServiceImpl dataService;
@PostConstruct @PostConstruct
public void init() { public void init() {
//初始化高性能队列 //初始化高性能队列
@ -135,7 +119,7 @@ public class NodeMessageServiceImpl extends TextWebSocketHandler implements Node
} }
@Override @Override
public JsonNode sendTerminalConfig(Long nodeId) { public JsonNode sendTerminalConfig(Long nodeId) throws ExecutionException, InterruptedException, TimeoutException {
ConfigUpdateVo configUpdateVo = new ConfigUpdateVo(); ConfigUpdateVo configUpdateVo = new ConfigUpdateVo();
List<LinkVo> links = new ArrayList<>(); List<LinkVo> links = new ArrayList<>();
try { try {
@ -232,199 +216,11 @@ public class NodeMessageServiceImpl extends TextWebSocketHandler implements Node
.time(time) .time(time)
.data(jsonNode) .data(jsonNode)
.build(); .build();
sendActionMessage(nodeId, configUpdate); sendTerminalMessageWithResult(nodeId,configUpdate);
System.out.println(jsonNode);
return jsonNode; return jsonNode;
} }
@Override
public void handleData(TerminalMessage data) {
JsonNode jsonNode = data.getData();
log.debug("收到消息:{}",data.getData());
String deviceId = jsonNode.get("deviceId").asText();
JsonNode values = jsonNode.get("values");
JsonNode archiveValues = jsonNode.get("archiveValues");
Long dataTime = jsonNode.get("dataTime").asLong();
Map<String, Object> keyValueMap = new HashMap<>();
String modelCode = dataService.deviceModelMap.get(deviceId);
Set<String> highKey = dataService.highIotFieldMap.get(modelCode).keySet();
Set<String> lowKey = dataService.lowIotFieldMap.get(modelCode).keySet();
Map<String, Object> highSpeedValueMap = new HashMap<>();
Map<String, Object> lowSpeedValueMap = new HashMap<>();
//数据入redis
if (values != null){
Iterator<String> keysHigh = values.fieldNames();
while (keysHigh.hasNext()) {
String fieldName = keysHigh.next();
String key = String.format("RT:%s:%s", deviceId, fieldName.toLowerCase());
keyValueMap.put(key, values.get(fieldName));
}
}
if (archiveValues != null){
Iterator<String> archiveKeys = archiveValues.fieldNames();
while (archiveKeys.hasNext()) {
String fieldName = archiveKeys.next();
String key = String.format("RT:%s:%s", deviceId, fieldName.toLowerCase());
keyValueMap.put(key, archiveValues.get(fieldName));
if (highKey.contains(fieldName)) {
highSpeedValueMap.put(fieldName, archiveValues.get(fieldName));
}
if (lowKey.contains(fieldName)) {
lowSpeedValueMap.put(fieldName, archiveValues.get(fieldName));
}
}
}
//更新td
if (!highSpeedValueMap.isEmpty()) {
List<RTData> highSpeedData = new ArrayList<>();
RTData rtHighData = RTData.builder().dataTime(dataTime).deviceId(Long.valueOf(deviceId)).values(highSpeedValueMap).build();
highSpeedData.add(rtHighData);
tdEngineService.updateYCHighValues(highSpeedData, modelCode);
}
if (!lowSpeedValueMap.isEmpty()) {
List<RTData> lowSpeedData = new ArrayList<>();
RTData rtLowData = RTData.builder().dataTime(dataTime).deviceId(Long.valueOf(deviceId)).values(lowSpeedValueMap).build();
lowSpeedData.add(rtLowData);
tdEngineService.updateYCLowValues(lowSpeedData, modelCode);
}
adminRedisTemplate.mSet(keyValueMap);
}
@Override
public void handleHighSpeed(TerminalMessage data) {
JsonNode jsonNode = data.getData();
String deviceId = jsonNode.get("deviceId").asText();
JsonNode values = jsonNode.get("values");
Map<String, Object> keyValueMap = new HashMap<>();
// 根据设备ID获取对应的物模型属性
String iotModelCode = sysIotModelMapper.getIotModel(jsonNode.get("deviceId").asLong());
//High数据
Iterator<String> keysHigh = values.fieldNames();
while (keysHigh.hasNext()) {
String fieldName = keysHigh.next();
keyValueMap.put(fieldName, values.get(fieldName));
}
Long dataTime = jsonNode.get("dataTime").asLong();
List<RTData> highList = new ArrayList<>();
RTData rtHighData = RTData.builder()
.dataTime(dataTime)
.deviceId(Long.valueOf(deviceId))
.values(keyValueMap)
.build();
highList.add(rtHighData);
tdEngineService.updateYCHighValues(highList, iotModelCode);
}
@Override
public void handleLowSpeed(TerminalMessage data) {
JsonNode jsonNode = data.getData();
String deviceId = jsonNode.get("deviceId").asText();
JsonNode values = jsonNode.get("values");
Map<String, Object> keyValueMap = new HashMap<>();
// 根据设备ID获取对应的物模型属性
String iotModelCode = sysIotModelMapper.getIotModel(jsonNode.get("deviceId").asLong());
//Low数据
Iterator<String> keysLow = values.fieldNames();
while (keysLow.hasNext()) {
String fieldName = keysLow.next();
keyValueMap.put(fieldName, values.get(fieldName));
}
Long dataTime = jsonNode.get("dataTime").asLong();
List<RTData> highList = new ArrayList<>();
RTData rtHighData = RTData.builder()
.dataTime(dataTime)
.deviceId(Long.valueOf(deviceId))
.values(keyValueMap)
.build();
highList.add(rtHighData);
tdEngineService.updateYCLowValues(highList, iotModelCode);
}
@Override
public void handleDeviceEvent(TerminalMessage data) {
JsonNode dataEvent = data.getData();
ObjectMapper objectMapper = new ObjectMapper();
List<DeviceEventInfo> valueList = new ArrayList<>();
// 使用 TypeReference 来指定转换目标类型
List<DeviceEventVo> list = objectMapper.convertValue(dataEvent, new TypeReference<List<DeviceEventVo>>() {
});
log.debug("消息data转化deviceVo,{}", list);
for (DeviceEventVo item : list) {
DeviceInfoCache deviceInfoCache = cacheService.getEquipmentCache().getDeviceInfoCacheById(Long.valueOf(item.getDeviceId()));
Integer firstTriggeredCode = adminRedisTemplate.get(String.format("RT:%s:%s", item.getDeviceId(), "FirstTriggeredCode".toLowerCase()));
DeviceEventInfo deviceEventInfo = new DeviceEventInfo();
deviceEventInfo.setEventTime(item.getEventTime());
deviceEventInfo.setEventId(IdWorker.getId());
deviceEventInfo.setAttributeCode(item.getAttrCode());
deviceEventInfo.setDeviceId(item.getDeviceId());
deviceEventInfo.setDeviceName(deviceInfoCache.getDeviceName());
deviceEventInfo.setDeviceCode(deviceInfoCache.getDeviceCode());
deviceEventInfo.setFirstTriggeredCode(firstTriggeredCode);
String eventType = getEventType(item.getEventType());
String model = dataService.deviceModelMap.get(item.getDeviceId());
if (StringUtils.isEmpty(model)) {
log.debug("未查询到物模型code设备id{}", item.getDeviceId());
}
String fieldName = dataService.fieldCodeNameMap.get(model).get(item.getAttrCode());
if (StringUtils.isEmpty(fieldName)) {
log.debug("未查询到物模型属性code设备id{}", item.getDeviceId());
}
deviceEventInfo.setEventType(item.getEventType());
deviceEventInfo.setConfirmed(0);
if (!StringUtils.isEmpty(eventType) && eventType.equals("遥信变位")) {
String stateDesc = dataService.stateDescMap.get(model).get(item.getAttrCode());
if (item.getAttrValue().equals(0)) {
deviceEventInfo.setEventText(item.getAttrCode() + fieldName + " 复归");
if (StringUtils.isNotEmpty(stateDesc)) {
List<String> descList = Arrays.stream(stateDesc.split("\\|")).toList();
deviceEventInfo.setEventText(item.getAttrCode() + fieldName + descList.get(0));
}
deviceEventInfo.setEventLevel(0);
} else {
deviceEventInfo.setEventText(item.getAttrCode() + fieldName + " 动作");
if (StringUtils.isNotEmpty(stateDesc)) {
List<String> descList = Arrays.stream(stateDesc.split("\\|")).toList();
deviceEventInfo.setEventText(item.getAttrCode() + fieldName + descList.get(1));
}
Integer level = dataService.eventLevelMap.get(model).get(item.getAttrCode());
log.debug("level:{}", level);
log.debug("fieldname{}", fieldName);
deviceEventInfo.setEventLevel(level == null ? 0 : level);
}
} else {
deviceEventInfo.setEventText(item.getAttrCode() + fieldName + eventType + ",属性值为:" + item.getAttrValue() + ",越限值为:" + item.getLimitValue());
deviceEventInfo.setEventLevel(1);
}
valueList.add(deviceEventInfo);
}
try {
tdEngineService.updateDeviceEventValues(valueList);
} catch (Exception e) {
log.error("事件信息存入Td失败,失败原因", e);
}
}
private String getEventType(int eventType) {
return switch (eventType) {
case 0 -> "遥信变位";
case 1 -> "越上限";
case 2 -> "越下限";
case 3 -> "越上上限";
case 4 -> "越下下限";
default -> "";
};
}
@Override @Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception { public void afterConnectionEstablished(WebSocketSession session) throws Exception {
String remoteIp = (String) session.getAttributes().getOrDefault(NodeConstant.REMOTE_IP, ""); String remoteIp = (String) session.getAttributes().getOrDefault(NodeConstant.REMOTE_IP, "");

View File

@ -28,35 +28,18 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.ClientAnchor; import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.HorizontalAlignment; import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment; import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xddf.usermodel.chart.*; import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.XSSFChart; import org.apache.poi.xssf.usermodel.XSSFChart;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDrawing; import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.category.DefaultCategoryDataset;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@ -105,13 +88,9 @@ public class StatisticalAnalysisServiceImpl implements StatisticalAnalysisServic
List<Map<String, Object>> dataList = new ArrayList<>(); List<Map<String, Object>> dataList = new ArrayList<>();
// 遍历数据填充Excel数据集 // 遍历数据填充Excel数据集
setTrendAnalyseExcelValue(mapsList, dataList); setTrendAnalyseExcelValue(mapsList, dataList);
//获取图表类别集
List<String> chartKey = getCharKey(map);
ExcelWriter writer = ExcelUtil.getWriter(RandomUtil.randomInt(100, 1000) + "statistics" + ".xlsx"); ExcelWriter writer = ExcelUtil.getWriter(RandomUtil.randomInt(100, 1000) + "statistics" + ".xlsx");
//设置Excel样式 //设置Excel样式
setExcelStyle(writer, map, dataList); setExcelStyle(writer, map, dataList);
//生成折线图
addChartToExcel(writer,dataList,StatisticalAnalysisConstant.TREND_ANALYSE,chartKey);
//下载Excel //下载Excel
downloadExcel(response, writer,StatisticalAnalysisConstant.TREND_ANALYSE); downloadExcel(response, writer,StatisticalAnalysisConstant.TREND_ANALYSE);
} }
@ -150,18 +129,9 @@ public class StatisticalAnalysisServiceImpl implements StatisticalAnalysisServic
setPowerCurveExcelValue(resultMap, dataList, curveItemEntitieList); setPowerCurveExcelValue(resultMap, dataList, curveItemEntitieList);
//获取功率曲线的列 //获取功率曲线的列
LinkedHashMap<String, String> map = getPowerCurveColumnName(param); LinkedHashMap<String, String> map = getPowerCurveColumnName(param);
//获取图表类别集
List<String> chartKey = getCharKey(map);
ExcelWriter writer = ExcelUtil.getWriter(RandomUtil.randomInt(100, 1000) + "statistics" + ".xlsx"); ExcelWriter writer = ExcelUtil.getWriter(RandomUtil.randomInt(100, 1000) + "statistics" + ".xlsx");
//设置Excel样式 //设置Excel样式
setExcelStyle(writer, map, dataList); setExcelStyle(writer, map, dataList);
//生成折线图
if (param.getDisplayCurve()==1){
addChartToExcel(writer,dataList,StatisticalAnalysisConstant.POWER_CURVE,chartKey);
}else {
//生成散点图
addScattersChartToExcel(writer,dataList,StatisticalAnalysisConstant.POWER_CURVE,chartKey);
}
//下载Excel //下载Excel
downloadExcel(response, writer,StatisticalAnalysisConstant.POWER_CURVE); downloadExcel(response, writer,StatisticalAnalysisConstant.POWER_CURVE);
} }
@ -191,15 +161,11 @@ public class StatisticalAnalysisServiceImpl implements StatisticalAnalysisServic
//自定义别名 别名的key和实体类中的名称要对应上 //自定义别名 别名的key和实体类中的名称要对应上
LinkedHashMap<String, String> map = gettrendContrastColumnName(param); LinkedHashMap<String, String> map = gettrendContrastColumnName(param);
List<Map<String, Object>> dataList = new ArrayList<>(); List<Map<String, Object>> dataList = new ArrayList<>();
//图表类别集
List<String> charKey = getCharKey(map);
// 遍历数据将数据添加到dataList中 // 遍历数据将数据添加到dataList中
setTrendContrastExcelValue(maps, dataList); setTrendContrastExcelValue(maps, dataList);
ExcelWriter writer = ExcelUtil.getWriter(RandomUtil.randomInt(100, 1000) + "statistics" + ".xlsx"); ExcelWriter writer = ExcelUtil.getWriter(RandomUtil.randomInt(100, 1000) + "statistics" + ".xlsx");
//设置Excel样式 //设置Excel样式
setExcelStyle(writer, map, dataList); setExcelStyle(writer, map, dataList);
//生成折线图
addChartToExcel(writer,dataList,StatisticalAnalysisConstant.TREND_CONTRAST,charKey);
//下载Excel //下载Excel
downloadExcel(response, writer,StatisticalAnalysisConstant.TREND_CONTRAST); downloadExcel(response, writer,StatisticalAnalysisConstant.TREND_CONTRAST);
} }
@ -284,22 +250,7 @@ public class StatisticalAnalysisServiceImpl implements StatisticalAnalysisServic
} }
} }
/**
* 获取图表类别集
* @param map Excel的列
* @return 图表类别集
*/
private List<String> getCharKey(LinkedHashMap<String, String> map) {
//获取图表类别集
List<String> chartKey = new ArrayList<>();
for (Map.Entry<String, String> stringStringEntry : map.entrySet()) {
String value = stringStringEntry.getValue();
if (!value.contains("时间")){
chartKey.add(value);
}
}
return chartKey;
}
/** /**
* 趋势对比填充Excel数据集 * 趋势对比填充Excel数据集
@ -542,75 +493,20 @@ public class StatisticalAnalysisServiceImpl implements StatisticalAnalysisServic
} }
/** /**
* 使用JFreeChart生成折线图(图片),已弃用 * 获取图表类别集
* @param data excel数据集 * @param map Excel的列
* @param writer excel对象 * @return 图表类别集
* @param dataset 图表数据集
* @param titleStr 标题
*/ */
private void createChart(List<Map<String, Object>> data, ExcelWriter writer, private List<String> getCharKey(LinkedHashMap<String, String> map) {
DefaultCategoryDataset dataset, String titleStr) { //获取图表类别集
// 获取Sheet对象 List<String> chartKey = new ArrayList<>();
XSSFSheet xssfSheet = (XSSFSheet) writer.getSheet(); for (Map.Entry<String, String> stringStringEntry : map.entrySet()) {
Workbook workbook = writer.getWorkbook(); String value = stringStringEntry.getValue();
JFreeChart chart = ChartFactory.createLineChart( if (!value.contains("时间")){
titleStr, // 图表标题 chartKey.add(value);
"", // 横轴标签
"", // 纵轴标签
dataset, // 数据集
PlotOrientation.VERTICAL, // 图表方向
true, // 是否显示图例
true, // 是否使用工具提示
false // 是否生成URL链接
);
// 设置图表标题的字体
TextTitle title = chart.getTitle();
title.setFont(new java.awt.Font("SimSun", java.awt.Font.BOLD, 16));
// 获取图表的绘图区域
CategoryPlot plot = chart.getCategoryPlot();
// 设置横轴标签
CategoryAxis domainAxis = plot.getDomainAxis();
domainAxis.setLabelFont(new java.awt.Font("SimSun", java.awt.Font.PLAIN, 12));
domainAxis.setMaximumCategoryLabelLines(1); // 可以控制标签行数
domainAxis.setCategoryMargin(3); // 控制类别之间的间距
domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45); // 旋转横轴标签为45度,90度为:UP_90
domainAxis.setLabelFont(new Font("SansSerif", Font.PLAIN, 7));//调整字体大小
// if (data.size() > 50) {
// domainAxis.setVisible(false); // 隐藏横坐标
// }
// 设置图例的字体
LegendTitle legend = chart.getLegend();
if (legend != null) {
legend.setItemFont(new java.awt.Font("SimSun", java.awt.Font.PLAIN, 12));
} }
// 设置绘图区域的背景颜色
plot.setBackgroundPaint(Color.WHITE);
// 设置绘图区域的边框
plot.setOutlinePaint(Color.LIGHT_GRAY);
plot.setOutlineVisible(true);
// 设置网格线的颜色
plot.setDomainGridlinePaint(Color.LIGHT_GRAY);
plot.setRangeGridlinePaint(Color.LIGHT_GRAY);
// 设置线条的宽度
CategoryItemRenderer renderer1 = plot.getRenderer();
renderer1.setSeriesStroke(0, new BasicStroke(2.0f)); // 设置线条宽度
// 将图表保存为 PNG 文件
String chartFilePath = "lineChart.png";
// 调整图表尺寸
int width = 750;
int height = 400;
try {
ChartUtils.saveChartAsPNG(new File(chartFilePath), chart, width, height);
byte[] bytes = java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(chartFilePath));
int pictureIdx = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
// 计算数据的最后一行,创建图表插入位置的锚点
XSSFClientAnchor anchor = new XSSFClientAnchor(0, 1, 0, 1, 0, data.size() + 2, 15, data.size() + 30);
XSSFDrawing drawing = xssfSheet.createDrawingPatriarch();
drawing.createPicture(anchor, pictureIdx);
} catch (IOException e) {
throw new ServiceException("图表保存失败==" + e);
} }
return chartKey;
} }
/** /**

View File

@ -77,6 +77,10 @@
select sim.iot_model_code from sys_iot_model sim left join sys_equipment se on sim.id = se.iot_model_id select sim.iot_model_code from sys_iot_model sim left join sys_equipment se on sim.id = se.iot_model_id
where se.id = #{id} where se.id = #{id}
</select> </select>
<select id="getIodModelCode" resultType="java.lang.String">
select sim.iot_model_code from sys_iot_model sim where sim.id = #{id}
</select>
<select id="getAllIotModelField" resultType="com.das.modules.equipment.entity.SysIotModelField"> <select id="getAllIotModelField" resultType="com.das.modules.equipment.entity.SysIotModelField">
select simf.attribute_name as attributeName, simf.attribute_code as attributeCode,simf.highspeed as highSpeed,simf.datatype as dataType,simf.attribute_type as attributeType,simf.level as level,simf.stateDesc as stateDesc from sys_iot_model_field simf where simf.iot_model_id = #{id} order by simf.attribute_code select simf.attribute_name as attributeName, simf.attribute_code as attributeCode,simf.highspeed as highSpeed,simf.datatype as dataType,simf.attribute_type as attributeType,simf.level as level,simf.stateDesc as stateDesc from sys_iot_model_field simf where simf.iot_model_id = #{id} order by simf.attribute_code
</select> </select>

View File

@ -157,6 +157,8 @@ export const getRealValueListReq = (data: { deviceId: string, attributes?: (stri
method: 'post', method: 'post',
data: data, data: data,
timeout: 60 * 1000 timeout: 60 * 1000
},{
showErrorMessage: false
}) })
} }
@ -166,6 +168,8 @@ export const getRealValueRangeReq = (data: { startTime: number, endTime: number,
method: 'post', method: 'post',
data: data, data: data,
timeout: 60 * 1000 timeout: 60 * 1000
},{
showErrorMessage: false
}) })
} }

View File

@ -274,7 +274,7 @@
<el-form-item :label="ModelAttributeFieldsEnums['subSystem']" prop="subSystem"> <el-form-item :label="ModelAttributeFieldsEnums['subSystem']" prop="subSystem">
<el-input v-model="attributeForm.subSystem" :placeholder="'请输入' + ModelAttributeFieldsEnums['subSystem']"></el-input> <el-input v-model="attributeForm.subSystem" :placeholder="'请输入' + ModelAttributeFieldsEnums['subSystem']"></el-input>
</el-form-item> </el-form-item>
<el-form-item :label="ModelAttributeFieldsEnums['highSpeed']" prop="highSpeed"> <el-form-item v-if="attributeForm.attributeType === 138" :label="ModelAttributeFieldsEnums['highSpeed']" prop="highSpeed">
<el-checkbox v-model="attributeForm.highSpeed"></el-checkbox> <el-checkbox v-model="attributeForm.highSpeed"></el-checkbox>
</el-form-item> </el-form-item>
<el-form-item :label="ModelAttributeFieldsEnums['visible']" prop="visible"> <el-form-item :label="ModelAttributeFieldsEnums['visible']" prop="visible">

View File

@ -43,16 +43,25 @@
<div class="tabsPart"> <div class="tabsPart">
<el-table :data="linkMonitorTableData" class="tablePart" highlight-current-row> <el-table :data="linkMonitorTableData" class="tablePart" highlight-current-row>
<el-table-column prop="linkName" :label="LinkMonitorFieldsEnums['linkName']" align="left"> </el-table-column> <el-table-column prop="linkName" :label="LinkMonitorFieldsEnums['linkName']" align="left"> </el-table-column>
<el-table-column prop="status" :label="LinkMonitorFieldsEnums['status']" align="center" width="80"> <el-table-column prop="status" :label="LinkMonitorFieldsEnums['status']" align="center" width="100">
<template #default="scope"> <template #default="scope">
<div class="status-container"> <div class="status-container">
<span :class="scope.row.status == 0 ? 'status-dot-online' : 'status-dot-offline'"></span> <el-tag v-if="scope.row.onlineStatus === 1" type="success">在线</el-tag>
<el-tag v-else type="danger">离线</el-tag>
</div>
<div></div>
</template>
</el-table-column>
<el-table-column label="数据状态" width="150" align="left">
<template #default="scope">
<div class="dataState">
<el-tag :type="scope.row.realStatus === 1 ? 'success' : 'danger'">实时数据</el-tag>
<el-tag v-if="scope.row.ftpStatus === 1" type="success">FTP</el-tag>
<el-tag v-else-if="scope.row.ftpStatus === 0" type="danger">FTP</el-tag>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="protocolName" :label="LinkMonitorFieldsEnums['protocolName']" align="center"> <el-table-column prop="protocolName" :label="LinkMonitorFieldsEnums['protocolName']" align="center"> </el-table-column>
<template #default="scope"> </template>
</el-table-column>
<!-- <el-table-column prop="remoteRegulation" :label="LinkMonitorFieldsEnums['remoteRegulation']" align="center"> <!-- <el-table-column prop="remoteRegulation" :label="LinkMonitorFieldsEnums['remoteRegulation']" align="center">
<template #default="scope"> <template #default="scope">
<span style="cursor: pointer; color: #0064aa; font-weight: 600">查看参数</span> <span style="cursor: pointer; color: #0064aa; font-weight: 600">查看参数</span>
@ -460,6 +469,11 @@ $paginationHeight: 32px;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.dataState {
display: flex;
justify-content: space-between;
align-items: center;
}
.status-dot-online, .status-dot-online,
.status-dot-offline { .status-dot-offline {
width: 10px; width: 10px;

View File

@ -18,8 +18,8 @@
</div> </div>
<div class="headerPart headerPartRight"> <div class="headerPart headerPartRight">
<span>风机编号</span> <span>风机编号</span>
<el-select v-model="windTurbinesCode"> <el-select v-model="windTurbinesCode" clearable placeholder="请选择风机">
<el-option v-for="item in attributeCodeList" :key="item.value" :value="item.value"></el-option> <el-option v-for="item in attributeCodeList" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select> </el-select>
</div> </div>
<el-button @click="search" :icon="Search" type="primary" class="headerBtn">查询</el-button> <el-button @click="search" :icon="Search" type="primary" class="headerBtn">查询</el-button>
@ -90,7 +90,7 @@ const shortcuts = [
] ]
const windTurbinesCode = ref('') const windTurbinesCode = ref('')
const attributeCodeList = ref<{ value: string }[]>([]) const attributeCodeList = ref<{ value: string; label: string }[]>([])
const tableData = ref<OperatingResType[]>([]) const tableData = ref<OperatingResType[]>([])
const tableColumn = [ const tableColumn = [
@ -152,7 +152,8 @@ const getAirBlowerList = () => {
if (res.success) { if (res.success) {
attributeCodeList.value = res.data.map((item: any) => { attributeCodeList.value = res.data.map((item: any) => {
return { return {
value: item.name, value: item.deviceCode,
label: item.name,
} }
}) })
} }