Merge branch 'main' of https://git.jsspisoft.com/ry-das
This commit is contained in:
commit
1f01735820
13
das/src/main/java/com/das/modules/cache/domain/IotFieldInfoCache.java
vendored
Normal file
13
das/src/main/java/com/das/modules/cache/domain/IotFieldInfoCache.java
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
package com.das.modules.cache.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class IotFieldInfoCache {
|
||||
private String attributeCode;
|
||||
private String attributeName;
|
||||
private Integer attributeType;
|
||||
private Integer porder;
|
||||
private Integer highspeed;
|
||||
private Integer datatype;
|
||||
}
|
12
das/src/main/java/com/das/modules/cache/domain/IotModelInfoCache.java
vendored
Normal file
12
das/src/main/java/com/das/modules/cache/domain/IotModelInfoCache.java
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
package com.das.modules.cache.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Data
|
||||
public class IotModelInfoCache {
|
||||
private Long iotModelId;
|
||||
private String iodModelCode;
|
||||
private ConcurrentHashMap<String,IotFieldInfoCache> fieldInfoCache;
|
||||
}
|
@ -10,4 +10,10 @@ public interface CacheService {
|
||||
* @return
|
||||
*/
|
||||
EquipmentCache getEquipmentCache();
|
||||
|
||||
/**
|
||||
* 获取物模型缓存接口
|
||||
* @return
|
||||
*/
|
||||
IotModelCache getIotModelCache();
|
||||
}
|
||||
|
@ -33,4 +33,10 @@ public interface EquipmentCache {
|
||||
* @return
|
||||
*/
|
||||
DeviceInfoCache getDeviceInfoCacheById(Long deviceId);
|
||||
|
||||
/**
|
||||
* 移除指定设备缓存
|
||||
* @param deviceId
|
||||
*/
|
||||
void removeDeviceCache(Long deviceId);
|
||||
}
|
||||
|
5
das/src/main/java/com/das/modules/cache/service/IotModelCache.java
vendored
Normal file
5
das/src/main/java/com/das/modules/cache/service/IotModelCache.java
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
package com.das.modules.cache.service;
|
||||
|
||||
public interface IotModelCache {
|
||||
|
||||
}
|
@ -2,6 +2,7 @@ package com.das.modules.cache.service.impl;
|
||||
|
||||
import com.das.modules.cache.service.CacheService;
|
||||
import com.das.modules.cache.service.EquipmentCache;
|
||||
import com.das.modules.cache.service.IotModelCache;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -9,10 +10,20 @@ import org.springframework.stereotype.Service;
|
||||
public class CacheServiceImpl implements CacheService {
|
||||
|
||||
@Autowired
|
||||
EquipmentCache equipmentCache;;
|
||||
EquipmentCache equipmentCache;
|
||||
|
||||
@Autowired
|
||||
IotModelCache iotModelCache;
|
||||
|
||||
@Override
|
||||
public EquipmentCache getEquipmentCache() {
|
||||
return equipmentCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IotModelCache getIotModelCache() {
|
||||
return iotModelCache;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -110,4 +110,13 @@ public class EquipmentCacheImpl implements EquipmentCache {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDeviceCache(Long deviceId) {
|
||||
Integer index = deviceIdIndex.get(deviceId);
|
||||
if (index != null) {
|
||||
deviceInfoCaches.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
24
das/src/main/java/com/das/modules/cache/service/impl/IotModelCacheImpl.java
vendored
Normal file
24
das/src/main/java/com/das/modules/cache/service/impl/IotModelCacheImpl.java
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package com.das.modules.cache.service.impl;
|
||||
|
||||
import com.das.modules.cache.service.IotModelCache;
|
||||
import com.das.modules.equipment.mapper.SysIotModelMapper;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class IotModelCacheImpl implements IotModelCache {
|
||||
|
||||
@Autowired
|
||||
SysIotModelMapper sysIotModelMapper;
|
||||
|
||||
@PostConstruct
|
||||
public void init(){
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void destroy(){
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package com.das.modules.calc.functions;
|
||||
|
||||
import cn.hutool.core.date.DateUnit;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.das.modules.cache.domain.DeviceInfoCache;
|
||||
import com.das.modules.cache.service.CacheService;
|
||||
import com.das.modules.data.domain.SnapshotValueQueryParam;
|
||||
import com.das.modules.data.service.DataService;
|
||||
import com.googlecode.aviator.runtime.function.AbstractFunction;
|
||||
import com.googlecode.aviator.runtime.type.AviatorNil;
|
||||
import com.googlecode.aviator.runtime.type.AviatorObject;
|
||||
import com.googlecode.aviator.runtime.type.AviatorRuntimeJavaType;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Aviator扩展函数 - 获取时间维度内最早的一条数据
|
||||
* 函数格式: topv(deviceId, attr, timedim)
|
||||
* timedim: day - 天, month - 月, year - 年
|
||||
* 返回值:数值, nil - 获取错误
|
||||
*/
|
||||
@Slf4j
|
||||
public class FunctionTopValue extends AbstractFunction {
|
||||
|
||||
private DataService dataService = null;
|
||||
private CacheService cacheService = null;
|
||||
|
||||
private ConcurrentHashMap<String,CacheValue> cacheValues = new ConcurrentHashMap<>();
|
||||
|
||||
public FunctionTopValue(DataService dataService, CacheService cacheService) {
|
||||
this.dataService = dataService;
|
||||
this.cacheService = cacheService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "topv";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AviatorObject call(Map<String, Object> env, AviatorObject deviceCode, AviatorObject attr, AviatorObject timeDim) {
|
||||
//设备Code
|
||||
String code = (String)deviceCode.getValue(env);
|
||||
String attrName = (String)attr.getValue(env);
|
||||
String timeDimName = (String)timeDim.getValue(env);
|
||||
|
||||
DeviceInfoCache deviceInfoCache = cacheService.getEquipmentCache().getDeviceInfoCacheByCode(code);
|
||||
if (deviceInfoCache == null) {
|
||||
return AviatorNil.NIL;
|
||||
}
|
||||
String key = String.format("%d_%s_%s", deviceInfoCache.getDeviceId(), attrName, timeDimName);
|
||||
//根据维度,获取维度时间
|
||||
Date curTimeValue = null;
|
||||
switch (timeDimName) {
|
||||
case "day":
|
||||
curTimeValue = DateUtil.beginOfDay(DateUtil.date());
|
||||
break;
|
||||
case "month":
|
||||
curTimeValue = DateUtil.beginOfMonth(DateUtil.date());
|
||||
break;
|
||||
case "year":
|
||||
curTimeValue = DateUtil.beginOfYear(DateUtil.date());
|
||||
break;
|
||||
default:
|
||||
return AviatorNil.NIL;
|
||||
}
|
||||
CacheValue cacheValue = cacheValues.get(key);
|
||||
if (cacheValue != null) {
|
||||
//缓存中存在,检查是否过期
|
||||
if (cacheValue.getCurTimeValue() != null && DateUtil.compare(cacheValue.getCurTimeValue(), curTimeValue) == 0) {
|
||||
return AviatorRuntimeJavaType.valueOf(cacheValue.value);
|
||||
}
|
||||
}
|
||||
//未找到缓存,查询时序API获取数据
|
||||
return AviatorRuntimeJavaType.valueOf(1);
|
||||
}
|
||||
|
||||
@Data
|
||||
class CacheValue{
|
||||
private Double value;
|
||||
private Date curTimeValue;
|
||||
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ public class CalcJob implements Job {
|
||||
}
|
||||
Expression expression = instance.getCachedExpressionByKey(calcModule.getName());
|
||||
if (expression == null) {
|
||||
log.error("expression is null, calcModule={}", calcModule.getName());
|
||||
throw new JobExecutionException("expression is null");
|
||||
}
|
||||
Map<String,Object> envs = expression.newEnv("G_DEVICES", cacheService.getEquipmentCache().getDevicesCache());
|
||||
|
@ -126,6 +126,7 @@ public class CalcService {
|
||||
try{
|
||||
//预编译脚本
|
||||
aviator.compile(scriptModule.getName(), scriptModule.getScript(), true);
|
||||
log.info("[预编译脚本] - {}", scriptModule.getName());
|
||||
startCalcJob(scriptModule);
|
||||
}
|
||||
catch (Exception e){
|
||||
|
@ -0,0 +1,9 @@
|
||||
package com.das.modules.data.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RTValue {
|
||||
private Object value;
|
||||
private Long time;
|
||||
}
|
@ -17,5 +17,5 @@ public interface DataService {
|
||||
|
||||
void updateCalFieldData(List<CalculateRTData> values);
|
||||
|
||||
|
||||
Double getTimeTopValue(Long devcieId, String attr, long startTime, long endTime);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.das.common.utils.PageDataInfo;
|
||||
import com.das.modules.data.domain.DeviceEventInfo;
|
||||
import com.das.modules.data.domain.RTValue;
|
||||
import com.das.modules.equipment.domain.vo.IotModelFieldVo;
|
||||
import com.das.modules.node.domain.bo.CalculateRTData;
|
||||
import com.das.modules.node.domain.bo.RTData;
|
||||
@ -681,7 +682,7 @@ public class TDEngineService {
|
||||
}
|
||||
}
|
||||
if (pageSize != null) {
|
||||
sb.append(" limit ").append(offset).append(",").append(pageSize);
|
||||
sb.append(" desc limit ").append(offset).append(",").append(pageSize);
|
||||
total = getEventCount(eventLevel, startTime, endTime, deviceCodeList);
|
||||
}
|
||||
|
||||
@ -702,6 +703,7 @@ public class TDEngineService {
|
||||
deviceEventInfo.setConfirmTime(rs.getLong("confirm_time"));
|
||||
deviceEventInfo.setDeviceCode(rs.getString("device_code"));
|
||||
deviceEventInfo.setDeviceId(rs.getString("device_id"));
|
||||
deviceEventInfo.setDeviceName(rs.getString("device_name"));
|
||||
result.add(deviceEventInfo);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@ -781,4 +783,27 @@ public class TDEngineService {
|
||||
hikariDataSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
public Double getTimeTopValue(String tableName, String attr, long startTime, long endTime) {
|
||||
StringBuffer sb = new StringBuffer(256);
|
||||
sb.append("select ");
|
||||
sb.append("first(");
|
||||
sb.append(attr);
|
||||
sb.append(") as value");
|
||||
sb.append(" from ");
|
||||
sb.append(tableName);
|
||||
sb.append(" where ");
|
||||
sb.append(String.format(" updatetime >= %d and updatetime < %d ", startTime, endTime));
|
||||
Double result = null;
|
||||
try (Connection conn = hikariDataSource.getConnection();
|
||||
Statement smt = conn.createStatement();
|
||||
ResultSet rs = smt.executeQuery(sb.toString())) {
|
||||
if (rs.next()) {
|
||||
result = rs.getDouble("value");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("获取数据异常", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import com.das.common.exceptions.ServiceException;
|
||||
import com.das.common.utils.AdminRedisTemplate;
|
||||
import com.das.modules.cache.domain.DeviceInfoCache;
|
||||
import com.das.modules.cache.service.CacheService;
|
||||
import com.das.modules.data.domain.SnapshotValueQueryParam;
|
||||
import com.das.modules.data.domain.TSValueQueryParam;
|
||||
import com.das.modules.data.service.DataService;
|
||||
@ -19,6 +21,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -45,6 +48,9 @@ public class DataServiceImpl implements DataService {
|
||||
@Autowired
|
||||
SysIotModelMapper sysIotModelMapper;
|
||||
|
||||
@Autowired
|
||||
CacheService cacheService;
|
||||
|
||||
//key:deviceId value:modelCode
|
||||
public ConcurrentHashMap<String, String> deviceModelMap = new ConcurrentHashMap<>(10000);
|
||||
|
||||
@ -141,7 +147,8 @@ public class DataServiceImpl implements DataService {
|
||||
}
|
||||
|
||||
private Map<String, Map<String, Map<String, Object>>> queryHistoryCurveValues(Long irn, Date startTime, Date endTime, String interval, String fill, List<String> attributes) {
|
||||
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start("prepare resources");
|
||||
String iotModelCode = sysIotModelFieldMapper.queryModelCodeByDeviceId(irn);
|
||||
Map<String, Object> highSpeedFieldMap = highIotFieldMap.get(iotModelCode);
|
||||
Map<String, Object> lowSpeedFieldMap = lowIotFieldMap.get(iotModelCode);
|
||||
@ -160,11 +167,15 @@ public class DataServiceImpl implements DataService {
|
||||
calField.add(field);
|
||||
}
|
||||
}
|
||||
stopWatch.stop();
|
||||
stopWatch.start("HighSpeedValues");
|
||||
Map<String, Map<String, Map<String, Object>>> result = new HashMap<>();
|
||||
if (!CollectionUtils.isEmpty(highSpeedField)) {
|
||||
Map<String, Map<String, Map<String, Object>>> highHistoryCurve = tdEngineService.fetchHighHistoryCurve(irn, startTime, endTime, interval, highSpeedField);
|
||||
result.putAll(highHistoryCurve);
|
||||
}
|
||||
stopWatch.stop();
|
||||
stopWatch.start("LowSpeedValues");
|
||||
if (!CollectionUtils.isEmpty(lowSpeedField)) {
|
||||
Map<String, Map<String, Map<String, Object>>> lowHistoryCurve = tdEngineService.fetchLowHistoryCurve(irn, startTime, endTime, interval, lowSpeedField);
|
||||
if (result.get(irn.toString()) == null) {
|
||||
@ -173,6 +184,8 @@ public class DataServiceImpl implements DataService {
|
||||
result.get(irn.toString()).putAll(lowHistoryCurve.get(irn.toString()));
|
||||
}
|
||||
}
|
||||
stopWatch.stop();
|
||||
stopWatch.start("CalculateValues");
|
||||
if (!CollectionUtils.isEmpty(calField)){
|
||||
ListUtil.page(calField,COMMIT_COUNT,list -> {
|
||||
for (String item : list){
|
||||
@ -185,6 +198,8 @@ public class DataServiceImpl implements DataService {
|
||||
}
|
||||
});
|
||||
}
|
||||
stopWatch.stop();
|
||||
log.debug("查询历史数据耗时: {}", stopWatch.prettyPrint());
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -236,4 +251,21 @@ public class DataServiceImpl implements DataService {
|
||||
tdEngineService.initIotModel(allIotModel, highIotFieldMap, lowIotFieldMap, calculateIotFieldMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定时间区间内的最早的数据
|
||||
* @param devcieId
|
||||
* @param attr
|
||||
* @param startTime
|
||||
* @param endTime
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Double getTimeTopValue(Long devcieId, String attr, long startTime, long endTime){
|
||||
DeviceInfoCache deviceInfoCacheById = cacheService.getEquipmentCache().getDeviceInfoCacheById(devcieId);
|
||||
if (deviceInfoCacheById == null) {
|
||||
return null;
|
||||
}
|
||||
String tableName = "";
|
||||
return tdEngineService.getTimeTopValue(tableName, attr, startTime, endTime);
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import com.das.common.utils.PageQuery;
|
||||
import com.das.common.utils.SequenceUtils;
|
||||
import com.das.modules.auth.domain.vo.SysUserVo;
|
||||
import com.das.modules.auth.mapper.SysOrgMapper;
|
||||
import com.das.modules.cache.service.CacheService;
|
||||
import com.das.modules.data.service.impl.DataServiceImpl;
|
||||
import com.das.modules.equipment.domain.dto.SysEquipmentDto;
|
||||
import com.das.modules.equipment.domain.excel.SysEquipmentExcel;
|
||||
@ -62,6 +63,9 @@ public class SysEquipmentServiceImpl implements SysEquipmentService {
|
||||
@Autowired
|
||||
private DataServiceImpl dataService;
|
||||
|
||||
@Autowired
|
||||
private CacheService cacheService;
|
||||
|
||||
@Override
|
||||
public SysEquipmentVo creatSysEquipment(SysEquipmentDto sysEquipmentDto) {
|
||||
//去除空格
|
||||
@ -84,6 +88,8 @@ public class SysEquipmentServiceImpl implements SysEquipmentService {
|
||||
if (sysEquipment.getIotModelId() != null){
|
||||
dataService.deviceModelMap.put(sysEquipment.getId().toString(),dataService.iotModelMap.get(sysEquipment.getIotModelId().toString()));
|
||||
}
|
||||
//更新设备缓存
|
||||
cacheService.getEquipmentCache().refreshDeviceCache(sysEquipment.getId());
|
||||
SysEquipmentVo sysEquipmentVo = new SysEquipmentVo();
|
||||
BeanCopyUtils.copy(sysEquipment, sysEquipmentVo);
|
||||
return sysEquipmentVo;
|
||||
@ -111,6 +117,8 @@ public class SysEquipmentServiceImpl implements SysEquipmentService {
|
||||
if (oldModelSysEquipInfo.getIotModelId() == null && sysEquipment.getIotModelId() != null){
|
||||
dataService.deviceModelMap.put(sysEquipment.getId().toString(),dataService.iotModelMap.get(sysEquipment.getIotModelId().toString()));
|
||||
}
|
||||
//更新设备缓存
|
||||
cacheService.getEquipmentCache().refreshDeviceCache(sysEquipment.getId());
|
||||
SysEquipmentVo sysEquipmentVo = new SysEquipmentVo();
|
||||
BeanCopyUtils.copy(sysEquipment, sysEquipmentVo);
|
||||
return sysEquipmentVo;
|
||||
@ -124,6 +132,8 @@ public class SysEquipmentServiceImpl implements SysEquipmentService {
|
||||
}
|
||||
sysEquipmentMapper.deleteById(sysEquipmentDto.getId());
|
||||
//删除缓存
|
||||
//更新设备缓存
|
||||
cacheService.getEquipmentCache().refreshDeviceCache(sysEquipmentDto.getId());
|
||||
dataService.deviceModelMap.remove(sysEquipmentDto.getId().toString());
|
||||
}
|
||||
|
||||
@ -297,6 +307,8 @@ public class SysEquipmentServiceImpl implements SysEquipmentService {
|
||||
String modelCode = dataService.iotModelMap.get(item.getIotModelId().toString());
|
||||
dataService.deviceModelMap.put(item.getId().toString(),modelCode);
|
||||
}
|
||||
//更新设备缓存
|
||||
cacheService.getEquipmentCache().refreshDeviceCache(item.getId());
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(updateSysEquipmentList)) {
|
||||
sysEquipmentMapper.updateBatchById(updateSysEquipmentList);
|
||||
@ -305,16 +317,17 @@ public class SysEquipmentServiceImpl implements SysEquipmentService {
|
||||
String modelCode = dataService.iotModelMap.get(item.getIotModelId().toString());
|
||||
dataService.deviceModelMap.put(item.getId().toString(),modelCode);
|
||||
}
|
||||
//更新设备缓存
|
||||
cacheService.getEquipmentCache().refreshDeviceCache(item.getId());
|
||||
}
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(delSysEquipmentList)) {
|
||||
// 删除设备
|
||||
sysEquipmentMapper.deleteBatchIds(delSysEquipmentList);
|
||||
for (SysEquipment item : updateSysEquipmentList){
|
||||
if (item.getIotModelId() != null){
|
||||
String modelCode = dataService.iotModelMap.get(item.getIotModelId().toString());
|
||||
dataService.deviceModelMap.put(item.getId().toString(),modelCode);
|
||||
}
|
||||
for (SysEquipment item : delSysEquipmentList){
|
||||
dataService.deviceModelMap.remove(item.getId().toString());
|
||||
//更新设备缓存
|
||||
cacheService.getEquipmentCache().removeDeviceCache(item.getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.lmax.disruptor.BlockingWaitStrategy;
|
||||
import com.lmax.disruptor.RingBuffer;
|
||||
import com.lmax.disruptor.YieldingWaitStrategy;
|
||||
import com.lmax.disruptor.dsl.Disruptor;
|
||||
@ -95,7 +96,7 @@ public class NodeMessageServiceImpl extends TextWebSocketHandler implements Node
|
||||
//初始化高性能队列
|
||||
int cpu = Runtime.getRuntime().availableProcessors();
|
||||
int bufferSize = 1024 * 4;
|
||||
disruptor = new Disruptor<>(TerminalMessage::new, bufferSize, DaemonThreadFactory.INSTANCE, ProducerType.MULTI, new YieldingWaitStrategy());
|
||||
disruptor = new Disruptor<>(TerminalMessage::new, bufferSize, DaemonThreadFactory.INSTANCE, ProducerType.MULTI, new BlockingWaitStrategy());
|
||||
//
|
||||
TerminalMessageWorkerHandler[] workerHandlers = new TerminalMessageWorkerHandler[cpu];
|
||||
for (int i = 0; i < cpu; i++) {
|
||||
|
@ -34,5 +34,11 @@ public class HomeWindTurbineMatrixDataVoVo {
|
||||
*/
|
||||
private Double nominalCapacity;
|
||||
|
||||
private String deviceCode;
|
||||
|
||||
private Map<String,Object> attributeMap;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import java.util.List;
|
||||
public interface TemperatureDashboardService {
|
||||
/**
|
||||
* 根据设备id获取该设备对应的温度相关测点的限制配置信息
|
||||
* @param deviceId 设备id
|
||||
* @param temperatureLimitDto 设备信息
|
||||
* @return 限制配置列表(如果限制未设置 则不返回对应测点信息)
|
||||
*/
|
||||
List<TemperatureLimitVo> getTemperatureLimitByDeviceId(TemperatureLimitDto temperatureLimitDto);
|
||||
|
@ -82,6 +82,7 @@ public class HomeServiceImpl implements HomeService {
|
||||
homeWindRealTimeVoResult.setIrn(item.getId());
|
||||
homeWindRealTimeVoResult.setName(item.getName());
|
||||
homeWindRealTimeVoResult.setModel(item.getModel());
|
||||
homeWindRealTimeVoResult.setDeviceCode(item.getCode());
|
||||
homeWindRealTimeVoResult.setModelId(item.getIotModelId());
|
||||
homeWindRealTimeVoResult.setBelongLine(item.getBelongLine());
|
||||
homeWindRealTimeVoResult.setStandard(item.getStandard());
|
||||
|
@ -9,10 +9,7 @@ import com.das.modules.page.service.TemperatureDashboardService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class TemperatureDashboardServiceImpl implements TemperatureDashboardService {
|
||||
@ -62,7 +59,8 @@ public class TemperatureDashboardServiceImpl implements TemperatureDashboardServ
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return new ArrayList<>(map.values());
|
||||
ArrayList<TemperatureLimitVo> temperatureLimitVos = new ArrayList<>(map.values());
|
||||
temperatureLimitVos.sort(Comparator.comparing(TemperatureLimitVo::getMeasPointName));
|
||||
return temperatureLimitVos;
|
||||
}
|
||||
}
|
||||
|
@ -40,16 +40,16 @@ spring:
|
||||
# 多个文件总大小
|
||||
max-request-size: 2048MB
|
||||
datasource:
|
||||
url: jdbc:postgresql://192.168.109.102:5432/das
|
||||
url: jdbc:postgresql://192.168.109.187:5432/das
|
||||
username: das
|
||||
password: qwaszx12
|
||||
# # redis相关配置
|
||||
data:
|
||||
redis:
|
||||
host: 192.168.109.195
|
||||
host: 192.168.109.187
|
||||
database: 0
|
||||
port: 6379
|
||||
password:
|
||||
password: zaq12WSX
|
||||
client-type: lettuce
|
||||
|
||||
|
||||
|
4
ui/.gitignore
vendored
Normal file
4
ui/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
.idea
|
||||
das-dn/.vscode
|
||||
ui/**/dist
|
||||
das/**/target
|
@ -43,8 +43,12 @@
|
||||
<div class="tabsPart">
|
||||
<el-table :data="alarmsTableData" class="tablePart" highlight-current-row>
|
||||
<el-table-column prop="eventTimeFormate" :label="AlarmsFieldsEnums['alarmTime']" align="center"> </el-table-column>
|
||||
<el-table-column prop="deviceCode" :label="AlarmsFieldsEnums['airBlowerNumber']" align="center"> </el-table-column>
|
||||
<el-table-column prop="eventText" :label="AlarmsFieldsEnums['faultDescription']" align="center"> </el-table-column>
|
||||
<el-table-column prop="devicecodeName" :label="AlarmsFieldsEnums['airBlowerNumber']" align="center"> </el-table-column>
|
||||
<el-table-column prop="eventText" :label="AlarmsFieldsEnums['faultDescription']" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-html="formatText(row.eventText)"></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="eventLevel" :label="AlarmsFieldsEnums['alarmType']" align="center">
|
||||
<template #default="scope">
|
||||
<div class="tip" v-if="scope.row.eventLevel === 0">提示</div>
|
||||
@ -199,6 +203,7 @@ const getalarmsList = () => {
|
||||
return {
|
||||
...item,
|
||||
eventTimeFormate: timestampToTime(item.eventTime),
|
||||
devicecodeName: item.deviceName + item.deviceCode,
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@ -209,6 +214,13 @@ const getalarmsList = () => {
|
||||
ElMessage.error(err?.response?.data?.msg ?? '查询失败')
|
||||
})
|
||||
}
|
||||
|
||||
const formatText = (text: any) => {
|
||||
text = text.replace(/ 动作/g, '<span style="color:#a03b1d;"> 动作</span>')
|
||||
text = text.replace(/复归/g, '<span style="color:#39baf4;">复归</span>')
|
||||
return text
|
||||
}
|
||||
|
||||
const timestampToTime = (timestamp: any) => {
|
||||
timestamp = timestamp ? timestamp : null
|
||||
let date = new Date(timestamp)
|
||||
|
@ -96,7 +96,7 @@
|
||||
@mouseout="hideButton"
|
||||
>
|
||||
<span>{{item.eventTimeFormate}}</span>
|
||||
<span>{{item.deviceCode}}</span>
|
||||
<span>{{item.deviceName}}</span>
|
||||
<span>{{item.eventText}}</span>
|
||||
<span v-show="showConfirmButton" >
|
||||
<a style="color: #0277b3; cursor: pointer;" @click="open(item)" >确认</a>
|
||||
@ -346,8 +346,7 @@ const getTableData = (deviceCode) => {
|
||||
startTime: new Date(new Date().toLocaleDateString()).getTime(),
|
||||
endTime: Date.now(),
|
||||
deviceCode: deviceCode,
|
||||
limit: 100,
|
||||
eventLevel:2
|
||||
// eventLevel:2
|
||||
}
|
||||
// console.log(JSON.stringify(data))
|
||||
getAlarmListReq(data).then((res) => {
|
||||
|
@ -537,17 +537,17 @@ const getTableData = () => {
|
||||
multipleSelection.value.forEach(item => {
|
||||
if (item.attributeCode) {
|
||||
const attributeCodeLower = item.attributeCode.toLowerCase();
|
||||
// if(attributeCodeLower==='ipitchangle1'||attributeCodeLower==='ipitchangle2'||attributeCodeLower==='ipitchangle3'){
|
||||
/* tableColumnEnds.value.push({
|
||||
label: '变桨角度',
|
||||
unit:'',
|
||||
prop: 'ipitchangle',
|
||||
if(item.unit==""||item.unit==undefined){
|
||||
tableColumnEnds.value.push({
|
||||
label: item.attributeName,
|
||||
unit:item.unit,
|
||||
prop: attributeCodeLower,
|
||||
align: 'center',
|
||||
custom: 'header',
|
||||
name: 'ipitchangle',
|
||||
title: '变桨角度',
|
||||
});*/
|
||||
//}else{
|
||||
name: item.attributeCode,
|
||||
title: item.attributeName,
|
||||
});
|
||||
}else{
|
||||
tableColumnEnds.value.push({
|
||||
label: item.attributeName+'\n'+item.unit,
|
||||
unit:item.unit,
|
||||
@ -557,7 +557,8 @@ const getTableData = () => {
|
||||
name: item.attributeCode,
|
||||
title: item.attributeName,
|
||||
});
|
||||
//}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
tableColumn.value = [...tableItem0, ...tableColumnEnds.value];
|
||||
|
@ -1,7 +1,17 @@
|
||||
<template>
|
||||
<div class="statAnalysis">
|
||||
<el-menu :default-active="activeIndex" class="headerList" mode="horizontal" @select="handleSelect">
|
||||
<el-menu-item v-for="(item, index) in headerList" :index="index" :key="index"> {{ item }} </el-menu-item>
|
||||
<el-menu-item index="0" key="0"> {{ headerList[0] }} </el-menu-item>
|
||||
<el-popover placement="top-start" :width="200" popper-class="admin-info-box" trigger="hover" content="同一测点,不同时间段对比">
|
||||
<template #reference>
|
||||
<el-menu-item index="1" key="1"> {{ headerList[1] }} </el-menu-item>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-popover placement="top-start" :width="200" popper-class="admin-info-box" trigger="hover" content="同一时间段,不同测点对比">
|
||||
<template #reference>
|
||||
<el-menu-item index="2" key="2"> {{ headerList[2] }} </el-menu-item>
|
||||
</template>
|
||||
</el-popover>
|
||||
</el-menu>
|
||||
<PowerCurveAnalysis v-if="activeIndex == 0"></PowerCurveAnalysis>
|
||||
<TrendAnalysis v-if="activeIndex == 1"></TrendAnalysis>
|
||||
@ -19,7 +29,6 @@ const config = useConfig()
|
||||
const activeIndex = ref(0)
|
||||
const { t } = useI18n()
|
||||
const headerList = [t('statAnalysis.PowerCurveAnalysis'), t('statAnalysis.trendAnalysis'), t('statAnalysis.trendComparison')]
|
||||
|
||||
const handleSelect = (index: number) => {
|
||||
activeIndex.value = index
|
||||
}
|
||||
|
@ -54,7 +54,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="chartContainer" style="width: 100%; height: 400px; border: 1px solid rgb(217, 217, 217)"></div>
|
||||
<div ref="chartContainer" style="position: absolute; bottom: 0px; width: 100%; height: 400px; border: 1px solid rgb(217, 217, 217)"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
@ -199,7 +199,7 @@ const shortcuts = [
|
||||
text: '今天',
|
||||
value: () => {
|
||||
const start = getFormattedDate(0) + ' 00:00:00'
|
||||
const end = new Date()
|
||||
const end = getFormattedDate(0) + ' 23:59:59'
|
||||
return [start, end]
|
||||
},
|
||||
},
|
||||
|
@ -34,7 +34,7 @@
|
||||
<el-button style="color: #0064aa" @click="statAnalysisExport()">{{ t('statAnalysis.export') }}</el-button>
|
||||
</div>
|
||||
</el-header>
|
||||
<div class="timeColumns" :class="{ expand: isExpand }">
|
||||
<div class="timeColumns">
|
||||
<div class="headerPart" v-for="(time, index) in times" :key="index">
|
||||
<div class="topLeft">
|
||||
<div class="selectPart">
|
||||
@ -75,10 +75,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="times.length > 2" class="ralIcon" @click="handleClick">
|
||||
<el-icon :size="20" color="#0064AA"><DArrowRight /></el-icon>
|
||||
</div>
|
||||
<div ref="chartContainer" style="width: 100%; height: 400px; border: 1px solid rgb(217, 217, 217)"></div>
|
||||
<div ref="chartContainer" style="position: absolute; bottom: 0px; width: 100%; height: 400px; border: 1px solid rgb(217, 217, 217)"></div>
|
||||
<el-dialog v-model="showMeasure" title="测点名称" :width="800">
|
||||
<template #header>
|
||||
<div class="measureSlotHeader">
|
||||
@ -131,10 +128,6 @@ const timechange = (value: any) => {
|
||||
ElMessage.warning('查询时间点错误,请重新输入')
|
||||
}
|
||||
}
|
||||
const isExpand = ref(false)
|
||||
const handleClick = () => {
|
||||
isExpand.value = !isExpand.value
|
||||
}
|
||||
|
||||
const iotModelId = ref('')
|
||||
const irn = ref('')
|
||||
@ -258,7 +251,7 @@ const shortcuts = [
|
||||
text: '今天',
|
||||
value: () => {
|
||||
const start = getFormattedDate(0) + ' 00:00:00'
|
||||
const end = new Date()
|
||||
const end = getFormattedDate(0) + ' 23:59:59'
|
||||
return [start, end]
|
||||
},
|
||||
},
|
||||
@ -396,14 +389,27 @@ const historyDataReq = (promises: any) => {
|
||||
const deviceId = statAnalysisSelect.deviceId
|
||||
const attributeCode = statAnalysisSelect.attributeCode
|
||||
results.forEach((res: any, index: number) => {
|
||||
console.log(times[index])
|
||||
const resData = (res && deviceId in res && res[deviceId][attributeCode]) || undefined
|
||||
const xData = resData['times']
|
||||
const yData = resData['values']
|
||||
if (!yData.length) {
|
||||
|
||||
if (!resData['values'].length) {
|
||||
ElMessage.info(`${customName[index]}数据为空`)
|
||||
return
|
||||
}
|
||||
calculate.value[index] = calculateStats(yData)
|
||||
const alltimes = getTimestamps(
|
||||
new Date(times[index][0]).getTime(),
|
||||
new Date(times[index][1]).getTime(),
|
||||
statAnalysisSelect.interval || '5m'
|
||||
)
|
||||
console.log(alltimes)
|
||||
console.log('🚀 ~ results.forEach ~ resData:', resData)
|
||||
const fillData = fillMissingData(alltimes, resData)
|
||||
console.log('🚀 ~ results.forEach ~ fillData:', fillData)
|
||||
|
||||
const xData = fillData['times']
|
||||
const yData = fillData['values']
|
||||
console.log(xData)
|
||||
calculate.value[index] = calculateStats(resData['values'])
|
||||
xDatas.push({
|
||||
series: String(customName[index]),
|
||||
data: xData,
|
||||
@ -484,6 +490,52 @@ function calculateStats(numbers: any) {
|
||||
average: average.toFixed(2),
|
||||
}
|
||||
}
|
||||
|
||||
const getTimestamps = (start: any, end: any, interval: any) => {
|
||||
let timestamps = []
|
||||
let current = start
|
||||
|
||||
while (current < end) {
|
||||
timestamps.push(current)
|
||||
switch (interval) {
|
||||
case '5m':
|
||||
current += 5 * 60 * 1000
|
||||
break
|
||||
case '1d':
|
||||
current += 24 * 60 * 60 * 1000
|
||||
break
|
||||
case '15m':
|
||||
current += 15 * 60 * 1000
|
||||
break
|
||||
case 'NONE':
|
||||
current += 40 * 1000
|
||||
break
|
||||
case '1h':
|
||||
current += 60 * 60 * 1000
|
||||
break
|
||||
default:
|
||||
throw new Error('Unsupported interval')
|
||||
}
|
||||
}
|
||||
|
||||
return timestamps
|
||||
}
|
||||
|
||||
const fillMissingData = (intervals: any, data: any) => {
|
||||
const { times, values } = data
|
||||
const filledTimes: any = []
|
||||
const filledValues: any = []
|
||||
intervals.forEach((time: any) => {
|
||||
const index = times.indexOf(time)
|
||||
filledTimes.push(time)
|
||||
if (index !== -1) {
|
||||
filledValues.push(values[index])
|
||||
} else {
|
||||
filledValues.push('')
|
||||
}
|
||||
})
|
||||
return { times: filledTimes, values: filledValues }
|
||||
}
|
||||
const timestampToTime = (timestamp: any) => {
|
||||
timestamp = timestamp ? timestamp : null
|
||||
let date = new Date(timestamp)
|
||||
@ -498,6 +550,9 @@ const timestampToTime = (timestamp: any) => {
|
||||
<style scoped lang="scss">
|
||||
.statAnalysis {
|
||||
height: 100%;
|
||||
.contain {
|
||||
height: calc(100% - 60px);
|
||||
}
|
||||
.headerPart {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
@ -572,24 +627,11 @@ const timestampToTime = (timestamp: any) => {
|
||||
}
|
||||
}
|
||||
.timeColumns {
|
||||
max-height: 120px;
|
||||
overflow: hidden;
|
||||
height: calc(100% - 480px);
|
||||
overflow-y: auto;
|
||||
.headerPart {
|
||||
padding: 10px 20px;
|
||||
padding: 2px 20px;
|
||||
}
|
||||
&.expand {
|
||||
max-height: max-content;
|
||||
height: auto;
|
||||
overflow: inherit;
|
||||
}
|
||||
}
|
||||
.timeColumns.expand + div.ralIcon {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
.ralIcon {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
#myEChart {
|
||||
width: 100%;
|
||||
|
@ -26,7 +26,7 @@
|
||||
<el-button style="color: #0064aa" @click="statAnalysisExport()">{{ t('statAnalysis.export') }}</el-button>
|
||||
</div>
|
||||
</el-header>
|
||||
<div class="timeColumns" :class="{ expand: isExpand }">
|
||||
<div class="timeColumns">
|
||||
<div class="headerPart" v-for="(deviceId, index) in statAnalysisDeviceId" :key="index">
|
||||
<div class="topLeft">
|
||||
<div class="selectPart">
|
||||
@ -81,10 +81,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="statAnalysisDeviceId.length > 2" class="ralIcon" @click="handleClick">
|
||||
<el-icon :size="20" color="#0064AA"><DArrowRight /></el-icon>
|
||||
</div>
|
||||
<div ref="chartContainer" style="width: 100%; height: 400px; border: 1px solid rgb(217, 217, 217)"></div>
|
||||
<div ref="chartContainer" style="position: absolute; bottom: 0px; width: 100%; height: 400px; border: 1px solid rgb(217, 217, 217)"></div>
|
||||
<el-dialog v-model="showMeasure" title="测点名称" :width="800">
|
||||
<template #header>
|
||||
<div class="measureSlotHeader">
|
||||
@ -141,10 +138,6 @@ const switchDevice = (index: number) => {
|
||||
calculate.value.splice(index, 1)
|
||||
}
|
||||
|
||||
const isExpand = ref(false)
|
||||
const handleClick = () => {
|
||||
isExpand.value = !isExpand.value
|
||||
}
|
||||
const iotModelId = ref('')
|
||||
const irn = ref('')
|
||||
const selectAtteibutes = (index: number) => {
|
||||
@ -260,7 +253,7 @@ const shortcuts = [
|
||||
text: '今天',
|
||||
value: () => {
|
||||
const start = getFormattedDate(0) + ' 00:00:00'
|
||||
const end = new Date()
|
||||
const end = getFormattedDate(0) + ' 23:59:59'
|
||||
return [start, end]
|
||||
},
|
||||
},
|
||||
@ -457,6 +450,9 @@ const timestampToTime = (timestamp: any) => {
|
||||
<style scoped lang="scss">
|
||||
.statAnalysis {
|
||||
height: 100%;
|
||||
.contain {
|
||||
height: calc(100% - 60px);
|
||||
}
|
||||
.headerPart {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
@ -531,24 +527,11 @@ const timestampToTime = (timestamp: any) => {
|
||||
}
|
||||
}
|
||||
.timeColumns {
|
||||
max-height: 120px;
|
||||
overflow: hidden;
|
||||
height: calc(100% - 480px);
|
||||
overflow-y: auto;
|
||||
.headerPart {
|
||||
padding: 10px 20px;
|
||||
padding: 2px 20px;
|
||||
}
|
||||
&.expand {
|
||||
max-height: max-content;
|
||||
height: auto;
|
||||
overflow: inherit;
|
||||
}
|
||||
}
|
||||
.timeColumns.expand + div.ralIcon {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
.ralIcon {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
#myEChart {
|
||||
width: 100%;
|
||||
|
Loading…
Reference in New Issue
Block a user