This commit is contained in:
高云鹏 2024-12-12 10:23:56 +08:00
commit c07681e080
19 changed files with 329 additions and 153 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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");
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -55,7 +55,7 @@ private:
struRYModbusOption m_nOptions;
//增加websocket连接
pthread_t m_pid;
pthread_t m_cpid;
//short m_uid;
time_t last_sec;

View File

@ -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<String, Object> 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;
}
}

View File

@ -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);

View File

@ -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<FileNode> getDirOrFileList(String fileType,String name, String startTime, String endTime) {
List<FileNode> fileResult = new ArrayList<>();
List<String> 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<String> daysBetween = getDaysBetween(startTime, endTime);
for (String item : daysBetween) {
String directoryName = name + FileConstants.FILE_SEPARATOR + fileType + FileConstants.FILE_SEPARATOR + item;
List<FileNode> fileTree = minioViewsServcie.getFileTree(directoryName);
fileResult.addAll(fileTree);
}
@ -154,18 +151,18 @@ public class FaultRecorderServiceImpl implements FaultRecorderService {
}
private List<String> getMonthsBetween(String startTime, String endTime) {
private List<String> getDaysBetween(String startTime, String endTime) {
List<String> 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;
}

View File

@ -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);
}
}
}

View File

@ -33,6 +33,11 @@ public class SysCommunicationLinkDto implements Serializable {
*/
private String params;
/**
* 结果是否包含状态
*/
private Integer withStatus;
private Integer revision;
/**

View File

@ -37,6 +37,11 @@ public class SysCommunicationLinkVo {
*/
private String nodeName;
/**
* 状态 0 - 正常 1 - 通讯中断
*/
private Integer status;
/**
* 所属系统节点id
*/

View File

@ -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, "");

View File

@ -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<SysNodeVo> querySysNodeList() {
@ -135,13 +136,60 @@ public class SysNodeServiceImpl implements SysNodeService {
sysNodeMapper.deleteById(id);
}
/**
* 查询系统通信链路列表
*
* @param sysCommunicationLinkDto 通信链路查询DTO包含分页信息和查询条件
* @return 返回包含通信链路列表的PageDataInfo对象
*/
@Override
public PageDataInfo<SysCommunicationLinkVo> querySysCommunicationLinkList(SysCommunicationLinkDto sysCommunicationLinkDto) {
// 初始化分页查询对象
PageQuery pageQuery = new PageQuery();
pageQuery.setPageNum(sysCommunicationLinkDto.getPageNum());
pageQuery.setPageSize(sysCommunicationLinkDto.getPageSize());
// 调用Mapper方法查询通信链路列表
IPage<SysCommunicationLinkVo> iPage = sysCommunicationLinkMapper.querySysCommunicationLinkList(pageQuery.build(), sysCommunicationLinkDto);
return PageDataInfo.build(iPage.getRecords(), iPage.getTotal());
// 获取查询结果列表
List<SysCommunicationLinkVo> records = iPage.getRecords();
// 根据条件判断是否需要查询在线状态
if (sysCommunicationLinkDto.getWithStatus() != null && sysCommunicationLinkDto.getWithStatus() == 1) {
// 初始化用于存储在线状态键的列表
List<String> 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<Object> 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

View File

@ -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) {

View File

@ -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) {

View File

@ -8,6 +8,12 @@
<el-option v-for="v in statAnalysisSelectOptions.deviceId" :key="v.value" :label="v.label" :value="v.value"></el-option>
</el-select>
</div>
<div class="selectPart">
<span>风速来源</span>
<el-select v-model="statAnalysisSpeedSource" placeholder="请选择风速来源" class="statAnalysisSelect">
<el-option v-for="v in statAnalysisSelectOptions.speedSource" :key="v.value" :label="v.label" :value="v.value"></el-option>
</el-select>
</div>
</div>
<div class="topRight">
<el-button type="primary" :loading="isLoading" @click="statAnalysisOperate()">{{ t('statAnalysis.search') }}</el-button>
@ -29,17 +35,7 @@
:shortcuts="shortcuts"
/>
</div>
<div class="selectPart">
<span>风速来源</span>
<el-select v-model="statAnalysisSpeedSource" placeholder="请选择风速来源" class="statAnalysisSelect">
<el-option
v-for="v in statAnalysisSelectOptions.speedSource"
:key="v.value"
:label="v.label"
:value="v.value"
></el-option>
</el-select>
</div>
<div class="selectPart">
<span>{{ t('statAnalysis.madeinfatory') }}</span>
<el-select
@ -56,6 +52,10 @@
></el-option>
</el-select>
</div>
<div class="selectPart">
<span> 显示曲线 </span>
<el-switch v-model="AvgWindSpeedSwitch" @change="changeUpdateAvgWindSpeed"></el-switch>
</div>
</div>
</div>
</div>
@ -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
}
</script>
<style scoped lang="scss">
.contain {