| | |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.net.InetSocketAddress; |
| | | import java.net.Socket; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | |
| | | |
| | | /** |
| | | * 远程启停控制 |
| | | * 不支持远程控制的设备直接拒绝,避免页面上出现"假按钮" |
| | | * <p> |
| | | * 不支持远程控制的设备直接拒绝,避免页面上出现"假按钮"。 |
| | | * <p> |
| | | * 执行流程(每一步都写进 exec_detail,前端可展开查看通讯日志): |
| | | * 1. 校验设备与远程控制开关 |
| | | * 2. 若配置了合法 IP,先做真实 TCP 连通性探测(Modbus 502 端口,800ms 超时) |
| | | * 3. 当前未接入现场设备,进入模拟模式:不写控制寄存器,但如实记录探测结果 |
| | | * 4. 更新设备状态并记录耗时 |
| | | */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public MonitorControl control(Long deviceId, String command, String remark) { |
| | |
| | | if (!"1".equals(device.getControlEnabled())) { |
| | | throw new IllegalArgumentException("该设备未接入远程控制回路,不支持远程启停,请现场操作"); |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | |
| | | long startMs = System.currentTimeMillis(); |
| | | List<String> logs = new ArrayList<>(); |
| | | String protocol = StringUtils.hasText(device.getCommProtocol()) ? device.getCommProtocol() : "未配置"; |
| | | String ip = StringUtils.hasText(device.getIpAddress()) ? device.getIpAddress().trim() : ""; |
| | | |
| | | logs.add(ts() + " 设备:" + device.getDeviceName() + ",协议:" + protocol + ",地址:" + (ip.isEmpty() ? "未配置" : ip)); |
| | | |
| | | String simulateTip; |
| | | boolean ipValid = ip.matches("^\\d{1,3}(\\.\\d{1,3}){3}$"); |
| | | if (ipValid) { |
| | | logs.add(ts() + " 尝试连接 " + ip + ":502 …"); |
| | | boolean tcpOk = probeTcp(ip, 502, 800); |
| | | if (tcpOk) { |
| | | logs.add(ts() + " TCP 连接成功"); |
| | | logs.add(ts() + " 模拟模式:连通性正常,但未配置寄存器映射,未写入控制寄存器"); |
| | | simulateTip = "设备在线(" + ip + ":502 可达),模拟模式指令未实际写入"; |
| | | } else { |
| | | logs.add(ts() + " 连接失败:" + ip + ":502 无响应(800ms 超时)"); |
| | | logs.add(ts() + " 模拟模式:跳过控制寄存器写入"); |
| | | simulateTip = "设备 " + ip + ":502 无法连接,指令未实际下发"; |
| | | } |
| | | } else { |
| | | logs.add(ts() + " 设备未配置有效通讯地址,跳过连通性探测"); |
| | | logs.add(ts() + " 模拟模式:跳过控制寄存器写入"); |
| | | simulateTip = "设备未配置通讯地址,指令未实际下发"; |
| | | } |
| | | |
| | | String execMsg; |
| | | if ("stop".equalsIgnoreCase(command)) { |
| | | device.setRunStatus("0"); |
| | | execMsg = "停机指令已受理"; |
| | | } else if ("start".equalsIgnoreCase(command)) { |
| | | device.setRunStatus("1"); |
| | | execMsg = "启动指令已受理"; |
| | | } else if ("restart".equalsIgnoreCase(command)) { |
| | | device.setRunStatus("1"); |
| | | execMsg = "重启指令已受理"; |
| | | } else { |
| | | throw new IllegalArgumentException("不支持的控制指令"); |
| | | } |
| | | |
| | | long duration = System.currentTimeMillis() - startMs; |
| | | logs.add(ts() + " " + execMsg + ",耗时 " + duration + "ms"); |
| | | |
| | | MonitorControl control = new MonitorControl(); |
| | | control.setDeviceId(deviceId); |
| | | control.setDeviceName(device.getDeviceName()); |
| | |
| | | control.setControlType(StringUtils.hasText(device.getControlType()) ? device.getControlType() : "启停"); |
| | | control.setCommandSource("远程控制"); |
| | | control.setOperator(currentUserName()); |
| | | control.setOperateTime(now); |
| | | control.setOperateTime(LocalDateTime.now()); |
| | | control.setRemark(remark); |
| | | if ("stop".equalsIgnoreCase(command)) { |
| | | device.setRunStatus("0"); |
| | | control.setResultMsg("停机指令已下发"); |
| | | } else if ("start".equalsIgnoreCase(command)) { |
| | | device.setRunStatus("1"); |
| | | control.setResultMsg("启动指令已下发"); |
| | | } else if ("restart".equalsIgnoreCase(command)) { |
| | | device.setRunStatus("1"); |
| | | control.setResultMsg("重启指令已下发"); |
| | | } else { |
| | | throw new IllegalArgumentException("不支持的控制指令"); |
| | | } |
| | | control.setExecResult("0"); |
| | | control.setExecDuration((int) duration); |
| | | control.setResultMsg(execMsg + "(" + simulateTip + ")"); |
| | | control.setExecDetail(String.join("\n", logs)); |
| | | controlMapper.insert(control); |
| | | deviceMapper.updateById(device); |
| | | return control; |
| | | } |
| | | |
| | | /** 当前时间戳 HH:mm:ss.SSS,用于通讯日志 */ |
| | | private static String ts() { |
| | | return LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS")); |
| | | } |
| | | |
| | | /** TCP 连通性探测(真实连接,用于判断设备是否可达) */ |
| | | private static boolean probeTcp(String host, int port, int timeoutMs) { |
| | | Socket socket = new Socket(); |
| | | try { |
| | | socket.connect(new InetSocketAddress(host, port), timeoutMs); |
| | | return true; |
| | | } catch (Exception e) { |
| | | return false; |
| | | } finally { |
| | | try { |
| | | socket.close(); |
| | | } catch (Exception ignored) { |
| | | } |
| | | } |
| | | } |
| | | |
| | | public IPage<MonitorControl> controlPage(Page<MonitorControl> page, MonitorControl query) { |
| | | return controlMapper.selectPage(page, Wrappers.<MonitorControl>lambdaQuery() |
| | | .like(StringUtils.hasText(query.getDeviceName()), MonitorControl::getDeviceName, query.getDeviceName()) |