diff --git a/das-dn/cmg/changemaster.h b/das-dn/cmg/changemaster.h index b651e116..7d0bea86 100644 --- a/das-dn/cmg/changemaster.h +++ b/das-dn/cmg/changemaster.h @@ -8,6 +8,7 @@ #define MASTER_TCI_ALIVE 0x55 #define MASTER_TCI_SEND_INTERVAL (1000*200) //sleep(1) +//#define MASTER_TCI_SEND_INTERVAL (1000*10) //sleep(1) #define TCI_CHANGE_FULL_CRITICAL 5 #define NORMAL_MACHINE 0 diff --git a/das-dn/cmg/ry.cpp b/das-dn/cmg/ry.cpp index 4e471b60..49cb0e2b 100644 --- a/das-dn/cmg/ry.cpp +++ b/das-dn/cmg/ry.cpp @@ -2911,6 +2911,7 @@ bool CRYDevice::publishAnalogData(int uid) datatime *= 1000; root["dataTime"] = datatime; root["deviceId"] = static_units[uid].deviceId; +// root["isStore"] = config.units[uid].state & 0x20 ? false : true; if (values.size()) { root["values"] = values; @@ -3216,20 +3217,14 @@ bool CRYDevice::ry_run(void) sec_changed = TRUE; } if (sec_changed) { - if ((last_sec % 20) == 0) { + if ((last_sec % 5) == 0) { heart_beat(m_status); } } publishdeviceEventData(); -// int i = 0; -#if 1 + for (int i = 0; i < UNIT_NUM; i++) { -#else - i = m_nCurUnit; - m_nCurUnit++; - if (m_nCurUnit >= UNIT_NUM) m_nCurUnit = 0; -#endif if ((config.units[i].state & 0x01) != TRUE) continue; if (config.units[i].type != MASTER_UNIT) continue; MakeYKFrame(i); @@ -3238,9 +3233,9 @@ bool CRYDevice::ry_run(void) publishAnalogData(i); publishStateData(i); } -#if 1 + usleep(300); } -#endif + return m_dataAcquisitionReload; } diff --git a/das-dn/comm/process.cpp b/das-dn/comm/process.cpp index cf304b37..994baf0b 100644 --- a/das-dn/comm/process.cpp +++ b/das-dn/comm/process.cpp @@ -1741,7 +1741,14 @@ void CProcess::Destroy(void) { m_bRunFlag = FALSE; - vLog(LOG_DEBUG, "waiting for protocol thread %d destroy.\n", m_nProcess); + char name[16]; + memset(name, '\0', sizeof(name)); + if (pthread_getname_np(m_pid, name, sizeof(name)) != 0) + { + memset(name, '\0', sizeof(name)); + } + + vLog(LOG_DEBUG, "waiting for protocol thread [%d,%s] destroy.\n", m_nProcess, (name[0] != '\0') ? name : "unknow"); pthread_join(m_pid, NULL); int i = 0; @@ -1750,7 +1757,7 @@ void CProcess::Destroy(void) DestroyItem(i, FALSE); } - vLog(LOG_DEBUG, "current protocol thread [%d] is destroyed.\n", m_nProcess); + vLog(LOG_DEBUG, "current protocol thread [%d,%s] is destroyed.\n", m_nProcess, (name[0] != '\0') ? name : "unknow"); } BOOLEAN CProcess::Run(void) diff --git a/das-dn/comm/public.cpp b/das-dn/comm/public.cpp index 166e494b..bebc20c5 100644 --- a/das-dn/comm/public.cpp +++ b/das-dn/comm/public.cpp @@ -2468,8 +2468,15 @@ BOOLEAN destroy_thread(void) { if (p_id[i] != 0) { - vLog(LOG_DEBUG, "waiting for system thread [%d] destroy.\n", i); + char name[16]; + memset(name, '\0', sizeof(name)); + if (pthread_getname_np(p_id[i], name, sizeof(name)) != 0) + { + memset(name, '\0', sizeof(name)); + } + vLog(LOG_DEBUG, "waiting for system thread [%d,%s] destroy.\n", i, (name[0] != '\0') ? name : "unknow"); pthread_join(p_id[i], NULL); + vLog(LOG_DEBUG, "system protocol thread [%d,%s] is destroyed.\n", i, (name[0] != '\0') ? name : "unknow"); } } diff --git a/das-dn/hostadsbf/hostadsbf.cpp b/das-dn/hostadsbf/hostadsbf.cpp index 1a0de13e..ea54c3e8 100644 --- a/das-dn/hostadsbf/hostadsbf.cpp +++ b/das-dn/hostadsbf/hostadsbf.cpp @@ -638,7 +638,7 @@ CHostADSBFProcess::CHostADSBFProcess() m_remoteNetId = "192.168.0.2.1.1"; //websocket接口 - m_pid = 0; + m_cpid = 0; //目录无效 m_iv = 1; m_currentDirNo = -1; //当前目录编号 @@ -664,6 +664,15 @@ CHostADSBFProcess::~CHostADSBFProcess() fwrite(&m_currentDirStartFileNo, sizeof(LONG), 1, pf); fclose(pf); } + char name[16]; + memset(name, '\0', sizeof(name)); + if (pthread_getname_np(m_cpid, name, sizeof(name)) != 0) + { + memset(name, '\0', sizeof(name)); + } + vLog(LOG_DEBUG, "waiting for child thread [%s] destroy.\n", (name[0] != '\0') ? name : "unknow"); + pthread_join(m_cpid, NULL); + vLog(LOG_DEBUG, "child protocol thread [%s] is destroyed.\n", (name[0] != '\0') ? name : "unknow"); } #if 0 if (m_threadRun) { @@ -844,6 +853,7 @@ BOOLEAN CHostADSBFProcess::OnPreCreate(int id) m_turbine = NULL; m_bRouteAdded = FALSE; + m_apdu_t0_begin = 10; SetLocalAmsNetId(AmsNetId(m_localNetId)); #if 0 m_threadRun = TRUE; @@ -875,7 +885,7 @@ BOOLEAN CHostADSBFProcess::OnPreCreate(int id) //启动后,创建ftp线程 - if (m_pid <= 0) { + if (m_cpid <= 0) { m_bFtpRun = TRUE; vLog(LOG_DEBUG, "ads create a ftp thread.\n"); @@ -884,13 +894,13 @@ BOOLEAN CHostADSBFProcess::OnPreCreate(int id) pthread_attr_setstacksize(&attr, MEMERY_1M); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); - if (pthread_create(&m_pid, &attr, ryftp_process, this) < 0) { + if (pthread_create(&m_cpid, &attr, ryftp_process, this) < 0) { vLog(LOG_ERROR, "ads create ryftp_process error(%d,%s).\n", errno, strerror(errno)); return TRUE; } char name[17]; snprintf(name, 16, "%s_ftp", m_pidName.c_str()); - pthread_setname_np(m_pid, name); + pthread_setname_np(m_cpid, name); pthread_attr_destroy(&attr); } @@ -918,56 +928,43 @@ BOOLEAN CHostADSBFProcess::OnTimer(void) } if (sec_changed) { - if (m_turbine == NULL) { -#if 0 - m_turbine = new AdsDevice{m_remoteIp, AmsNetId(m_remoteNetId), m_remotePort}; - const auto state = m_turbine->GetState(); - if ((uint16_t)state.ads >= ADSSTATE::ADSSTATE_MAXSTATES || (uint16_t)state.device >= ADSSTATE::ADSSTATE_MAXSTATES) + if (m_turbine == NULL) + { + if ((m_apdu_t0_begin + 20) < system32.timers) { - vLog(LOG_DEBUG, "read device state error.\n"); - long ret = AddRemoteRoute(m_remoteIp, m_localNetId, m_localIp, std::string("isoftstone"), std::string("admin"), std::string("admin")); - vLog(LOG_DEBUG, "bbb %s add route return value is: %d\n", m_pidName.c_str(), ret); - delete m_turbine; - m_turbine = NULL; + m_apdu_t0_begin = system32.timers; + //先添加一条路由 + if (m_bRouteAdded == FALSE) + { + long ret = AddRemoteRoute(m_remoteIp, AmsNetId(m_localNetId), m_localIp, std::string("isoftstone"), std::string("admin"), std::string("admin")); + vLog(LOG_DEBUG, "%s add route(%s to %s) return value is: %d\n", m_pidName.c_str(), m_localIp.c_str(), m_remoteIp.c_str(), ret); + if (ret != 0) return TRUE; + } + m_bRouteAdded = TRUE; + //添加成功 + vLog(LOG_DEBUG, "%s start to connect(%s:%d)\n", m_pidName.c_str(), m_remoteIp.c_str(), m_remotePort); + m_turbine = new AdsDevice{m_remoteIp, AmsNetId(m_remoteNetId), m_remotePort}; + const auto state = m_turbine->GetState(); + if ((uint16_t)state.ads >= ADSSTATE::ADSSTATE_MAXSTATES || (uint16_t)state.device >= ADSSTATE::ADSSTATE_MAXSTATES) + { + vLog(LOG_DEBUG, "%s read device state error.\n", m_pidName.c_str()); + delete m_turbine; + m_turbine = NULL; + } } -#else - //先添加一条路由 - if (m_bRouteAdded == FALSE) - { - long ret = AddRemoteRoute(m_remoteIp, m_localNetId, m_localIp, std::string("isoftstone"), std::string("admin"), std::string("admin")); - vLog(LOG_DEBUG, "%s add route(%s to %s) return value is: %d\n", m_localIp.c_str(), m_remoteIp.c_str(), m_pidName.c_str(), ret); - if (ret != 0) return TRUE; - } - m_bRouteAdded = TRUE; - //添加成功 - vLog(LOG_DEBUG, "%s start to connect(%s:%d)\n", m_pidName.c_str(), m_remoteIp.c_str(), m_remotePort); - m_turbine = new AdsDevice{m_remoteIp, AmsNetId(m_remoteNetId), m_remotePort}; - const auto state = m_turbine->GetState(); - if ((uint16_t)state.ads >= ADSSTATE::ADSSTATE_MAXSTATES || (uint16_t)state.device >= ADSSTATE::ADSSTATE_MAXSTATES) - { - vLog(LOG_DEBUG, "%s read device state error.\n", m_pidName.c_str()); - delete m_turbine; - m_turbine = NULL; - } -#endif return TRUE; } if (!m_turbine->IsConnected()) return TRUE; - //try { - if (m_bHaveFTP) - { //启动时读取一次,后面自己维护序号 - if ((m_currentDirNo == -1) && (m_currentFileNo == -1)) - { //当前文件和目录都为-1,程序第一次启动。需要获取ftp目录及文件ID - readFileID(); - } + m_apdu_t0_begin = system32.timers; + if (m_bHaveFTP) + { //启动时读取一次,后面自己维护序号 + if ((m_currentDirNo == -1) && (m_currentFileNo == -1)) + { //当前文件和目录都为-1,程序第一次启动。需要获取ftp目录及文件ID + readFileID(); } - readRealData(); - FeedDog(); -// } catch (const AdsException& ex) { -// vLog(LOG_ERROR, "%s AdsException message: %s, remote is: %s:%d\n", m_pidName.c_str(), ex.what(), m_remoteIp.c_str(), m_remotePort); -// } catch (const std::runtime_error& ex) { -// vLog(LOG_ERROR, "%s %s\n", m_pidName.c_str(), ex.what()); -// } + } + readRealData(); + FeedDog(); } return TRUE; } diff --git a/das-dn/hostadsbf/hostadsbf.h b/das-dn/hostadsbf/hostadsbf.h index ed16c27b..891537f4 100644 --- a/das-dn/hostadsbf/hostadsbf.h +++ b/das-dn/hostadsbf/hostadsbf.h @@ -37,7 +37,7 @@ private: struRYADSOption m_nOptions; //增加websocket连接 - pthread_t m_pid; + pthread_t m_cpid; std::string m_localIp; //本机IP地址 std::string m_localNetId; //本机IP地址 @@ -46,6 +46,7 @@ private: AdsDevice *m_turbine; BOOLEAN m_bRouteAdded; //路由是否添加成功 + DWORD m_apdu_t0_begin; int m_total_length; DWORD last_sec; diff --git a/das-dn/hostmodbustcpbf/host_modbus_tcp_bf.cpp b/das-dn/hostmodbustcpbf/host_modbus_tcp_bf.cpp index ae35dfe2..3116a3b6 100644 --- a/das-dn/hostmodbustcpbf/host_modbus_tcp_bf.cpp +++ b/das-dn/hostmodbustcpbf/host_modbus_tcp_bf.cpp @@ -756,7 +756,7 @@ CHostModbusTcpBFProcess::CHostModbusTcpBFProcess() m_nNeedSend = FALSE; //websocket接口 - m_pid = 0; + m_cpid = 0; //目录无效 m_iv = 1; @@ -784,6 +784,16 @@ CHostModbusTcpBFProcess::~CHostModbusTcpBFProcess() fwrite(&m_currentDirStartFileNo, sizeof(LONG), 1, pf); fclose(pf); } + + char name[16]; + memset(name, '\0', sizeof(name)); + if (pthread_getname_np(m_cpid, name, sizeof(name)) != 0) + { + memset(name, '\0', sizeof(name)); + } + vLog(LOG_DEBUG, "waiting for child thread [%s] destroy.\n", (name[0] != '\0') ? name : "unknow"); + pthread_join(m_cpid, NULL); + vLog(LOG_DEBUG, "child protocol thread [%s] is destroyed.\n", (name[0] != '\0') ? name : "unknow"); } } @@ -1147,7 +1157,7 @@ BOOLEAN CHostModbusTcpBFProcess::OnPreCreate(int id) } //启动后,创建ftp线程 - if (m_pid <= 0) { + if (m_cpid <= 0) { m_bFtpRun = TRUE; vLog(LOG_DEBUG, "rymodbus create a ftp thread.\n"); @@ -1156,13 +1166,13 @@ BOOLEAN CHostModbusTcpBFProcess::OnPreCreate(int id) pthread_attr_setstacksize(&attr, MEMERY_1M); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); - if (pthread_create(&m_pid, &attr, ryftp_process, this) < 0) { + if (pthread_create(&m_cpid, &attr, ryftp_process, this) < 0) { vLog(LOG_ERROR, "rymodbus create ryftp_process error(%d,%s).\n", errno, strerror(errno)); return TRUE; } char name[17]; snprintf(name, 16, "%s_ftp", m_pidName.c_str()); - pthread_setname_np(m_pid, name); + pthread_setname_np(m_cpid, name); pthread_attr_destroy(&attr); } diff --git a/das-dn/hostmodbustcpbf/host_modbus_tcp_bf.h b/das-dn/hostmodbustcpbf/host_modbus_tcp_bf.h index 9aa96834..770ca85a 100644 --- a/das-dn/hostmodbustcpbf/host_modbus_tcp_bf.h +++ b/das-dn/hostmodbustcpbf/host_modbus_tcp_bf.h @@ -55,7 +55,7 @@ private: struRYModbusOption m_nOptions; //增加websocket连接 - pthread_t m_pid; + pthread_t m_cpid; //short m_uid; time_t last_sec; diff --git a/das/src/main/java/com/das/modules/calc/functions/FunctionIsOnline.java b/das/src/main/java/com/das/modules/calc/functions/FunctionIsOnline.java new file mode 100644 index 00000000..2833e546 --- /dev/null +++ b/das/src/main/java/com/das/modules/calc/functions/FunctionIsOnline.java @@ -0,0 +1,59 @@ +package com.das.modules.calc.functions; + +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.service.DataService; +import com.googlecode.aviator.runtime.function.AbstractFunction; +import com.googlecode.aviator.runtime.type.AviatorBoolean; +import com.googlecode.aviator.runtime.type.AviatorNil; +import com.googlecode.aviator.runtime.type.AviatorObject; +import com.googlecode.aviator.runtime.type.AviatorRuntimeJavaType; +import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.units.qual.C; + +import java.util.Date; +import java.util.Map; + + +@Slf4j +public class FunctionIsOnline extends AbstractFunction { + private CacheService cacheService; + private AdminRedisTemplate redis; + public FunctionIsOnline(AdminRedisTemplate redis, CacheService cacheService) { + this.redis = redis; + this.cacheService = cacheService; + } + + @Override + public String getName() { + return "isOnline"; + } + + /** + * 根据设备Code判断设备是否在线 + * + * @param env 环境变量映射,包含调用此方法时所需的上下文信息 + * @param deviceCode 设备Code的AviatorObject表示 + * @return 如果设备在线,则返回AviatorBoolean.TRUE;否则返回AviatorBoolean.FALSE + */ + @Override + public AviatorObject call(Map env, AviatorObject deviceCode) { + //设备Code + String code = (String)deviceCode.getValue(env); + + DeviceInfoCache deviceInfoCache = cacheService.getEquipmentCache().getDeviceInfoCacheByCode(code); + if (deviceInfoCache == null) { + return AviatorBoolean.FALSE; + } + + String key = String.format("device:%d:online", deviceInfoCache.getDeviceId()); + Integer online = redis.get(key); + if (online == null || online == 0) { + return AviatorBoolean.FALSE; + } + //未找到缓存,查询时序API获取数据 + return AviatorBoolean.TRUE; + } + +} diff --git a/das/src/main/java/com/das/modules/fdr/service/MinioViewsServcie.java b/das/src/main/java/com/das/modules/fdr/service/MinioViewsServcie.java index af3372af..0a81db32 100644 --- a/das/src/main/java/com/das/modules/fdr/service/MinioViewsServcie.java +++ b/das/src/main/java/com/das/modules/fdr/service/MinioViewsServcie.java @@ -161,7 +161,7 @@ public class MinioViewsServcie { lastModifyTime = zonedDateTime.format(dateFormat); } if (parts.length > 0) { - String nodeName = parts[2]; + String nodeName = parts[1]; int type = isDir ? 0 : 1; itemName= isDir ? itemName.substring(0,itemName.lastIndexOf("/")) : itemName; FileNode node = new FileNode(nodeName, type,size,lastModifyTime,"/"+itemName); diff --git a/das/src/main/java/com/das/modules/fdr/service/impl/FaultRecorderServiceImpl.java b/das/src/main/java/com/das/modules/fdr/service/impl/FaultRecorderServiceImpl.java index 17c86d0f..024b257d 100644 --- a/das/src/main/java/com/das/modules/fdr/service/impl/FaultRecorderServiceImpl.java +++ b/das/src/main/java/com/das/modules/fdr/service/impl/FaultRecorderServiceImpl.java @@ -9,13 +9,11 @@ import com.das.common.exceptions.ServiceException; import com.das.common.utils.ExcelUtil; import com.das.common.utils.HuExcelUtils; import com.das.modules.auth.domain.vo.SysUserVo; -import com.das.modules.curve.domain.entity.CurveItemEntity; import com.das.modules.curve.domain.entity.TheoreticalPowerCurveEntity; import com.das.modules.curve.mapper.TheoreticalPowerCurveMapper; import com.das.modules.equipment.domain.excel.SheetInfoBean; import com.das.modules.equipment.entity.SysEquipment; import com.das.modules.equipment.mapper.SysEquipmentMapper; -import com.das.modules.fdr.config.MinioProperties; import com.das.modules.fdr.domain.FileNode; import com.das.modules.fdr.domain.SysFaultCodeDict; import com.das.modules.fdr.domain.SysFaultRecordingDesc; @@ -38,7 +36,6 @@ import org.springframework.web.multipart.MultipartFile; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; -import java.rmi.ServerException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.LocalDate; @@ -71,9 +68,9 @@ public class FaultRecorderServiceImpl implements FaultRecorderService { @Override public List getDirOrFileList(String fileType,String name, String startTime, String endTime) { List fileResult = new ArrayList<>(); - List monthsBetween = getMonthsBetween(startTime, endTime); - for (String item : monthsBetween) { - String directoryName = name + FileConstants.FILE_SEPARATOR + fileType + FileConstants.FILE_SEPARATOR + item.substring(0, item.indexOf("-")) + FileConstants.FILE_SEPARATOR + item.substring(item.indexOf("-") + 1); + List daysBetween = getDaysBetween(startTime, endTime); + for (String item : daysBetween) { + String directoryName = name + FileConstants.FILE_SEPARATOR + fileType + FileConstants.FILE_SEPARATOR + item; List fileTree = minioViewsServcie.getFileTree(directoryName); fileResult.addAll(fileTree); } @@ -154,18 +151,18 @@ public class FaultRecorderServiceImpl implements FaultRecorderService { } - private List getMonthsBetween(String startTime, String endTime) { + private List getDaysBetween(String startTime, String endTime) { List months = new ArrayList<>(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); - LocalDate start = LocalDate.parse(startTime + "-01", formatter); - LocalDate end = LocalDate.parse(endTime + "-01", formatter); + LocalDate start = LocalDate.parse(startTime, formatter); + LocalDate end = LocalDate.parse(endTime, formatter); - DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("yyyy-MM"); + DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd"); while (!start.isAfter(end)) { months.add(start.format(monthFormatter)); - start = start.plusMonths(1); + start = start.plusDays(1); } return months; } diff --git a/das/src/main/java/com/das/modules/node/command/HeartbeatCommand.java b/das/src/main/java/com/das/modules/node/command/HeartbeatCommand.java index f0b78e33..f4500301 100644 --- a/das/src/main/java/com/das/modules/node/command/HeartbeatCommand.java +++ b/das/src/main/java/com/das/modules/node/command/HeartbeatCommand.java @@ -2,7 +2,6 @@ package com.das.modules.node.command; import com.das.common.constant.EquipmentTypeIds; import com.das.common.utils.AdminRedisTemplate; -import com.das.common.utils.StringUtils; import com.das.modules.cache.domain.DeviceInfoCache; import com.das.modules.cache.service.CacheService; import com.das.modules.node.constant.NodeConstant; @@ -12,61 +11,70 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.text.MessageFormat; - @Slf4j @Service(value = NodeConstant.HEARTBEAT) public class HeartbeatCommand implements BaseCommand{ - public static final String HEART_BEAT = "heartBeat:{0}"; + public static final long HEARTBEAT_TTL = 12L; @Autowired AdminRedisTemplate adminRedisTemplate; @Autowired CacheService cacheService; + /** + * 执行命令方法 + * 该方法处理接收到的终端消息,特别是心跳报文中的设备和链路在线状态更新 + * + * @param data 包含心跳报文信息的TerminalMessage对象 + */ @Override public void doCommand(TerminalMessage data) { + log.info("收到[heartbeat]报文"); + + // 解析心跳报文中的数据信息 JsonNode dataInfo = data.getData(); if (!dataInfo.isEmpty()) { - JsonNode linkNode = data.getData().get("links"); - if (linkNode != null && linkNode.isArray()) { - for (JsonNode fruitNode : linkNode) { - String linkId = fruitNode.get("linkId").asText(); - String online = fruitNode.get("online").asText(); - String key = MessageFormat.format(HEART_BEAT, linkId); - if (StringUtils.isEmpty(online)) { - adminRedisTemplate.set(key, 0); - } else { - if ("true".equals(online)) { - adminRedisTemplate.set(key, 1); - } else { - adminRedisTemplate.set(key, 0); - } - } - adminRedisTemplate.expire(key, 300L); + // 处理链路信息 + JsonNode links = data.getData().get("links"); + if (links != null && links.isArray()) { + for (JsonNode linkNode : links) { + String linkId = linkNode.get("linkId").asText(); + boolean online = linkNode.get("online").asBoolean(); + String key = String.format("link:%s:online", linkId); + // 更新链路在线状态到Redis + adminRedisTemplate.set(key, online ? 1 : 0); + adminRedisTemplate.expire(key, HEARTBEAT_TTL); } } + + // 处理设备信息 JsonNode devices = data.getData().get("devices"); if (devices != null && devices.isArray()) { for (JsonNode device : devices) { - Long deviceId = device.get("deviceId").asLong(); - int online = device.get("online").asInt(); + long deviceId = device.get("deviceId").asLong(); + boolean online = device.get("online").asBoolean(); + // 获取设备缓存信息 DeviceInfoCache deviceInfoCacheById = cacheService.getEquipmentCache().getDeviceInfoCacheById(deviceId); if (deviceInfoCacheById == null || !deviceInfoCacheById.getObjectType().equals(EquipmentTypeIds.EQUIPMENT_TYPE_STATION_WTG)) { continue; } - //判断是不是风机 + // 判断是不是风机 + String keyDeviceOnline = String.format("device:%d:online", deviceId); + // 更新设备在线状态到Redis + adminRedisTemplate.set(keyDeviceOnline, online ? 1 : 0); + adminRedisTemplate.expire(keyDeviceOnline, HEARTBEAT_TTL); String keyPLCDeviceStatus = String.format("RT:%d:iturbineoperationmode", deviceId); String keyCommFaultState = String.format("RT:%d:commfaultstate",deviceId); Integer plcDeviceStatus = adminRedisTemplate.get(keyPLCDeviceStatus); log.debug("设备ID:{},在线状态:{},通讯状态: {}", deviceId, online, plcDeviceStatus); + // 根据设备在线状态和通讯状态更新通讯故障状态 if (plcDeviceStatus == null){ - adminRedisTemplate.set(keyCommFaultState, (online == 1) ? 0 : 1); + adminRedisTemplate.set(keyCommFaultState, online ? 0 : 1); } else{ - adminRedisTemplate.set(keyCommFaultState, (online == 1) && (plcDeviceStatus != 0) ? 0 : 1); + adminRedisTemplate.set(keyCommFaultState, online && (plcDeviceStatus != 0) ? 0 : 1); } } } diff --git a/das/src/main/java/com/das/modules/node/domain/dto/SysCommunicationLinkDto.java b/das/src/main/java/com/das/modules/node/domain/dto/SysCommunicationLinkDto.java index 256abdab..86fd3345 100644 --- a/das/src/main/java/com/das/modules/node/domain/dto/SysCommunicationLinkDto.java +++ b/das/src/main/java/com/das/modules/node/domain/dto/SysCommunicationLinkDto.java @@ -33,6 +33,11 @@ public class SysCommunicationLinkDto implements Serializable { */ private String params; + /** + * 结果是否包含状态 + */ + private Integer withStatus; + private Integer revision; /** diff --git a/das/src/main/java/com/das/modules/node/domain/vo/SysCommunicationLinkVo.java b/das/src/main/java/com/das/modules/node/domain/vo/SysCommunicationLinkVo.java index 71eb226d..0424daaa 100644 --- a/das/src/main/java/com/das/modules/node/domain/vo/SysCommunicationLinkVo.java +++ b/das/src/main/java/com/das/modules/node/domain/vo/SysCommunicationLinkVo.java @@ -37,6 +37,11 @@ public class SysCommunicationLinkVo { */ private String nodeName; + /** + * 状态 0 - 正常 1 - 通讯中断 + */ + private Integer status; + /** * 所属系统节点id */ diff --git a/das/src/main/java/com/das/modules/node/service/impl/NodeMessageServiceImpl.java b/das/src/main/java/com/das/modules/node/service/impl/NodeMessageServiceImpl.java index daec5399..027c16d0 100644 --- a/das/src/main/java/com/das/modules/node/service/impl/NodeMessageServiceImpl.java +++ b/das/src/main/java/com/das/modules/node/service/impl/NodeMessageServiceImpl.java @@ -238,7 +238,7 @@ public class NodeMessageServiceImpl extends TextWebSocketHandler implements Node @Override public void handleData(TerminalMessage data) { JsonNode jsonNode = data.getData(); - log.info("收到消息:{}",data.getData()); + log.debug("收到消息:{}",data.getData()); String deviceId = jsonNode.get("deviceId").asText(); JsonNode values = jsonNode.get("values"); JsonNode archiveValues = jsonNode.get("archiveValues"); @@ -473,7 +473,6 @@ public class NodeMessageServiceImpl extends TextWebSocketHandler implements Node //如果是主动请求报文,加入队列,等待处理 String nodeId = session.getAttributes().get(NodeConstant.NODE_ID).toString(); String cmd = msg.getCmd(); - JsonNode data = msg.getData(); log.debug("收到 Node:{} WS 报文: {}", nodeId, cmd); pushMessage(msg); } @@ -495,6 +494,13 @@ public class NodeMessageServiceImpl extends TextWebSocketHandler implements Node log.error(String.format("IP: %s 通讯异常, NodeId:%d, Version: %d", remoteIp, nodeId, version), exception); } + /** + * 当WebSocket连接关闭后,执行该方法。 + * + * @param session 关闭的WebSocket会话对象 + * @param status 连接关闭的状态信息 + * @throws Exception 如果在关闭连接的过程中出现异常,则抛出该异常 + */ @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { String remoteIp = (String) session.getAttributes().getOrDefault(NodeConstant.REMOTE_IP, ""); diff --git a/das/src/main/java/com/das/modules/node/service/impl/SysNodeServiceImpl.java b/das/src/main/java/com/das/modules/node/service/impl/SysNodeServiceImpl.java index 11940877..f5031917 100644 --- a/das/src/main/java/com/das/modules/node/service/impl/SysNodeServiceImpl.java +++ b/das/src/main/java/com/das/modules/node/service/impl/SysNodeServiceImpl.java @@ -2,14 +2,12 @@ package com.das.modules.node.service.impl; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.NumberUtil; import com.baomidou.mybatisplus.core.metadata.IPage; import com.das.common.config.SessionUtil; import com.das.common.constant.MeasType; import com.das.common.exceptions.ServiceException; -import com.das.common.utils.BeanCopyUtils; -import com.das.common.utils.PageDataInfo; -import com.das.common.utils.PageQuery; -import com.das.common.utils.SequenceUtils; +import com.das.common.utils.*; import com.das.modules.auth.domain.vo.SysUserVo; import com.das.modules.auth.entity.SysOrg; import com.das.modules.auth.mapper.SysOrgMapper; @@ -40,6 +38,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; @@ -81,6 +80,8 @@ public class SysNodeServiceImpl implements SysNodeService { @Autowired SysIotModelServiceMapper iotModelServiceMapper; + @Autowired + AdminRedisTemplate adminRedisTemplate; @Override public List querySysNodeList() { @@ -135,13 +136,60 @@ public class SysNodeServiceImpl implements SysNodeService { sysNodeMapper.deleteById(id); } + /** + * 查询系统通信链路列表 + * + * @param sysCommunicationLinkDto 通信链路查询DTO,包含分页信息和查询条件 + * @return 返回包含通信链路列表的PageDataInfo对象 + */ @Override public PageDataInfo querySysCommunicationLinkList(SysCommunicationLinkDto sysCommunicationLinkDto) { + // 初始化分页查询对象 PageQuery pageQuery = new PageQuery(); pageQuery.setPageNum(sysCommunicationLinkDto.getPageNum()); pageQuery.setPageSize(sysCommunicationLinkDto.getPageSize()); + + // 调用Mapper方法查询通信链路列表 IPage iPage = sysCommunicationLinkMapper.querySysCommunicationLinkList(pageQuery.build(), sysCommunicationLinkDto); - return PageDataInfo.build(iPage.getRecords(), iPage.getTotal()); + + // 获取查询结果列表 + List records = iPage.getRecords(); + + // 根据条件判断是否需要查询在线状态 + if (sysCommunicationLinkDto.getWithStatus() != null && sysCommunicationLinkDto.getWithStatus() == 1) { + // 初始化用于存储在线状态键的列表 + List keys = new ArrayList<>(records.size()); + + // 遍历查询结果,构造每个通信链路的在线状态键 + for (int i = 0; i < records.size(); i++) { + SysCommunicationLinkVo sysCommunicationLinkVo = records.get(i); + String onlineKey = String.format("link:%d:online", sysCommunicationLinkVo.getId()); + keys.add(onlineKey); + } + + // 批量获取在线状态值 + List values = adminRedisTemplate.mGet(keys); + + // 遍历在线状态值,更新通信链路的在线状态 + for (int i = 0; i < values.size(); i++) { + Object val = values.get(i); + if (val == null) { + records.get(i).setStatus(1); + } + else{ + Integer status = NumberUtil.parseInt(val.toString(), null); + if (status == null || status == 0) { + records.get(i).setStatus(1); + } + else{ + records.get(i).setStatus(0); + } + } + } + } + + // 构建并返回分页数据信息 + return PageDataInfo.build(records, iPage.getTotal()); } @Override diff --git a/das/src/main/java/com/das/modules/page/service/impl/HomeServiceImpl.java b/das/src/main/java/com/das/modules/page/service/impl/HomeServiceImpl.java index 73fffd18..755cf3d6 100644 --- a/das/src/main/java/com/das/modules/page/service/impl/HomeServiceImpl.java +++ b/das/src/main/java/com/das/modules/page/service/impl/HomeServiceImpl.java @@ -69,6 +69,7 @@ public class HomeServiceImpl implements HomeService { attributesList.add("ikwhthisday"); //是否锁定 attributesList.add("Locked"); + attributesList.add("ProcessedOperationMode"); //叶轮转速 attributesList.add("iRotorSpeed"); for (SysEquipmentVo item : sysEquipmentVos) { diff --git a/ui/dasadmin/src/views/backend/dashboard.vue b/ui/dasadmin/src/views/backend/dashboard.vue index 726d2645..de45cb83 100644 --- a/ui/dasadmin/src/views/backend/dashboard.vue +++ b/ui/dasadmin/src/views/backend/dashboard.vue @@ -341,23 +341,6 @@ const currentDayStatus = ref({ }) const deviceCode = ref([]) const FanList = ref([]) -const getRealTimeState = (data: any) => { - if (data.iturbineoperationmode) { - if (data.iturbineoperationmode > 1 && data.iturbineoperationmode < 6) { - return 2 - } - if (data.iturbineoperationmode === 21) { - return 20 - } - return data.iturbineoperationmode - } else if (data.iyplevel === 10) { - return 1110 - } else if (data.gridlostdetected === 1) { - return 1111 - } else if (data.ibplevel === 200) { - return 1112 - } -} const StatusListData = () => { getWindTurbineMatrixData().then((res) => { if (res.code == 200) { diff --git a/ui/dasadmin/src/views/backend/statAnalysis/powerCurveAnalysis.vue b/ui/dasadmin/src/views/backend/statAnalysis/powerCurveAnalysis.vue index 7f7709cb..3c2fdfe5 100644 --- a/ui/dasadmin/src/views/backend/statAnalysis/powerCurveAnalysis.vue +++ b/ui/dasadmin/src/views/backend/statAnalysis/powerCurveAnalysis.vue @@ -8,6 +8,12 @@ +
+ 风速来源 + + + +
{{ t('statAnalysis.search') }} @@ -29,17 +35,7 @@ :shortcuts="shortcuts" />
-
- 风速来源 - - - -
+
{{ t('statAnalysis.madeinfatory') }}
+
+ 显示曲线 + +
@@ -75,7 +75,8 @@ import * as echarts from 'echarts' import { getCutDecimalsValue } from '/@/views/backend/equipment/airBlower/utils' const { t } = useI18n() - +const AvgWindSpeedSwitch = ref(false) +const chartType = ref('scatter') // 默认散点图 const statAnalysisFatory = ref('') const statAnalysisFatoryList: any = ref([]) const statAnalysisSpeedSource = ref('AvgWindSpeed_10min') @@ -88,6 +89,28 @@ const statAnalysisSelectOptions: any = reactive({ deviceId: [], }) +const changeUpdateAvgWindSpeed = (val: any) => { + chartType.value = val ? 'line' : 'scatter' + updateChart() +} + +const seriesDataInit = ref([]) +const calculateData: any = ref([]) +const updateChart = () => { + const series = { + type: chartType.value, + data: chartType.value === 'scatter' ? seriesDataInit.value : calculateData.value, + name: '实际值', + symbolSize: 5, + symbol: 'circle', + } + option.series[0] = series + if (!option.legend.data.includes('实际值')) { + option.legend.data.push('实际值') + } + chart.value.setOption(option) +} + const getFormattedDate = (offset: number) => { const date = new Date() date.setDate(date.getDate() + offset) @@ -330,19 +353,12 @@ const statAnalysisOperate = () => { const seriesData = iGenPower.map((item: any, index: number) => { return [getCutDecimalsValue(iWindSpeed[index], 2), getCutDecimalsValue(item, 2)] }) - seriesData.sort((a: any, b: any) => { - return a[0] - b[0] - }) - - const series = { - type: 'scatter', - data: seriesData, - name: '实际值', - symbolSize: 5, - symbol: 'circle', - } - option.series.push(series) - option.legend.data.push('实际值') + // seriesData.sort((a: any, b: any) => { + // return a[0] - b[0] + // }) + seriesDataInit.value = seriesData + calculateData.value = calculateAverages(seriesDataInit.value) + updateChart() } } if (resData1.length) { @@ -360,9 +376,11 @@ const statAnalysisOperate = () => { option.series.push(series) option.legend.data.push('理论值') } + console.log('🚀 ~ .then ~ option.legend.data:', option.legend.data) chart.value.setOption(option) }) .catch((error) => { + console.log(error) isLoading.value = false ElMessage.warning(error) }) @@ -393,6 +411,34 @@ const statAnalysisExport = () => { document.body.removeChild(a) }) } + +const calculateAverages = (data: any) => { + let maxWindSpeed = Math.max(...data.map((item: any) => item[0])) + let interval = 5 // 每5m/s一个区间 + let result = [] + + for (let windSpeed = 0; windSpeed <= maxWindSpeed; windSpeed += interval) { + let sumPower = 0 + let count = 0 + + for (let i = 0; i < data.length; i++) { + let currentWindSpeed = data[i][0] + let currentPower = data[i][1] + + if (currentWindSpeed >= windSpeed && currentWindSpeed < windSpeed + interval) { + sumPower += currentPower + count++ + } + } + + if (count > 0) { + let averagePower = sumPower / count + result.push([windSpeed + interval, averagePower]) + } + } + + return result +}