From 541a70757063e823215ac24f5c9fecbbfa330d29 Mon Sep 17 00:00:00 2001 From: hongawen <83944980@qq.com> Date: Tue, 12 Aug 2025 10:26:55 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handler/SocketSourceResponseService.java | 415 +++++++++++++----- .../detection/util/socket/CnSocketUtil.java | 6 +- 2 files changed, 314 insertions(+), 107 deletions(-) diff --git a/detection/src/main/java/com/njcn/gather/detection/handler/SocketSourceResponseService.java b/detection/src/main/java/com/njcn/gather/detection/handler/SocketSourceResponseService.java index d4b33f14..52842553 100644 --- a/detection/src/main/java/com/njcn/gather/detection/handler/SocketSourceResponseService.java +++ b/detection/src/main/java/com/njcn/gather/detection/handler/SocketSourceResponseService.java @@ -11,130 +11,260 @@ import com.njcn.gather.detection.pojo.vo.SocketDataMsg; import com.njcn.gather.detection.pojo.vo.SocketMsg; import com.njcn.gather.detection.pojo.vo.WebSocketVO; import com.njcn.gather.detection.util.socket.*; -import com.njcn.gather.detection.util.socket.cilent.NettyClient; -import com.njcn.gather.detection.util.socket.cilent.NettyDevClientHandler; import com.njcn.gather.detection.util.socket.websocket.WebServiceManager; import com.njcn.gather.device.pojo.vo.PreDetection; import com.njcn.gather.device.service.IPqDevService; import com.njcn.gather.script.pojo.po.SourceIssue; import com.njcn.gather.system.pojo.enums.DicDataEnum; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.*; import java.util.stream.Collectors; +/** + * 程控源Socket响应处理服务 + *
+ * 该服务类负责处理来自程控源设备的Socket消息响应,包括: + * - 源初始化响应处理 + * - 检测流程控制和状态管理 + * - 相序检测、系数校验等特定检测类型的响应处理 + * - WebSocket消息推送给前端用户 + * - 错误处理和连接管理 + *
+ *+ * 主要处理的操作类型: + * - YJC_YTXJY: 源通信校验/源初始化 + * - YJC_XUJY: 相序检测 + * - FORMAL_REAL: 正式检测 + * - Coefficient_Check: 系数校验 + * - QUITE_SOURCE: 退出源连接 + *
+ * + * @author CN_Gather Detection Team + * @version 1.0 + * @since 2023 + */ +@Slf4j @Service @RequiredArgsConstructor public class SocketSourceResponseService { /** - * 向webSocket客户端发送消息 + * 设备信息服务,提供设备基础信息查询功能 */ - private final SocketDevResponseService socketDevResponseService; private final IPqDevService iPqDevService; + + /** + * Socket连接管理器,负责管理设备和源的Socket连接 + */ private final SocketManager socketManager; + + /** + * 发送WebSocket消息到指定用户页面 + *+ * 将数据对象转换为JSON字符串并通过WebSocket推送给前端用户, + * 用于实时通知用户检测进度、状态变化或错误信息。 + *
+ * + * @param userPageId 用户页面ID,用于标识消息接收方 + * @param data 要发送的数据对象,将被转换为JSON格式 + */ + private void sendWebSocketMessage(String userPageId, Object data) { + WebServiceManager.sendMsg(userPageId, JSON.toJSONString(data)); + } + + /** + * 发送错误消息并退出源连接 + *+ * 当检测过程中发生错误时,执行以下操作: + * 1. 主动断开与程控源的连接 + * 2. 构造包含错误信息的Socket消息 + * 3. 通过WebSocket将错误信息推送给前端用户 + *
+ * + * @param param 检测参数,包含用户页面ID等信息 + * @param socketDataMsg 原始Socket消息,用于构造响应消息 + * @param errorMessage 具体的错误描述信息 + */ + private void sendErrorAndQuit(PreDetectionParam param, SocketDataMsg socketDataMsg, String errorMessage) { + CnSocketUtil.quitSendSource(param); + SocketMsg+ * 重载方法,使用预定义的错误码枚举来获取标准化的错误消息。 + * 确保错误信息的一致性和规范性。 + *
+ * + * @param param 检测参数 + * @param socketDataMsg 原始Socket消息 + * @param errorCode 错误码枚举,包含标准化的错误描述 + */ + private void sendErrorAndQuit(PreDetectionParam param, SocketDataMsg socketDataMsg, SourceResponseCodeEnum errorCode) { + sendErrorAndQuit(param, socketDataMsg, errorCode.getMessage()); + } + /** + * 当前检测会话中的设备列表 + *+ * 存储正在进行检测的设备信息,包含设备基本信息和监测点配置。 + * 注意:该字段存在线程安全问题,建议后续重构为线程安全的设计。 + *
+ */ private List+ * 从设备列表中提取的所有监测点ID集合,用于向设备发送数据请求时指定监测范围。 + * 与devList字段保持同步更新。 + *
+ */ private List+ * 根据消息中的操作码,分发到相应的处理方法: + * - 解析Socket消息,提取操作码 + * - 根据操作码类型调用对应的处理方法 + * - 支持检测计划模式和模拟测试模式的区分处理 + *
+ *+ * 支持的操作类型: + * - YJC_YTXJY: 源通信校验/源初始化 + * - YJC_XUJY: 相序检测 + * - FORMAL_REAL: 正式检测 + * - Coefficient_Check: 系数校验 + * - QUITE_SOURCE: 退出源连接 + *
+ * + * @param param 检测参数,包含用户ID、设备ID、计划ID等关键信息 + * @param msg 从程控源接收的原始Socket消息 + * @throws Exception 当消息解析失败或处理过程中发生异常时抛出 + */ public void deal(PreDetectionParam param, String msg) throws Exception { + // 解析接收到的Socket消息 SocketDataMsg socketDataMsg = MsgUtil.socketDataMsg(msg); + + // 从requestId中提取操作码,requestId格式为:操作码_步骤标识 String[] tem = socketDataMsg.getRequestId().split(CnSocketUtil.STEP_TAG); SourceOperateCodeEnum enumByCode = SourceOperateCodeEnum.getDictDataEnumByCode(tem[0]); + if (ObjectUtil.isNotNull(enumByCode)) { switch (enumByCode) { - //源初始化 + // 源初始化处理:根据是否有计划ID判断是正式检测还是模拟检测 case YJC_YTXJY: if (ObjectUtil.isNotNull(param.getPlanId())) { + // 有计划ID:正式检测模式,源初始化成功后启动设备检测 detectionDev(param, socketDataMsg); } else { - // 程控源-源通信校验 + // 无计划ID:模拟检测模式,仅进行源通信校验 handleYtxjySimulate(param, socketDataMsg); } break; - //相序检测 + + // 相序检测:检测设备的相序是否正确 case YJC_XUJY: phaseSequenceDev(param, socketDataMsg); break; - //正式检测 + + // 正式检测:根据是否有计划ID选择不同的处理方式 case FORMAL_REAL: if (ObjectUtil.isNotNull(param.getPlanId())) { + // 有计划ID:向设备发送检测参数 senParamToDev(param, socketDataMsg); } else { + // 无计划ID:模拟测试模式 handleSimulateTest(param, socketDataMsg); } break; - //系数校验 + + // 系数校验:验证设备的计量系数是否准确 case Coefficient_Check: coefficient(param, socketDataMsg); break; + + // 退出源连接:清理资源并关闭连接 case QUITE_SOURCE: quitDeal(socketDataMsg, param); break; + + // YXT操作:暂未实现具体功能 case YXT: + // TODO: 实现YXT操作的具体逻辑 break; + default: - // todo... 要日志记录或者websocket送到前端友好提示用户 + // TODO: 记录未知操作码到日志,并向前端发送友好提示 break; } } else { - // todo... 要日志记录或者websocket送到前端友好提示用户 - System.out.println("fggggggggggggggggggggg" + enumByCode); + // TODO: 向前端发送错误提示 + log.error("程控源响应消息操作码解析失败,原始消息: {}, 解析结果: {}", msg, enumByCode); } } + /** + * 处理模拟检测中的源通信校验响应 + *+ * 在模拟检测模式下(非计划检测),处理程控源的通信校验响应: + * - 成功时:根据参数决定是否向前端发送WebSocket消息 + * - 业务未处理:直接转发消息给前端 + * - 各种错误情况:统一处理为退出源连接并通知前端 + *
+ *+ * 支持的错误类型包括: + * - 源连接错误、程控源错误、测试项解析错误 + * - 源控制错误、目标源错误、未初始化错误 + * - 未知错误、无法响应错误 + *
+ * + * @param param 检测参数,包含用户信息和WebSocket消息发送控制 + * @param socketDataMsg 程控源返回的响应消息 + */ private void handleYtxjySimulate(PreDetectionParam param, SocketDataMsg socketDataMsg) { SourceResponseCodeEnum dictDataEnumByCode = SourceResponseCodeEnum.getDictDataEnumByCode(socketDataMsg.getCode()); if (ObjectUtil.isNotNull(dictDataEnumByCode)) { switch (dictDataEnumByCode) { case SUCCESS: + // 源初始化成功:根据参数控制是否发送WebSocket消息 if (param.getSendWebMsg()) { - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); + sendWebSocketMessage(param.getUserPageId(), socketDataMsg); } - System.out.println(param.getSendWebMsg() + "模拟检测-源初始化成功"); + log.info("模拟检测源初始化成功,用户: {}, WebSocket发送: {}", + param.getUserPageId(), param.getSendWebMsg()); break; case UNPROCESSED_BUSINESS: - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); + // 业务暂未处理:直接转发消息给前端等待处理 + sendWebSocketMessage(param.getUserPageId(), socketDataMsg); break; + // 各种错误情况:源连接错误、程控源控制错误、测试项解析错误等 case SOURCE_CONNECTION_ERROR: - CnSocketUtil.quitSendSource(param); - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); - break; case CONTROLLED_SOURCE_ERROR: - CnSocketUtil.quitSendSource(param); - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); - break; case TEST_ITEM_PARSING_ERROR: - CnSocketUtil.quitSendSource(param); - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); - break; case SOURCE_CONTROL_ERROR: - CnSocketUtil.quitSendSource(param); - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); - break; case TARGET_SOURCE_ERROR: - CnSocketUtil.quitSendSource(param); - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); - break; case NOT_INITIALIZED: - CnSocketUtil.quitSendSource(param); - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); - break; case UNKNOWN_ERROR: - CnSocketUtil.quitSendSource(param); - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); - break; case UNABLE_TO_RESPOND: + // 所有错误情况统一处理:退出源连接并通知前端 CnSocketUtil.quitSendSource(param); - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); + sendWebSocketMessage(param.getUserPageId(), socketDataMsg); break; default: + // 未识别的响应码:发送通用错误消息 WebServiceManager.sendUnknownErrorMessage(param.getUserPageId()); break; } @@ -152,19 +282,15 @@ public class SocketSourceResponseService { if (ObjectUtil.isNotNull(dictDataEnumByCode)) { switch (dictDataEnumByCode) { case SUCCESS: - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); - System.out.println("模拟检测-源成功执行脚本" + JSON.toJSONString(socketDataMsg)); + sendWebSocketMessage(param.getUserPageId(), socketDataMsg); + log.info("模拟检测源成功执行脚本,用户: {}, 响应消息: {}", + param.getUserPageId(), JSON.toJSONString(socketDataMsg)); break; case UNPROCESSED_BUSINESS: - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); + sendWebSocketMessage(param.getUserPageId(), socketDataMsg); break; default: - CnSocketUtil.quitSendSource(param); - SocketMsg+ * 处理系数校验阶段程控源的响应消息: + * 1. 成功时:向前端推送响应信息,然后向设备发送数据请求 + * 2. 构造设备数据请求参数,包含监测点列表和数据类型 + * 3. 设置固定的读取参数:读取3次数据,忽略前4次 + * 4. 请求的数据类型:实时电压有效值(real$VRMS)和实时电流有效值(real$IRMS) + *
+ *+ * 数据请求配置: + * - 监测点:使用当前会话的monitorIdList + * - 数据类型:["real$VRMS", "real$IRMS"] + * - 读取次数:3次 + * - 忽略次数:4次(预热数据) + *
+ * + * @param param 检测参数 + * @param socketDataMsg 程控源响应消息 */ private void coefficient(PreDetectionParam param, SocketDataMsg socketDataMsg) { SourceResponseCodeEnum dictDataEnumByCode = SourceResponseCodeEnum.getDictDataEnumByCode(socketDataMsg.getCode()); @@ -181,29 +324,28 @@ public class SocketSourceResponseService { switch (dictDataEnumByCode) { case SUCCESS: //向前端推送信息 - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); + sendWebSocketMessage(param.getUserPageId(), socketDataMsg); String s = param.getUserPageId() + CnSocketUtil.DEV_TAG; socketMsg.setRequestId(SourceOperateCodeEnum.Coefficient_Check.getValue()); socketMsg.setOperateCode(SourceOperateCodeEnum.DEV_DATA_REQUEST_02.getValue()); DevPhaseSequenceParam phaseSequenceParam = new DevPhaseSequenceParam(); phaseSequenceParam.setMoniterIdList(monitorIdList); + // 系数校验固定检测项:实时电压有效值和实时电流有效值 phaseSequenceParam.setDataType(Arrays.asList("real$VRMS", "real$IRMS")); + // 读取3次数据用于系数计算 phaseSequenceParam.setReadCount(3); + // 忽略前4次数据,等待测量稳定 phaseSequenceParam.setIgnoreCount(4); socketMsg.setData(JSON.toJSONString(phaseSequenceParam)); SocketManager.sendMsg(s, JSON.toJSONString(socketMsg)); break; case UNPROCESSED_BUSINESS: - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); + sendWebSocketMessage(param.getUserPageId(), socketDataMsg); break; default: - CnSocketUtil.quitSendSource(param); - socketMsg.setRequestId(socketDataMsg.getRequestId()); - socketMsg.setOperateCode(socketDataMsg.getOperateCode()); - socketMsg.setData(dictDataEnumByCode.getMessage()); - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketMsg)); + sendErrorAndQuit(param, socketDataMsg, dictDataEnumByCode); break; } } @@ -222,7 +364,7 @@ public class SocketSourceResponseService { switch (dictDataEnumByCode) { case SUCCESS: //todo 前端推送收到的消息暂未处理好 - WebServiceManager.sendMsg(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); + sendWebSocketMessage(param.getUserPageId(), socketDataMsg); //开始设备通讯检测(发送设备初始化) Map+ * 当程控源成功执行脚本后,根据检测项目类型向设备发送相应的数据请求: + * 1. 获取源脚本信息,确定检测类型和数据类型 + * 2. 根据检测类型设置不同的读取参数: + * - 闪变(F):忽略1次,读取2次,使用DEV_DATA_REQUEST_01 + * - 暂态(VOLTAGE):忽略5次,读取1次,使用DEV_DATA_REQUEST_03 + * - 其他类型:忽略5次,读取5次,根据数据类型选择操作码 + * 3. 构造设备数据请求并发送 + * 4. 向前端推送检测开始信息 + *
+ *+ * 检测参数配置: + * - 闪变检测:ignoreCount=1, readCount=2 + * - 暂态检测:ignoreCount=5, readCount=1 + * - 常规检测:ignoreCount=5, readCount=5 + * - 实时数据:使用DEV_DATA_REQUEST_02 + * - 分钟数据:使用DEV_DATA_REQUEST_01 + *
* - * @param param - * @param socketDataMsg + * @param param 检测参数,包含用户和设备信息 + * @param socketDataMsg 程控源成功响应消息 */ private void senParamToDev(PreDetectionParam param, SocketDataMsg socketDataMsg) { SourceResponseCodeEnum dictDataEnumByCode = SourceResponseCodeEnum.getDictDataEnumByCode(socketDataMsg.getCode()); @@ -343,42 +481,71 @@ public class SocketSourceResponseService { //向前端推送信息 // webSocketHandler.sendMsgToUser(param.getUserPageId(), JSON.toJSONString(socketDataMsg)); + // 构造设备通道标识:用户ID + 设备标签 String s = param.getUserPageId() + CnSocketUtil.DEV_TAG; + + // 获取当前检测的源脚本信息,包含检测类型和数据要求 SourceIssue sourceIssue = SocketManager.getSourceList().get(0); - List