From 1248e5543b852aab191cf6f19a65262cc786afae Mon Sep 17 00:00:00 2001 From: weitang Date: Wed, 23 Apr 2025 17:56:18 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BE=85=E6=8E=A7=E9=A1=B9=E7=9B=AE=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=95=B4=E5=90=88=E5=88=9D=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iec104/client/IEC104ClientRunner.java | 89 ++++ .../iec104/client/MasterSysDataHandler.java | 91 ++++ .../iec104/common/BasicInstruction104.java | 230 +++++++++ .../iec104/common/Iec104Constant.java | 49 ++ .../iec104/config/DefaultIec104Config.java | 17 + .../component/iec104/config/Iec104Config.java | 33 ++ .../iec104/core/CachedThreadPool.java | 26 ++ .../iec104/core/ControlManageUtil.java | 123 +++++ .../component/iec104/core/Decoder104.java | 305 ++++++++++++ .../component/iec104/core/Encoder104.java | 94 ++++ .../iec104/core/Iec104ThreadLocal.java | 59 +++ .../iec104/core/ScheduledTaskPool.java | 131 ++++++ .../iec104/enums/QualifiersEnum.java | 89 ++++ .../iec104/enums/TypeIdentifierEnum.java | 133 ++++++ .../component/iec104/enums/UControlEnum.java | 43 ++ .../iec104/message/MessageDetail.java | 148 ++++++ .../component/iec104/message/MessageInfo.java | 48 ++ .../component/iec104/server/Iec104Master.java | 33 ++ .../iec104/server/Iec104MasterFactory.java | 26 ++ .../component/iec104/server/Iec104Slave.java | 34 ++ .../iec104/server/Iec104SlaveFactory.java | 24 + .../iec104/server/handler/BytesEncoder.java | 21 + .../iec104/server/handler/ChannelHandler.java | 16 + .../server/handler/ChannelHandlerImpl.java | 29 ++ .../server/handler/Check104Handler.java | 51 ++ .../iec104/server/handler/DataDecoder.java | 34 ++ .../iec104/server/handler/DataEncoder.java | 48 ++ .../iec104/server/handler/DataHandler.java | 32 ++ .../server/handler/SysSframeHandler.java | 62 +++ .../server/handler/Unpack104Handler.java | 52 +++ .../server/master/BootNettyClientChannel.java | 62 +++ .../master/BootNettyClientChannelCache.java | 32 ++ .../server/master/EventLoopGroupManager.java | 40 ++ .../master/Iec104ClientInitializer.java | 59 +++ .../server/master/Iec104TcpClientMaster.java | 67 +++ .../master/handler/Iec104ClientHandler.java | 98 ++++ .../handler/SysUframeClientHandler.java | 92 ++++ .../server/slave/Iec104ServerInitializer.java | 62 +++ .../server/slave/Iec104TcpServerSlave.java | 85 ++++ .../slave/handler/Iec104TcpSlaveHandler.java | 108 +++++ .../slave/handler/SysUframeServerHandler.java | 89 ++++ .../component/iec104/util/ByteUtil.java | 247 ++++++++++ .../component/iec104/util/Iec104Util.java | 258 +++++++++++ .../DeviceAlarmParameterController.java | 20 + .../DeviceAlarmRecordController.java | 20 + .../controller/DeviceSignalController.java | 20 + .../controller/DeviceWorkDataController.java | 20 + .../controller/GatewayDeviceController.java | 20 + .../controller/MeterDeviceController.java | 20 + .../domain/DeviceAlarmParameter.java | 115 +++++ .../auxcontrol/domain/DeviceAlarmRecord.java | 160 +++++++ .../auxcontrol/domain/DeviceSignal.java | 141 ++++++ .../auxcontrol/domain/DeviceWorkData.java | 91 ++++ .../auxcontrol/domain/GatewayDevice.java | 152 ++++++ .../auxcontrol/domain/MeterDevice.java | 150 ++++++ .../mapper/DeviceAlarmParameterMapper.java | 16 + .../mapper/DeviceAlarmRecordMapper.java | 16 + .../auxcontrol/mapper/DeviceSignalMapper.java | 23 + .../mapper/DeviceWorkDataMapper.java | 16 + .../mapper/GatewayDeviceMapper.java | 16 + .../auxcontrol/mapper/MeterDeviceMapper.java | 16 + .../service/IDeviceAlarmParameterService.java | 16 + .../service/IDeviceAlarmRecordService.java | 27 ++ .../service/IDeviceSignalService.java | 29 ++ .../service/IDeviceWorkDataService.java | 27 ++ .../service/IGatewayDeviceService.java | 23 + .../service/IMeterDeviceService.java | 16 + .../impl/DeviceAlarmParameterServiceImpl.java | 20 + .../impl/DeviceAlarmRecordServiceImpl.java | 438 ++++++++++++++++++ .../service/impl/DeviceSignalServiceImpl.java | 85 ++++ .../impl/DeviceWorkDataServiceImpl.java | 72 +++ .../impl/GatewayDeviceServiceImpl.java | 38 ++ .../service/impl/MeterDeviceServiceImpl.java | 20 + .../com/yfd/platform/utils/CodeGenerator.java | 4 +- .../auxcontrol/DeviceAlarmParameterMapper.xml | 5 + .../auxcontrol/DeviceAlarmRecordMapper.xml | 5 + .../mapper/auxcontrol/DeviceSignalMapper.xml | 72 +++ .../auxcontrol/DeviceWorkDataMapper.xml | 5 + .../mapper/auxcontrol/GatewayDeviceMapper.xml | 5 + .../mapper/auxcontrol/MeterDeviceMapper.xml | 5 + 80 files changed, 5411 insertions(+), 2 deletions(-) create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/client/IEC104ClientRunner.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/client/MasterSysDataHandler.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/common/BasicInstruction104.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/common/Iec104Constant.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/config/DefaultIec104Config.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/config/Iec104Config.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/core/CachedThreadPool.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/core/ControlManageUtil.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/core/Decoder104.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/core/Encoder104.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/core/Iec104ThreadLocal.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/core/ScheduledTaskPool.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/enums/QualifiersEnum.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/enums/TypeIdentifierEnum.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/enums/UControlEnum.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/message/MessageDetail.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/message/MessageInfo.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104Master.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104MasterFactory.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104Slave.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104SlaveFactory.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/BytesEncoder.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/ChannelHandler.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/ChannelHandlerImpl.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/Check104Handler.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/DataDecoder.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/DataEncoder.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/DataHandler.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/SysSframeHandler.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/Unpack104Handler.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/BootNettyClientChannel.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/BootNettyClientChannelCache.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/EventLoopGroupManager.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/Iec104ClientInitializer.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/Iec104TcpClientMaster.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/handler/Iec104ClientHandler.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/handler/SysUframeClientHandler.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/Iec104ServerInitializer.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/Iec104TcpServerSlave.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/handler/Iec104TcpSlaveHandler.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/handler/SysUframeServerHandler.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/util/ByteUtil.java create mode 100644 riis-system/src/main/java/com/yfd/platform/component/iec104/util/Iec104Util.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceAlarmParameterController.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceAlarmRecordController.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceSignalController.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceWorkDataController.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/GatewayDeviceController.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/MeterDeviceController.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceAlarmParameter.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceAlarmRecord.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceSignal.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceWorkData.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/GatewayDevice.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/MeterDevice.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceAlarmParameterMapper.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceAlarmRecordMapper.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceSignalMapper.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceWorkDataMapper.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/GatewayDeviceMapper.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/MeterDeviceMapper.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceAlarmParameterService.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceAlarmRecordService.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceSignalService.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceWorkDataService.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IGatewayDeviceService.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IMeterDeviceService.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceAlarmParameterServiceImpl.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceAlarmRecordServiceImpl.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceSignalServiceImpl.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceWorkDataServiceImpl.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/GatewayDeviceServiceImpl.java create mode 100644 riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/MeterDeviceServiceImpl.java create mode 100644 riis-system/src/main/resources/mapper/auxcontrol/DeviceAlarmParameterMapper.xml create mode 100644 riis-system/src/main/resources/mapper/auxcontrol/DeviceAlarmRecordMapper.xml create mode 100644 riis-system/src/main/resources/mapper/auxcontrol/DeviceSignalMapper.xml create mode 100644 riis-system/src/main/resources/mapper/auxcontrol/DeviceWorkDataMapper.xml create mode 100644 riis-system/src/main/resources/mapper/auxcontrol/GatewayDeviceMapper.xml create mode 100644 riis-system/src/main/resources/mapper/auxcontrol/MeterDeviceMapper.xml diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/client/IEC104ClientRunner.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/client/IEC104ClientRunner.java new file mode 100644 index 0000000..718e6fc --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/client/IEC104ClientRunner.java @@ -0,0 +1,89 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.component.iec104.client; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.component.iec104.config.Iec104Config; +import com.yfd.platform.component.iec104.core.CachedThreadPool; +import com.yfd.platform.component.iec104.server.Iec104Master; +import com.yfd.platform.component.iec104.server.Iec104MasterFactory; +import com.yfd.platform.modules.auxcontrol.domain.GatewayDevice; +import com.yfd.platform.modules.auxcontrol.mapper.GatewayDeviceMapper; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author + * @date 2019-01-07 + */ +@Component +@RequiredArgsConstructor +public class IEC104ClientRunner implements ApplicationRunner { + + private static final Logger log = LoggerFactory.getLogger(IEC104ClientRunner.class); + private final GatewayDeviceMapper gatewayDeviceMapper; + + /** + * 项目启动时激活启用的IEC104 与各子站的连接任务 + * + * @param applicationArguments / + */ + @Override + @Async + public void run(ApplicationArguments applicationArguments) throws Exception { + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + //没有停用 + queryWrapper.ne(GatewayDevice::getStatus, "00"); + queryWrapper.eq(GatewayDevice::getDeviceType, "30"); + queryWrapper.eq(GatewayDevice::getProtocol, "IEC104"); + queryWrapper.select(GatewayDevice::getId, GatewayDevice::getDeviceCode, GatewayDevice::getDeviceName, + GatewayDevice::getIpAddr, GatewayDevice::getIecAddr); + queryWrapper.orderByAsc(GatewayDevice::getDeviceCode); + List list = gatewayDeviceMapper.selectList(queryWrapper); + if (list.size() > 0) { + for (GatewayDevice gatewayDevice : list) { + Iec104Config iec104Config = new Iec104Config(); + iec104Config.setFrameAmountMax((short) 10); + short terminnalAddress = gatewayDevice.getIecAddr().shortValue(); + //通讯网关机地址 + iec104Config.setTerminnalAddress(terminnalAddress); + iec104Config.setSlaveCode(gatewayDevice.getDeviceCode()); + iec104Config.setSlaveIP(gatewayDevice.getIpAddr()); + Runnable runnable = () -> { + Iec104Master iec104server = Iec104MasterFactory.createTcpClientMaster(iec104Config.getSlaveIP(), + 2404); + try { + iec104server.setDataHandler(new MasterSysDataHandler()).setConfig(iec104Config).run(); + + } catch (Exception e) { + log.error(String.format("%s-对应的终端连接失败!", iec104Config.getSlaveIP())); + } + }; + CachedThreadPool.getCachedThreadPool().execute(runnable); + } + + } + + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/client/MasterSysDataHandler.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/client/MasterSysDataHandler.java new file mode 100644 index 0000000..461d89b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/client/MasterSysDataHandler.java @@ -0,0 +1,91 @@ +package com.yfd.platform.component.iec104.client; + +import cn.hutool.core.util.ObjUtil; +import com.ydl.iec.util.ByteUtil; +import com.yfd.platform.component.iec104.message.MessageDetail; +import com.yfd.platform.component.iec104.message.MessageInfo; +import com.yfd.platform.component.iec104.server.handler.ChannelHandler; +import com.yfd.platform.component.iec104.server.handler.DataHandler; +import com.yfd.platform.component.iec104.server.master.BootNettyClientChannel; +import com.yfd.platform.component.iec104.server.master.BootNettyClientChannelCache; +import com.yfd.platform.modules.auxcontrol.service.IDeviceAlarmRecordService; +import com.yfd.platform.modules.auxcontrol.service.IDeviceSignalService; +import com.yfd.platform.modules.auxcontrol.service.IDeviceWorkDataService; +import com.yfd.platform.modules.auxcontrol.service.IGatewayDeviceService; +import com.yfd.platform.utils.SpringContextHolder; +import io.netty.channel.ChannelHandlerContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * @author Admin + */ +@io.netty.channel.ChannelHandler.Sharable +public class MasterSysDataHandler implements DataHandler { + + private static final Logger log = LoggerFactory.getLogger(MasterSysDataHandler.class); + IDeviceSignalService signalService = SpringContextHolder.getBean(IDeviceSignalService.class); + IDeviceWorkDataService workDataService = SpringContextHolder.getBean(IDeviceWorkDataService.class); + IGatewayDeviceService gatewayDeviceService = SpringContextHolder.getBean(IGatewayDeviceService.class); + IDeviceAlarmRecordService alarmRecordService = SpringContextHolder.getBean(IDeviceAlarmRecordService.class); + + @Override + public void handlerAdded(ChannelHandler ctx) throws Exception { + ChannelHandlerContext currentctx = ctx.getCtx(); + BootNettyClientChannel channel = BootNettyClientChannelCache.get(currentctx.channel().id().asShortText()); + String slaveIp = channel.getCode(); + log.info(String.format("%s-对应的终端连接成功!", slaveIp)); + } + + @Override + public void channelRead(ChannelHandler ctx, MessageDetail ruleDetail104) throws Exception { + ChannelHandlerContext currentctx = ctx.getCtx(); + //------------从通道中获取当前连接的IP-----------------// + BootNettyClientChannel channel = BootNettyClientChannelCache.get(currentctx.channel().id().asShortText()); + if (ObjUtil.isEmpty(channel)) { + //通道已关闭 + } else { + + String slaveIp = channel.getCode(); + String hexString = ByteUtil.byteArrayToHexString(ruleDetail104.getControl()); + if ("83000000".equals(hexString)) { + //测试确认,更新心跳时间 + gatewayDeviceService.updateKeepLiveTime(slaveIp); + return; + } + int type = ruleDetail104.getTypeIdentifier().getValue(); + if (type == 1 || type == 3 || type == 30 || type == 31) { + //遥信值 + List list = ruleDetail104.getMessages(); + for (MessageInfo messageInfo : list) { + String address = String.valueOf(messageInfo.getMessageAddress()); + String status = String.valueOf(messageInfo.getStatus()); + String dateTime = messageInfo.getTimeScale(); + signalService.updateDeviceSignalValue(slaveIp, address, "yx", status, dateTime); + //生成设备自身报警记录(status=1) + alarmRecordService.doAlaramRecord("IEC104", "yx", slaveIp, address, status); + } + } + if (type == 9 || type == 11 || type == 13 || type == 34 || type == 35 || type == 36) { + //遥测值 + List list = ruleDetail104.getMessages(); + for (MessageInfo messageInfo : list) { + String address = String.valueOf(messageInfo.getMessageAddress() - 16384); + //从设备上去的地址减去 + // 4000H对应的偏差值16384 + String value = String.valueOf(messageInfo.getValue()); + String dateTime = messageInfo.getTimeScale(); + workDataService.insertData("IEC104", slaveIp, address, value, dateTime); + signalService.updateDeviceSignalValue(slaveIp, address, "yc", value, dateTime); + //生成阈值越限报警记录 + alarmRecordService.doAlaramRecord("IEC104", "yc", slaveIp, address, value); + } + } + + } + + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/common/BasicInstruction104.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/common/BasicInstruction104.java new file mode 100644 index 0000000..877577b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/common/BasicInstruction104.java @@ -0,0 +1,230 @@ +package com.yfd.platform.component.iec104.common; + +import cn.hutool.core.util.TypeUtil; +import com.yfd.platform.component.iec104.core.Encoder104; +import com.yfd.platform.component.iec104.message.MessageDetail; +import com.yfd.platform.component.iec104.message.MessageInfo; +import com.yfd.platform.component.iec104.enums.QualifiersEnum; +import com.yfd.platform.component.iec104.enums.TypeIdentifierEnum; +import com.yfd.platform.component.iec104.enums.UControlEnum; +import com.ydl.iec.util.ByteUtil; +import com.ydl.iec.util.Iec104Util; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +/** + * 104 规约的基本指令封装 +* @ClassName: BasicInstruction104 +* @Description: 返回指定的指令 +* @author YDL + */ + +public class BasicInstruction104 { + // 68040B 00 00 00 + + /** + * 链路启动指令 + */ + public static final byte[] STARTDT = new byte[] {0x68, 0x04, 0x07, 0x00, 0x00, 0x00}; + + /** + * 链路启动确认指令 + */ + public static final byte[] STARTDT_YES = new byte[] {0x68, 0x04, 0x0B, 0x00, 0x00, 0x00}; + + /** + * 测试命令指令 + */ + public static final byte[] TESTFR = new byte[] {0x68, 0x04, (byte) 0x43, 0x00, 0x00, 0x00}; + + /** + * 测试回复确认 + */ + public static final byte[] TESTFR_YES = new byte[] {0x68, 0x04, (byte) 0x83, 0x00, 0x00, 0x00}; + + + + + /** + * 停止确认 + */ + public static final byte[] STOPDT_YES = new byte[] {0x68, 0x04, 0x23, 0x00, 0x00, 0x00}; + + + + + + /** + * + * @Title: getGeneralCallRuleDetail104 + * @Description: 总召唤指令 + * @param @return + * @param @throws IOException + * @return MessageDetail + * @throws + */ + public static MessageDetail getGeneralCallRuleDetail104() throws IOException { + TypeIdentifierEnum typeIdentifierEnum = TypeIdentifierEnum.generalCall; + int sq = 0; + boolean isContinuous = sq == 0 ? false : true; + // 接收序号 + short accept = 0; + // 发送序号 + short send = 0; + byte[] control = Iec104Util.getIcontrol(accept, send); + // 传输原因 + short transferReason = 6; + boolean isTest = false; + boolean isPn = true; + // 终端地址 实际发生的时候会被替换 + short terminalAddress = 1; + // 消息地址 总召唤地址为0 + int messageAddress = 0; + QualifiersEnum qualifiers = QualifiersEnum.generalCallGroupingQualifiers; + List messages = new ArrayList<>(); + MessageInfo messageInfo=new MessageInfo(); + byte[] message=new byte[]{0x00, 0x00, 0x00, 0x00}; + messageInfo.setMessageInfos(message); + messages.add(messageInfo); + MessageDetail ruleDetail104 = new MessageDetail(control, typeIdentifierEnum, isContinuous, isTest, isPn, transferReason, + terminalAddress, messageAddress, messages, null, qualifiers); + + byte[] bytes = Encoder104.encoder(ruleDetail104); + ruleDetail104.setHexString(ByteUtil.byteArrayToHexString(bytes)); + return ruleDetail104; + } + + + /** + * + * @Title: getYesGeneralCallRuleDetail104 + * @Description: 总召唤确认指令 + * @return + * @return MessageDetail + * @throws + */ + public static MessageDetail getYesGeneralCallRuleDetail104() { + TypeIdentifierEnum typeIdentifierEnum = TypeIdentifierEnum.generalCall; + //SQ=0 length =1 + int sq = 0; + boolean isContinuous = sq == 0 ? false : true; + // 接收序号 + short accept = 0; + // 发送序号 + short send = 0; + byte[] control = Iec104Util.getIcontrol(accept, send); + // 传输原因 + short transferReason = 7; + // true:1 ; false : 0 + boolean isTest = false; + // true:0 false;1 + boolean isPN = true; + + short terminalAddress = 1; + // 消息地址 总召唤地址为0 + int messageAddress = 0; + + QualifiersEnum qualifiers = QualifiersEnum.generalCallGroupingQualifiers; + List messages = new ArrayList<>(); + MessageDetail ruleDetail104 = new MessageDetail(control, typeIdentifierEnum, isContinuous, isTest, isPN, transferReason, + terminalAddress, messageAddress, messages, null, qualifiers); + return ruleDetail104; + } + + + /** + * + * @Title: getEndGeneralCallRuleDetail104 + * @Description: 总召唤结束指令 + * @return + * @return MessageDetail + * @throws + */ + public static MessageDetail getEndGeneralCallRuleDetail104() { + TypeIdentifierEnum typeIdentifierEnum = TypeIdentifierEnum.generalCall; + //SQ=0 length =1 + int sq = 0; + boolean isContinuous = sq == 0 ? false : true; + // 接收序号 + short accept = 1; + // 发送序号 + short send = 4; + byte[] control = Iec104Util.getIcontrol(accept, send); + // 传输原因 + short transferReason = 0x0A; + // true:1 ; false : 0 + boolean isTest = false; + // true:0 false;1 + boolean isPN = true; + + short terminalAddress = 1; + // 消息地址 总召唤地址为0 + int messageAddress = 0; + // 老板限定词 + QualifiersEnum qualifiers = QualifiersEnum.generalCallGroupingQualifiers; + List messages = new ArrayList<>(); + MessageDetail ruleDetail104 = new MessageDetail(control, typeIdentifierEnum, isContinuous, isTest, isPN, transferReason, + terminalAddress, messageAddress, messages, null, qualifiers); + return ruleDetail104; + } + + public static MessageDetail getInitRuleDetail104() { + byte[] control = ByteUtil.intToByteArray(UControlEnum.STARTDT.getValue()); + MessageDetail ruleDetail104 = new MessageDetail(control); + return ruleDetail104; + } + + /** + * + * @Title: gettTimeScale104 + * @Description: 对时 + * @param @return + * @param @throws IOException + * @return MessageDetail + * @throws + */ + public static MessageDetail getTimeScale104() { + + // 接收序号 + short accept = 0; + // 发送序号 + short send = 0; + //控制欲 + byte[] control = Iec104Util.getIcontrol(accept, send); + // 消息地址 总召唤地址为0 + int messageAddress = 0; + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + Calendar calendar = Calendar.getInstance(); + Date timeScale = calendar.getTime(); + //对时没有限定词 +// QualifiersEnum qualifiers = QualifiersEnum.generalCallGroupingQualifiers; + List messages = new ArrayList<>(); + MessageInfo message = new MessageInfo(); + message.setMessageInfos(new byte[] {0x01}); + //类型标识 对时同步 + TypeIdentifierEnum typeIdentifierEnum = TypeIdentifierEnum.timeSynchronization; + + int sq = 0; + //可变结构限定词 01 + boolean isContinuous = sq == 0 ? false : true; + + // 传输原因 + short transferReason = 6; + // 终端地址 实际发生的时候会被替换 公共地址 + short terminalAddress = 1; + + boolean isTest = false; + boolean isPn = true; + + message.setMessageAddress(messageAddress); + messages.add(message); + MessageDetail ruleDetail104 = new MessageDetail(control, typeIdentifierEnum, isContinuous, isTest, isPn, transferReason, + terminalAddress, messageAddress, messages, timeScale,null); + return ruleDetail104; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/common/Iec104Constant.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/common/Iec104Constant.java new file mode 100644 index 0000000..a2fc995 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/common/Iec104Constant.java @@ -0,0 +1,49 @@ +package com.yfd.platform.component.iec104.common; + +/** + * +* @ClassName: Iec104Constant +* @Description: TODO +* @author YDL + */ +public class Iec104Constant { + + /** + * 开始字符 + */ + public static final byte HEAD_DATA = 0x68; + + /** + * 控制域长度 + */ + public static final byte CPNTROL_LENGTH = 0x04; + + /** + * APCI 长度 + */ + public static final byte APCI_LENGTH = 0x06; + + /** + * APCI 中 发送序号低位坐标 + */ + public static final int ACCEPT_LOW_INDEX = 2; + + /** + * APCI 中 发送序号高位坐标 + */ + public static final int ACCEPT_HIGH_INDEX = 3; + + + /** + *最大接收序号 + */ + public static final Short SEND_MAX = 32767; + + /** + * 最小接收序号 + */ + public static final Short SEND_MIN = 0; + + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/config/DefaultIec104Config.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/config/DefaultIec104Config.java new file mode 100644 index 0000000..4471212 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/config/DefaultIec104Config.java @@ -0,0 +1,17 @@ +package com.yfd.platform.component.iec104.config; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 默认的配置 + */ +@Data +@EqualsAndHashCode(callSuper=false) +public class DefaultIec104Config extends Iec104Config { + + public DefaultIec104Config() { + setFrameAmountMax((short) 1); + setTerminnalAddress((short) 1); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/config/Iec104Config.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/config/Iec104Config.java new file mode 100644 index 0000000..a1a135e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/config/Iec104Config.java @@ -0,0 +1,33 @@ +package com.yfd.platform.component.iec104.config; + +import lombok.Data; + +/** + * 104规约的配置 + */ +@Data +public class Iec104Config { + + /** + * 接收到帧的数量到该值就要发一个确认帧 + */ + private short frameAmountMax; + + + /** + * 终端地址 + */ + private short terminnalAddress; + + + /** + * 终端IP + */ + private String slaveIP; + + + /** + * 终端编号 + */ + private String slaveCode; +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/core/CachedThreadPool.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/core/CachedThreadPool.java new file mode 100644 index 0000000..f95000a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/core/CachedThreadPool.java @@ -0,0 +1,26 @@ +package com.yfd.platform.component.iec104.core; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * 线程池 + */ +public final class CachedThreadPool { + + private static CachedThreadPool cachedThreadPool = new CachedThreadPool(); + private ExecutorService executorService; + + private CachedThreadPool() { + executorService = Executors.newCachedThreadPool(); + } + + public static CachedThreadPool getCachedThreadPool() { + return cachedThreadPool; + } + + public void execute(Runnable runnable) { + executorService.execute(runnable); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/core/ControlManageUtil.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/core/ControlManageUtil.java new file mode 100644 index 0000000..829da6e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/core/ControlManageUtil.java @@ -0,0 +1,123 @@ +package com.yfd.platform.component.iec104.core; + +import com.yfd.platform.component.iec104.common.Iec104Constant; +import com.yfd.platform.component.iec104.message.MessageDetail; +import com.ydl.iec.util.Iec104Util; +import io.netty.channel.ChannelHandlerContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import io.netty.channel.ChannelInboundHandlerAdapter; +/** + * 控制域的管理工具 + */ +public class ControlManageUtil { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class); + + /** + * 发送序号 + */ + private Short send; + + /** + * 接收序号 + */ + private Short accept; + + /** + * 接收到帧的数量 + */ + private Short frameAmount; + + /** + * 发送S帧的锁 + */ + private Boolean sendSframeLock; + + /** + * 接收到帧的数量最大阀值 + */ + + private short frameAmountMax; + + /** + * 发送消息句柄 + */ + private ChannelHandlerContext ctx; + + + + public ControlManageUtil(ChannelHandlerContext ctx) { + send = 0; + accept = 0; + frameAmount = 0; + sendSframeLock = true; + frameAmountMax = 1; + this.ctx = ctx; + } + + /** + * 启动S发送S确认帧 的任务 + */ + public void startSendFrameTask() { + Runnable runnable = () -> { + while (true) { + try { + synchronized (sendSframeLock) { + if (frameAmount >= frameAmountMax) { + // 查过最大帧 的数量就要发送一个确认帧出去 + byte[] control = Iec104Util.getScontrol(accept); + MessageDetail ruleDetail104 = new MessageDetail(control); + ctx.channel().writeAndFlush(Encoder104.encoder(ruleDetail104)); + frameAmount = 0; + } + sendSframeLock.wait(); + } + } catch (Exception e) { + LOGGER.error("Exception caught", e); + } + } + }; + CachedThreadPool.getCachedThreadPool().execute(runnable); + } + + + /** + * 返回当前的发送序号 + */ + public short getSend() { + synchronized (send) { + short sendRule = this.send; + this.send++; + if (send > Iec104Constant.SEND_MAX) { + send = Iec104Constant.SEND_MIN; + } + return sendRule; + } + } + public short getAccept() { + return accept; + } + + /** + * + * @Title: setAccept + * @Description: 设置接收序号 + * @param lastAccept + */ + public void setAccept(short lastAccept) { + synchronized (sendSframeLock) { + this.accept = lastAccept; + frameAmount++; + if (frameAmount >= frameAmountMax) { + this.accept = lastAccept; + sendSframeLock.notifyAll(); + } + } + } + + + public ControlManageUtil setFrameAmountMax(short frameAmountMax) { + this.frameAmountMax = frameAmountMax; + return this; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/core/Decoder104.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/core/Decoder104.java new file mode 100644 index 0000000..40016f1 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/core/Decoder104.java @@ -0,0 +1,305 @@ +package com.yfd.platform.component.iec104.core; + +import com.ydl.iec.util.Iec104Util; +import com.yfd.platform.component.iec104.message.MessageDetail; +import com.yfd.platform.component.iec104.message.MessageInfo; +import com.yfd.platform.component.iec104.enums.QualifiersEnum; +import com.yfd.platform.component.iec104.enums.TypeIdentifierEnum; +import com.ydl.iec.util.ByteUtil; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; + +/** + * +* @ClassName: Decoder104 +* @Description: 104 协议转码工具 +* @author YDL +* @date 2020年5月13日 + */ +public class Decoder104 { + + /** + * 将bytes 转换成指定的数据结构 + * @param bytes + * @return + */ + public static MessageDetail encoder(byte[] bytes) { + MessageDetail ruleDetail104 = new MessageDetail(); + int index = 0; + ruleDetail104.setStart(bytes[index++]); + ruleDetail104.setApuuLength(bytes[index++] & 0xFF); + ruleDetail104.setControl(ByteUtil.getByte(bytes, index, 4)); + index += 4; + if (ruleDetail104.getApuuLength() <= 4) { + ruleDetail104.setMessages(new ArrayList<>()); + // 如果只有APCI 就此返回 + return ruleDetail104; + } + // 下面是返回ASDU的结构 + ruleDetail104.setTypeIdentifier(TypeIdentifierEnum.getTypeIdentifierEnum(bytes[index++])); + // 添加可变结构限定词 + Iec104Util.setChanged(ruleDetail104, bytes[index++]); + + ruleDetail104.setTransferReason(ByteUtil.byteArrayToShort(ByteUtil.getByte(bytes, index, 2))); + index += 2; + // + ruleDetail104.setTerminalAddress(Iec104Util.getTerminalAddressShort(ByteUtil.getByte(bytes, index, 2))); + index += 2; + Iec104Util.setMeaageAttribute(ruleDetail104); + setMessage(ruleDetail104, bytes, index); + return ruleDetail104; + } + + /** + * + * @Title: setMessage + * @Description: 对消息进行编码 + * @param @param ruleDetail104 + * @param @param bytes + * @param @param index + * @return void + * @throws + */ + public static void setMessage(MessageDetail ruleDetail104, byte[] bytes, int index) { + int mesageIndex = index; + if (ruleDetail104.isContinuous()) { + setContinuoustMessage(ruleDetail104, bytes, mesageIndex); + } else { + setNoContinuoustMessage(ruleDetail104, bytes, mesageIndex); + } + } + + + /** + * + * @Title: setContinuoustMessage + * @Description: 设置连续地址的消息 + * @param ruleDetail104 + * @param bytes + * @param index + * @return void + * @throws + */ + public static void setContinuoustMessage(MessageDetail ruleDetail104, byte[] bytes, int index) { + List messages = new ArrayList<>(); + int mesageIndex = index; + // 连续的 前三个字节是地址 + // 如果是地址联系的只需要设置一个初始地址就可以了 + // TODO 此处不处理地址 + int messageAddress = Iec104Util.messageAddressToInt(ByteUtil.getByte(bytes, mesageIndex, 3)); + ruleDetail104.setMessageAddress(messageAddress); + mesageIndex += 3; + if (ruleDetail104.isMessage()) { + // 获取每个消息的长度 + int messageLength = getMessageLength(ruleDetail104); + int messageSize = 0; + while (messageSize < ruleDetail104.getMeasgLength()) { + MessageInfo messageObj = new MessageInfo(); + messageObj.setMessageAddress(messageAddress); + byte[] messageInfos = ByteUtil.getByte(bytes, mesageIndex, messageLength); + mesageIndex += messageLength; + messageObj.setMessageInfos(messageInfos); + //对数据的值进行解析 + setMessageValue(ruleDetail104, messageObj); + + if (ruleDetail104.isQualifiers() && TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) { + // 判断是否有限定词 + // 0(每个信息元素后缀1个字节) + ruleDetail104.setQualifiersType(QualifiersEnum.getQualifiersEnum(ruleDetail104.getTypeIdentifier(), bytes[mesageIndex++])); + } + + messageSize++; + messageAddress++; + messages.add(messageObj); + } + } + + + if (ruleDetail104.isQualifiers() && !TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) { + // 判断是否有限定词 + ruleDetail104.setQualifiersType(QualifiersEnum.getQualifiersEnum(ruleDetail104.getTypeIdentifier(), bytes[mesageIndex++])); + } + + + if (ruleDetail104.isTimeScaleExit()) { + ruleDetail104.setTimeScale(ByteUtil.byte2Hdate(ByteUtil.getByte(bytes, mesageIndex, 7))); + // 创建一个 SimpleDateFormat 对象,指定日期格式 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + // 使用 SimpleDateFormat 对象将 Date 对象格式化为字符串 + String dateString = sdf.format(ruleDetail104.getTimeScale()); + for (MessageInfo message : messages) { + message.setTimeScale(dateString); + } + } + + ruleDetail104.setMessages(messages); + } + + + /** + * + * @Title: setNoContinuoustMessage + * @Description: 设置不连续地址的消息 + * @param ruleDetail104 + * @param bytes + * @param index + * @return void + * @throws + */ + public static void setNoContinuoustMessage(MessageDetail ruleDetail104, byte[] bytes, int index) { + List messages = new ArrayList<>(); + int mesageIndex = index; + // 获取每个消息的长度 + int messageLength = getMessageLength(ruleDetail104); + int messageSize = 0; + while (messageSize < ruleDetail104.getMeasgLength()) { + MessageInfo messageObj = new MessageInfo(); + // 消息地址 + messageObj.setMessageAddress(Iec104Util.messageAddressToInt(ByteUtil.getByte(bytes, mesageIndex, 3))); + mesageIndex += 3; + + if (ruleDetail104.isMessage()) { + // 消息集合 + byte[] messageInfos = ByteUtil.getByte(bytes, mesageIndex, messageLength); + mesageIndex += messageLength; + messageObj.setMessageInfos(messageInfos); + //对数据的值进行解析 + setMessageValue(ruleDetail104, messageObj); + } else { + messageObj.setMessageInfos(new byte[] {}); + } + + if (ruleDetail104.isQualifiers() && TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) { + // 判断是否有限定词 + // 0(每个信息元素后缀1个字节) + ruleDetail104.setQualifiersType(QualifiersEnum.getQualifiersEnum(ruleDetail104.getTypeIdentifier(), bytes[mesageIndex++])); + } + + messageSize++; + messages.add(messageObj); + } + + + if (ruleDetail104.isQualifiers() && !TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) { + // 判断是否有限定词 + ruleDetail104.setQualifiersType(QualifiersEnum.getQualifiersEnum(ruleDetail104.getTypeIdentifier(), bytes[mesageIndex++])); + } + + + if (ruleDetail104.isTimeScaleExit()) { + ruleDetail104.setTimeScale(ByteUtil.byte2Hdate(ByteUtil.getByte(bytes, mesageIndex, 7))); + // 创建一个 SimpleDateFormat 对象,指定日期格式 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + // 使用 SimpleDateFormat 对象将 Date 对象格式化为字符串 + String dateString = sdf.format(ruleDetail104.getTimeScale()); + for (MessageInfo message : messages) { + message.setTimeScale(dateString); + } + } + + ruleDetail104.setMessages(messages); + } + + /** + * 根据类型对数据的值进行解析 + * */ + private static void setMessageValue(MessageDetail ruleDetail104, MessageInfo messageObj) { + switch (ruleDetail104.getTypeIdentifier().getValue()) { + case 0x01: + messageObj.setStatus(messageObj.getMessageInfos()[0] & 0xFF); + break; + case 0x03: + int value3=messageObj.getMessageInfos()[0] & 0xFF; + if(value3==2) + { + messageObj.setStatus(1); + } + else if(value3==1){ + messageObj.setStatus(0); + } + else{ + messageObj.setStatus(9);//未确定 + } + break; + case 0x09: + // 遥测 测量值 归一化值 遥测 + byte[] newbyte9=new byte[2]; + newbyte9[0]=messageObj.getMessageInfos()[1]; + newbyte9[1]=messageObj.getMessageInfos()[0]; + float value9 =ByteUtil.byteArrayToShort(newbyte9); + messageObj.setValue(value9); + break; + case 0x0B: + // 遥测 测量值 标度化值 遥测 + byte[] newbyte11=new byte[2]; + newbyte11[0]=messageObj.getMessageInfos()[1]; + newbyte11[1]=messageObj.getMessageInfos()[0]; + float value11 = ByteUtil.byteArrayToShort(newbyte11); + messageObj.setValue(value11); + break; + case 0x0D: + // 遥测 测量值 短浮点数 遥测 + float value13 = Float.intBitsToFloat((messageObj.getMessageInfos()[0] & 0xff) | ((messageObj.getMessageInfos()[1] & 0xff) << 8) | ((messageObj.getMessageInfos()[2] & 0xff) << 16) | ((messageObj.getMessageInfos()[3] & 0xff) << 24)); + messageObj.setValue(value13); + break; + case 0x1E: + messageObj.setStatus(messageObj.getMessageInfos()[0] & 0xFF); + break; + case 0x1F: + int value31=messageObj.getMessageInfos()[0] & 0xFF; + if(value31==2) + { + messageObj.setStatus(1); + } + else if(value31==1){ + messageObj.setStatus(0); + } + else{ + messageObj.setStatus(9);//未确定 + } + break; + case 0x22: + // 遥测 测量值 归一化值 带时标 + byte[] newbyte34=new byte[2]; + newbyte34[0]=messageObj.getMessageInfos()[1]; + newbyte34[1]=messageObj.getMessageInfos()[0]; + messageObj.setValue(ByteUtil.byteArrayToShort(newbyte34)); + break; + case 0x23: + // 遥测 测量值 标度化值 带时标 + byte[] newbyte35=new byte[2]; + newbyte35[0]=messageObj.getMessageInfos()[1]; + newbyte35[1]=messageObj.getMessageInfos()[0]; + messageObj.setValue(ByteUtil.byteArrayToShort(newbyte35)); + break; + case 0x24: + // 遥测 测量值 短浮点数 遥测 + float value36 = Float.intBitsToFloat((messageObj.getMessageInfos()[0] & 0xff) | ((messageObj.getMessageInfos()[1] & 0xff) << 8) | ((messageObj.getMessageInfos()[2] & 0xff) << 16) | ((messageObj.getMessageInfos()[3] & 0xff) << 24)); + messageObj.setValue(value36); + break; + case 0x66: + // 读单个参数 + break; + case (byte) 0x84: + // 读多个参数 + break; + case 0x30: + // 预置单个参数命令 + break; + case (byte) 0x88: + // 预置多个个参数 + break; + default : + } + } + + + /** + * 根据类型标识返回消息长度 + */ + private static int getMessageLength(MessageDetail ruleDetail104) { + return ruleDetail104.getTypeIdentifier().getMessageLength(); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/core/Encoder104.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/core/Encoder104.java new file mode 100644 index 0000000..010f6da --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/core/Encoder104.java @@ -0,0 +1,94 @@ +package com.yfd.platform.component.iec104.core; + +import com.yfd.platform.component.iec104.enums.QualifiersEnum; +import com.yfd.platform.component.iec104.enums.TypeIdentifierEnum; +import com.yfd.platform.component.iec104.message.MessageDetail; +import com.yfd.platform.component.iec104.message.MessageInfo; +import com.ydl.iec.util.ByteUtil; +import com.ydl.iec.util.Iec104Util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * + * @author Admin + * + */ +public class Encoder104 { + + + public static byte[] encoder(MessageDetail ruleDetail104) throws IOException { + Iec104Util.setMeaageAttribute(ruleDetail104); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + bytes.write(ruleDetail104.getStart()); + byte[] apduBytes = getApduBytes(ruleDetail104); + int messageLen = apduBytes.length; + ruleDetail104.setApuuLength(messageLen); + bytes.write((byte) messageLen); + bytes.write(apduBytes); + return bytes.toByteArray(); + } + + private static byte[] getApduBytes(MessageDetail ruleDetail104) throws IOException { + ByteArrayOutputStream bOutput = new ByteArrayOutputStream(); + // 控制域 + bOutput.write(ruleDetail104.getControl()); + if (ruleDetail104.getTypeIdentifier() == null) { + // U帧或者S帧 + return bOutput.toByteArray(); + } + // 类型标识 + bOutput.write((byte) ruleDetail104.getTypeIdentifier().getValue()); + // 可变结构限定词 + bOutput.write(Iec104Util.getChangedQualifiers(ruleDetail104)); + // 传输原因 + bOutput.write(ByteUtil.shortToByteArray(ruleDetail104.getTransferReason())); + // 终端地址 + bOutput.write((Iec104Util.getTerminalAddressByte(ruleDetail104.getTerminalAddress()))); +// 如果是是连续的则数据地址 只需要在开头写以后的数据单元就不需要再写了 + if (ruleDetail104.isContinuous()) { + bOutput.write(Iec104Util.intToMessageAddress(ruleDetail104.getMessageAddress())); + // 地址只取三个字节 + if (ruleDetail104.isMessage()) { + for (MessageInfo ruleDetail104Message : ruleDetail104.getMessages()) { + bOutput.write(ruleDetail104Message.getMessageInfos()); + if (ruleDetail104.isQualifiers() && TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) { + // 0(每个信息元素后缀1个字节) + bOutput.write(ruleDetail104.getQualifiersType().getValue()); + } + + } + } + + if (ruleDetail104.isQualifiers() && !TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) { + bOutput.write(ruleDetail104.getQualifiersType().getValue()); + } + + if (ruleDetail104.isTimeScaleExit()) { + bOutput.write(ByteUtil.date2Hbyte(ruleDetail104.getTimeScale())); + } + } else { + for (MessageInfo ruleDetail104Message : ruleDetail104.getMessages()) { + bOutput.write(Iec104Util.intToMessageAddress(ruleDetail104Message.getMessageAddress())); + if (ruleDetail104.isMessage()) { + bOutput.write(ruleDetail104Message.getMessageInfos()); + } + if (ruleDetail104.isQualifiers() && TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) { + // 0(每个信息元素后缀1个字节) + bOutput.write(ruleDetail104.getQualifiersType().getValue()); + } + } + + if (ruleDetail104.isQualifiers() && !TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) { + bOutput.write(ruleDetail104.getQualifiersType().getValue()); + } + + if (ruleDetail104.isTimeScaleExit()) { + bOutput.write(ByteUtil.date2Hbyte(ruleDetail104.getTimeScale())); + } + } + return bOutput.toByteArray(); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/core/Iec104ThreadLocal.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/core/Iec104ThreadLocal.java new file mode 100644 index 0000000..dd36a91 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/core/Iec104ThreadLocal.java @@ -0,0 +1,59 @@ +package com.yfd.platform.component.iec104.core; + + +import com.yfd.platform.component.iec104.config.Iec104Config; + +/** + * @ClassName: Iec104ThreadLocal + * @Description: 线程变量管理 + * @author: YDL + * @date: 2020年5月19日 上午10:48:57 + */ +public class Iec104ThreadLocal { + + /** + * 定时发送启动链指令 测试链指令 + */ + private static ThreadLocal scheduledTaskPoolThreadLocal = new ThreadLocal<>(); + + /** + * 返回 发送序号 和接收序号 定时发送S帧 + */ + private static ThreadLocal controlPoolThreadLocal = new ThreadLocal<>(); + + /** + * 存放相关配置文件 + */ + private static ThreadLocal iec104ConfigThreadLocal = new ThreadLocal<>(); + + + + + public static void setScheduledTaskPool(ScheduledTaskPool scheduledTaskPool) { + scheduledTaskPoolThreadLocal.set(scheduledTaskPool); + } + + public static ScheduledTaskPool getScheduledTaskPool() { + return scheduledTaskPoolThreadLocal.get(); + } + + public static void setControlPool(ControlManageUtil controlPool) { + controlPoolThreadLocal.set(controlPool); + } + + public static ControlManageUtil getControlPool() { + return controlPoolThreadLocal.get(); + } + + + public static Iec104Config getIec104Conig() { + return iec104ConfigThreadLocal.get(); + } + + + public static void setIec104Config(Iec104Config iec104Confiig) { + iec104ConfigThreadLocal.set(iec104Confiig); + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/core/ScheduledTaskPool.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/core/ScheduledTaskPool.java new file mode 100644 index 0000000..c17afaf --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/core/ScheduledTaskPool.java @@ -0,0 +1,131 @@ +package com.yfd.platform.component.iec104.core; + +import cn.hutool.core.util.ObjUtil; +import com.yfd.platform.component.iec104.common.BasicInstruction104; +import com.yfd.platform.component.iec104.message.MessageDetail; +import com.yfd.platform.component.iec104.server.master.BootNettyClientChannel; +import com.yfd.platform.component.iec104.server.master.BootNettyClientChannelCache; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + + +/** + * 这是一个定时任务管理池 + * @ClassName: ScheduledTaskPool + * @Description: + * @author: YDL + * @date: 2020年5月19日 上午10:47:15 + */ +@Slf4j +public class ScheduledTaskPool { + + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class); + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); + /** + * 发送指令 + */ + private ChannelHandlerContext ctx; + + public ScheduledTaskPool(ChannelHandlerContext ctx) { + this.ctx = ctx; + } + + /** + * + * @Title: sendStatrFrame + * @Description: 发送启动帧 + */ + public void sendStartFrame() { + try { + BootNettyClientChannel Channel= BootNettyClientChannelCache.get(ctx.channel().id().asShortText()); + if(ObjUtil.isNotEmpty(Channel)){ + ctx.channel().writeAndFlush(BasicInstruction104.STARTDT); + String slave_ip= Channel.getCode(); + LOGGER.info(String.format("向从站[%s]发送启动链路指令!",slave_ip)); + } + } catch (Exception e) { + LOGGER.error("Exception caught", e); + } + } + + + /** + * + * @Title: startTestLinkTask + * @Description: 启动发送测试帧任务 + */ + public void startTestLinkTask() { + scheduler.scheduleAtFixedRate(this::sendTestFrame, 0, 30, TimeUnit.SECONDS); + } + + /** + * + * @Title: sendTestFrame + * @Description: 发送测试帧 + */ + private void sendTestFrame() { + Runnable runnable = () -> { + try { + BootNettyClientChannel Channel= BootNettyClientChannelCache.get(ctx.channel().id().asShortText()); + if(ObjUtil.isNotEmpty(Channel)){ + String slave_ip= Channel.getCode(); + LOGGER.info(String.format("向从站[%s]发送测试链路指令!",slave_ip)); + ctx.channel().writeAndFlush(BasicInstruction104.TESTFR); + //对时指令 + ctx.channel().writeAndFlush(BasicInstruction104.getTimeScale104()); + } + } catch (Exception e) { + LOGGER.error("Exception caught", e); + } + }; + CachedThreadPool.getCachedThreadPool().execute(runnable); + } + + + /** + * + * @Title: startGeneralCallTask + * @Description: 启动总召唤任务 + */ + public void startGeneralCallTask() { + scheduler.scheduleAtFixedRate(this::sendGeneralCall, 15, 45, TimeUnit.SECONDS); + } + + private void sendGeneralCall() { + Runnable runnable = () -> { + try { + BootNettyClientChannel channel = BootNettyClientChannelCache.get(ctx.channel().id().asShortText()); + if (ObjUtil.isNotEmpty(channel)) { + String slaveIp = channel.getCode(); + MessageDetail messageDetail = BasicInstruction104.getGeneralCallRuleDetail104(); + LOGGER.info(String.format("向从站[%s]发送总召唤指令[%s]!", slaveIp, messageDetail.getHexString())); + ctx.channel().writeAndFlush(messageDetail); + } + } catch (Exception e) { + LOGGER.error("Exception caught", e); + } + }; + CachedThreadPool.getCachedThreadPool().execute(runnable); + } + + public void stopSendCommandTask() { + scheduler.shutdown(); + try { + if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { + scheduler.shutdownNow(); + } + } catch (InterruptedException e) { + scheduler.shutdownNow(); + Thread.currentThread().interrupt(); // 保持中断状态 + } + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/enums/QualifiersEnum.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/enums/QualifiersEnum.java new file mode 100644 index 0000000..1960fc6 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/enums/QualifiersEnum.java @@ -0,0 +1,89 @@ +package com.yfd.platform.component.iec104.enums; + +import lombok.Getter; + +/** + * +* @ClassName: 限定词 +* @author YDL + */ +public enum QualifiersEnum { + + /** + * 总召唤限定词 + */ + generalCallQualifiers(TypeIdentifierEnum.generalCall, 0x20), + + /** + * 总召唤限定词 支持 老版的分组 + */ + generalCallGroupingQualifiers(TypeIdentifierEnum.generalCall, 0x14), + /** + * 复位进程限定词 + */ + resetPprocessQualifiers(TypeIdentifierEnum.resetPprocess, 0x01), + /** + * 初始化原因 当地电源合上 + */ + localCloseUpQualifiers(TypeIdentifierEnum.initEnd, 0x00), + /** + * 初始化原因 当地手动复位 + */ + localMmanualResetQualifiers(TypeIdentifierEnum.initEnd, 0x01), + /** + * 远方复位 + */ + distanceResetQualifiers(TypeIdentifierEnum.initEnd, 0x02), + /** + * 品质描述词 遥测 + */ + qualityQualifiers(null, 0x00), + /** + * 设置命令限定词 选择预置参数 1000 0000 + */ + prefabParameterQualifiers(null, 0x40), + /** + * 设置命令限定词 执行激活参数 + */ + activationParameterQualifiers(null, 0x0F); + + @Getter + private byte value; + @Getter + private TypeIdentifierEnum typeIdentifier; + + QualifiersEnum(TypeIdentifierEnum typeIdentifier, int value) { + this.value = (byte) value; + this.typeIdentifier = typeIdentifier; + } + + + /** + * 根据传输类型和 限定词的关系返回 限定词的类型 + * @param typeIdentifier + * @param value + * @return + */ + public static QualifiersEnum getQualifiersEnum(TypeIdentifierEnum typeIdentifier, byte value) { + for (QualifiersEnum type : QualifiersEnum.values()) { + if (type.getValue() == value && type.getTypeIdentifier() == typeIdentifier) { + return type; + } + } + // 品质描述词和设置参数 限定词对应多个值 所以需要做特殊处理 + QualifiersEnum qualifiersEnum = null; + if ((TypeIdentifierEnum.normalizedTelemetry.equals(typeIdentifier) + || TypeIdentifierEnum.scaledTelemetry.equals(typeIdentifier) + || TypeIdentifierEnum.shortFloatingPointTelemetry.equals(typeIdentifier)) + && qualityQualifiers.getValue() == value) { + qualifiersEnum = qualityQualifiers; + } + if ((TypeIdentifierEnum.readOneParameter.equals(typeIdentifier) + || TypeIdentifierEnum.readMultipleParameter.equals(typeIdentifier) + || TypeIdentifierEnum.prefabActivationMultipleParameter.equals(typeIdentifier)) + && qualityQualifiers.getValue() == value) { + qualifiersEnum = qualityQualifiers; + } + return qualifiersEnum; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/enums/TypeIdentifierEnum.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/enums/TypeIdentifierEnum.java new file mode 100644 index 0000000..eb30041 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/enums/TypeIdentifierEnum.java @@ -0,0 +1,133 @@ +package com.yfd.platform.component.iec104.enums; + +import lombok.Getter; + +/** + * +* @ClassName: TypeIdentifierEnum 类型标识 +* @author YDL +* @date 2020年5月13日 + */ +public enum TypeIdentifierEnum { + + /** + * 单点 摇信 1 + */ + onePointTeleindication(0x01, 1), + /** + * 双点 摇信 2 + */ + twoPointTeleindication(0x03, 1), + /** + * 测量值 归一化值 遥测 9 + */ + normalizedTelemetry(0x09,2), + /** + * 测量值 标度化值 遥测 11 + */ + scaledTelemetry(0x0B, 2), + /** + * 测量值 短浮点数 遥测 Short floating point 13 + */ + shortFloatingPointTelemetry(0x0D, 4), + + /** + * 摇信带时标 单点 30 + */ + onePointTimeTeleindication(0x1E, 1), + /** + * 摇信带时标 双点 31 + */ + twoPointTimeTeleindication(0x1F, 1), + /** + * 测量值 归一化值 带时标 遥测 9 + */ + normalizedTimeTelemetry(0x22,2), + /** + * 测量值 标度化值 带时标 遥测 11 + */ + scaledTimeTelemetry(0x23, 2), + /** + * 测量值 短浮点数 带时标 遥测 Short floating point 13 + */ + shortFloatingPointTimeTelemetry(0x24, 4), + /** + * 单命令 遥控 45 + */ + onePointTelecontrol(0x2D, 1), + /** + * 双命令遥控 46 + */ + twoPointTelecontrol(0x2E, 1), + /** + * 归一化值 遥调 48 + * + */ + normalizedTeleadjustment(0x30,2), + /** + * 标度化值 遥调 49 + */ + scaledTeleadjustment(0x31, 2), + /** + * 短浮点数 遥调 50 + */ + shortFloatingPointTeleadjustment(0x32, 4), + + /** + * 读单个参数 + */ + readOneParameter(0x66, 4), + /** + * 读多个参数 + */ + readMultipleParameter(0x84, 4), + + /** + * 预置多个个参数 + */ + prefabActivationMultipleParameter(0x88, 4), + /** + * 初始化结束 + */ + initEnd(0x46, 0), + /** + * 召唤命令 + */ + generalCall(0x64, 0), + /** + * 时钟同步 + */ + timeSynchronization(0x67, 0), + /** + * 复位进程 + */ + resetPprocess(0x69, 0); + + @Getter + private byte value; + @Getter + private int messageLength; + + TypeIdentifierEnum(int value, int messageLength) { + this.value = (byte) value; + this.messageLength = messageLength; + } + public static TypeIdentifierEnum getTypeIdentifierEnum(byte value) { + for (TypeIdentifierEnum type : TypeIdentifierEnum.values()) { + if (type.getValue() == value) { + return type; + } + } + return null; + } + + + public static boolean isTelemetry(TypeIdentifierEnum typeIdentifierEnum) { + return TypeIdentifierEnum.normalizedTelemetry == typeIdentifierEnum + ||TypeIdentifierEnum.scaledTelemetry == typeIdentifierEnum + ||TypeIdentifierEnum.shortFloatingPointTelemetry == typeIdentifierEnum + ||TypeIdentifierEnum.normalizedTimeTelemetry == typeIdentifierEnum + ||TypeIdentifierEnum.scaledTimeTelemetry == typeIdentifierEnum + ||TypeIdentifierEnum.shortFloatingPointTimeTelemetry == typeIdentifierEnum; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/enums/UControlEnum.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/enums/UControlEnum.java new file mode 100644 index 0000000..3ab33a7 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/enums/UControlEnum.java @@ -0,0 +1,43 @@ +package com.yfd.platform.component.iec104.enums; + +import lombok.Getter; + +/** + * U帧 基本指令 + * + */ +public enum UControlEnum { + /** + * 测试命令 + */ + TESTFR(0x43000000), + /** + * 测试确认指令 + */ + TESTFR_YES(0x83000000), + /** + * 停止指令 + */ + STOPDT(0x13000000), + /** + * 停止确认 + */ + STOPDT_YES(0x23000000), + /** + * 启动命令 + */ + STARTDT(0x07000000), + /** + * 启动确认命令 + */ + STARTDT_YES(0x0B000000); + + + @Getter + private int value; + + UControlEnum(int value) { + this.value = value; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/message/MessageDetail.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/message/MessageDetail.java new file mode 100644 index 0000000..d4839e4 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/message/MessageDetail.java @@ -0,0 +1,148 @@ +package com.yfd.platform.component.iec104.message; + +import com.ydl.iec.util.Iec104Util; +import com.yfd.platform.component.iec104.enums.QualifiersEnum; +import com.yfd.platform.component.iec104.enums.TypeIdentifierEnum; +import lombok.Data; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 一条报文对应的消息体 + * @author YDL + */ +@Data +public class MessageDetail { + + /** + * 启动字符 固定 一个字节 + */ + private byte start = 0x68; + + /** + * APUU 长度1个字节 + */ + private int apuuLength = 0; + + /** + * 控制域 四个字节 + */ + private byte[] control; + + + /** + * 类型标识 1字节 + */ + private TypeIdentifierEnum typeIdentifier; + + + /** + * 可变结构限定词 1个字节 + * true SQ = 0 true 数目number 是 信息对象的数目 + * false SQ = 1 单个对象的信息元素或者信息元素的集合的数目 + */ + + private boolean isContinuous; + + /** + * 消息长度 + */ + private int measgLength; + /** + * 传输原因 两个字节 + */ + private short transferReason; + + /** + * 终端地址 也就是应用服务数据单元公共地址 + */ + private short terminalAddress; + + /** + * 消息地址 字节 + */ + private int messageAddress; + + /** + * 消息结构 + */ + private List messages; + + + /** + * 判断是否有消息元素 + */ + private boolean isMessage; + /** + * 判断是否有限定词 + */ + private boolean isQualifiers; + /** + * 判断是否有时标 + */ + private boolean isTimeScaleExit; + + private QualifiersEnum qualifiersType; + /** + * + * 时标 + */ + private Date timeScale; + + /** + * 十六进制 字符串 + */ + private String hexString; + + public MessageDetail() { + } + + /** + * + * @param control 控制域 + * @param typeIdentifierEnum 类型标识 + * @param sq 0 地址不连续 1 地址连续 + * @param isTest 传输原因 0 未试验 1 试验 + * @param isPn 肯定确认 和否定确认 + * @param transferReason 传输原因 后六个比特位 + * @param terminalAddress 服务地址 + * @param messageAddress 消息地址 + * @param messages 消息列表 + * @param timeScale 时间 + * @param qualifiers 限定词 + * @return + */ + public MessageDetail(byte[] control, TypeIdentifierEnum typeIdentifierEnum, boolean sq, + boolean isTest, boolean isPn, short transferReason, short terminalAddress, int messageAddress, + List messages, Date timeScale, QualifiersEnum qualifiers) { + this.control = control; + this.typeIdentifier = typeIdentifierEnum; + this.isContinuous = sq; + this.measgLength = messages.size(); + this.transferReason = Iec104Util.getTransferReasonShort(isTest, isPn, transferReason); + this.messages = messages; + this.terminalAddress = terminalAddress; + this.timeScale = timeScale; + if (isContinuous) { + // 只有连续地址才会在次设置地址, + this.messageAddress = messageAddress; + } + if (timeScale != null) { + this.isTimeScaleExit = true; + } + this.qualifiersType = qualifiers; + } + + + /** + * U 帧或者S帧 + * @param control 控制域 + */ + public MessageDetail(byte[] control) { + this.control = control; + this.messages = new ArrayList<>(); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/message/MessageInfo.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/message/MessageInfo.java new file mode 100644 index 0000000..b650154 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/message/MessageInfo.java @@ -0,0 +1,48 @@ +package com.yfd.platform.component.iec104.message; + +import com.yfd.platform.component.iec104.enums.QualifiersEnum; +import lombok.Data; + +import java.util.Date; + +/** + * 报文中 的消息部分 + */ +@Data +public class MessageInfo { + /** + * 消息地址 字节 + */ + private int messageAddress; + + /** + * 信息元素集合 1 2 4 个字节 + */ + private byte[] messageInfos; + + /** + * 限定词 + */ + private QualifiersEnum qualifiersType; +// /** +// * +// * 时标 +// */ + private String timeScale; + + /** + * 消息详情 + */ + private int messageInfoLength; + + /** + * 遥信状态值 + */ + private int status; + + /** + * 遥测值 + */ + private float value; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104Master.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104Master.java new file mode 100644 index 0000000..d391b4d --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104Master.java @@ -0,0 +1,33 @@ +package com.yfd.platform.component.iec104.server; + +import com.yfd.platform.component.iec104.config.Iec104Config; +import com.yfd.platform.component.iec104.server.handler.DataHandler; + +/** + * 主站抽象类 + */ +public interface Iec104Master { + + /** + * 服务启动方法 + * @throws Exception + */ + void run() throws Exception; + + /** + * + * @Title: setDataHandler + * @Description: 设置数据处理类 + * @param dataHandler + */ + Iec104Master setDataHandler(DataHandler dataHandler); + + /** + * 设置配置文件 + * @param iec104Confiig + * @return + */ + Iec104Master setConfig(Iec104Config iec104Confiig); + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104MasterFactory.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104MasterFactory.java new file mode 100644 index 0000000..4e7b2a5 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104MasterFactory.java @@ -0,0 +1,26 @@ +package com.yfd.platform.component.iec104.server; + +import com.yfd.platform.component.iec104.server.master.Iec104TcpClientMaster; + +/** + * 主站 工厂类 + * @ClassName: Iec104MasterFactory + * @Description: IEC104规约主站 + * @author: YDL + * @date: 2020年5月19日 上午10:22:59 + */ +public class Iec104MasterFactory { + + + + /** + * @Title: createTcpClientMaster + * @Description: 创建一个TCM客户端的104主站 + * @param host 从机地址 + * @param port 端口 + * @return + */ + public static Iec104Master createTcpClientMaster(String host, int port) { + return new Iec104TcpClientMaster(host, port); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104Slave.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104Slave.java new file mode 100644 index 0000000..62433e2 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104Slave.java @@ -0,0 +1,34 @@ +package com.yfd.platform.component.iec104.server; + +import com.yfd.platform.component.iec104.config.Iec104Config; +import com.yfd.platform.component.iec104.server.handler.DataHandler; + +/** + * 从站抽象类 + */ +public interface Iec104Slave { + /** + * + * @Title: run + * @Description: 启动主机 + * @throws Exception + */ + void run() throws Exception; + + + /** + * + * @Title: setDataHandler + * @Description: 设置数据处理类 + * @param dataHandler + */ + Iec104Slave setDataHandler(DataHandler dataHandler); + + + /** + * 设置配置文件 + * @param iec104Config + * @return + */ + Iec104Slave setConfig(Iec104Config iec104Config); +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104SlaveFactory.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104SlaveFactory.java new file mode 100644 index 0000000..fd85349 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/Iec104SlaveFactory.java @@ -0,0 +1,24 @@ +package com.yfd.platform.component.iec104.server; + +import com.yfd.platform.component.iec104.server.slave.Iec104TcpServerSlave; + +/** + * + * @ClassName: Iec104SlaveFactory + * @Description: 104从机工厂 + * @author: YDL + * @date: 2020年5月19日 上午10:41:39 + */ +public class Iec104SlaveFactory { + + /** + * + * @Title: createTcpServerSlave + * @Description: 生产一个 iec104 协议TCP传输方式服务端做从机服务 + * @param port 端口 从机端口 + * @return Iec104Slave + */ + public static Iec104Slave createTcpServerSlave(int port) { + return new Iec104TcpServerSlave(port); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/BytesEncoder.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/BytesEncoder.java new file mode 100644 index 0000000..cc7a57c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/BytesEncoder.java @@ -0,0 +1,21 @@ +package com.yfd.platform.component.iec104.server.handler; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + + +/** + * 数组编码器 + * @author Admin + * + */ +public class BytesEncoder extends MessageToByteEncoder { + + + @Override + protected void encode(ChannelHandlerContext ctx, byte[] msg, ByteBuf out) throws Exception { + out.writeBytes(msg); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/ChannelHandler.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/ChannelHandler.java new file mode 100644 index 0000000..14e3db9 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/ChannelHandler.java @@ -0,0 +1,16 @@ +package com.yfd.platform.component.iec104.server.handler; + +import com.yfd.platform.component.iec104.message.MessageDetail; +import io.netty.channel.ChannelHandlerContext; + +/** + * + * @ClassName: ChannelHandler + * @Description: 处理数据 + * @author: YDL + * @date: 2020年5月19日 上午11:41:58 + */ +public interface ChannelHandler { + ChannelHandlerContext getCtx(); + void writeAndFlush(MessageDetail ruleDetail104); +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/ChannelHandlerImpl.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/ChannelHandlerImpl.java new file mode 100644 index 0000000..7416147 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/ChannelHandlerImpl.java @@ -0,0 +1,29 @@ +package com.yfd.platform.component.iec104.server.handler; + +import com.yfd.platform.component.iec104.message.MessageDetail; +import io.netty.channel.ChannelHandlerContext; + +/** + * + * @ClassName: ChannelHandlerImpl + * @Description: 实现一个自定义发现消息的类 + * @author: YDL + * @date: 2020年5月19日 上午11:47:16 + */ +public class ChannelHandlerImpl implements ChannelHandler { + + private ChannelHandlerContext ctx; + + public ChannelHandlerImpl(ChannelHandlerContext ctx) { + this.ctx = ctx; + } + @Override + public ChannelHandlerContext getCtx() { + return this.ctx; + } + @Override + public void writeAndFlush(MessageDetail ruleDetail104) { + ctx.channel().writeAndFlush(ruleDetail104); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/Check104Handler.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/Check104Handler.java new file mode 100644 index 0000000..4f3aa5b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/Check104Handler.java @@ -0,0 +1,51 @@ +package com.yfd.platform.component.iec104.server.handler; + +import com.yfd.platform.component.iec104.common.Iec104Constant; +import com.yfd.platform.component.iec104.server.master.BootNettyClientChannel; +import com.yfd.platform.component.iec104.server.master.BootNettyClientChannelCache; +import com.ydl.iec.util.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.util.ReferenceCountUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * +* @ClassName: Check104Handler +* @Description: 检查104报文 +* @author YDL +* @date 2020年5月13日 + */ +public class Check104Handler extends ChannelInboundHandlerAdapter { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class); + private static final int MAX_BUFFER_SIZE = 1024; // 缓存区大小 + + /** + * 拦截系统消息 + */ + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + ByteBuf result = (ByteBuf) msg; + // 检查ByteBuf中的数据长度是否超过最大缓冲区大小 + if (result.readableBytes() > MAX_BUFFER_SIZE) { + LOGGER.error("接收的数据长度是否超过最大缓冲区大小!"); + return; + } + + byte[] bytes = new byte[result.readableBytes()]; + result.readBytes(bytes); + BootNettyClientChannel Channel= BootNettyClientChannelCache.get(ctx.channel().id().asShortText()); + String slave_ip= Channel.getCode(); + LOGGER.info( String.format("接收到从站[%s]发送的报文: %s", slave_ip,ByteUtil.byteArrayToHexString(bytes) )); + if (bytes.length < Iec104Constant.APCI_LENGTH || bytes[0] != Iec104Constant.HEAD_DATA) { + LOGGER.error("报文无效"); + ReferenceCountUtil.release(result); + } else { + result.writeBytes(bytes); + ctx.fireChannelRead(msg); + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/DataDecoder.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/DataDecoder.java new file mode 100644 index 0000000..cfb0023 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/DataDecoder.java @@ -0,0 +1,34 @@ +package com.yfd.platform.component.iec104.server.handler; + +import com.yfd.platform.component.iec104.core.Iec104ThreadLocal; +import com.ydl.iec.util.Iec104Util; +import com.yfd.platform.component.iec104.core.Decoder104; +import com.yfd.platform.component.iec104.message.MessageDetail; +import com.ydl.iec.util.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.ByteToMessageDecoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + + +/** + * 解码器 + * @author Admin + * + */ +public class DataDecoder extends ByteToMessageDecoder { + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + byte[] data = new byte[in.readableBytes()]; + in.readBytes(data); + short send = Iec104Util.getSend(ByteUtil.getByte(data, 2, 4)); + Iec104ThreadLocal.getControlPool().setAccept(send); + MessageDetail ruleDetail104 = Decoder104.encoder(data); + out.add(ruleDetail104); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/DataEncoder.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/DataEncoder.java new file mode 100644 index 0000000..26ea196 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/DataEncoder.java @@ -0,0 +1,48 @@ +package com.yfd.platform.component.iec104.server.handler; + + +import com.yfd.platform.component.iec104.core.Iec104ThreadLocal; +import com.ydl.iec.util.Iec104Util; +import com.yfd.platform.component.iec104.core.Encoder104; +import com.yfd.platform.component.iec104.message.MessageDetail; +import com.ydl.iec.util.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import lombok.extern.slf4j.Slf4j; + +/** + * 编码器 + * @author Admin + * + */ +@Slf4j +public class DataEncoder extends MessageToByteEncoder { + + + @Override + protected void encode(ChannelHandlerContext ctx, MessageDetail msg, ByteBuf out) throws Exception { + try { + byte[] bytes = Encoder104.encoder(msg); + short accept = Iec104ThreadLocal.getControlPool().getAccept(); + short send = Iec104ThreadLocal.getControlPool().getSend(); + short terminalAddress = Iec104ThreadLocal.getIec104Conig().getTerminnalAddress(); + // 替换终端地址 发送序号和接收序号 + byte[] terminalAddressBytes = Iec104Util.getTerminalAddressByte(terminalAddress); + byte[] icontrol = Iec104Util.getIcontrol(accept, send); + for (int i = 0; i < icontrol.length; i++) { + bytes[i + 2] = icontrol[i]; + } + bytes[10] = terminalAddressBytes[0]; + bytes[11] = terminalAddressBytes[1]; + String hexString=ByteUtil.byteArrayToHexString(bytes); + log.info(hexString); + out.writeBytes(bytes); + + } catch (Exception e) { + log.error("", e); + } + + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/DataHandler.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/DataHandler.java new file mode 100644 index 0000000..13dae58 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/DataHandler.java @@ -0,0 +1,32 @@ +package com.yfd.platform.component.iec104.server.handler; + +import com.yfd.platform.component.iec104.message.MessageDetail; + +/** + * + * @ClassName: DataHandler + * @Description: 数据处理 + * @author: YDL + * @date: 2020年5月19日 上午11:27:04 + */ +public interface DataHandler { + + /** + * @param ctx + * @throws Exception + * @Title: handlerAdded + * @Description: 建立连接 + */ + void handlerAdded(ChannelHandler ctx) throws Exception; + + /** + * @param ctx + * @param ruleDetail104 + * @throws Exception + * @Title: channelRead0 + * @Description: 收到消息 + */ + void channelRead(ChannelHandler ctx, MessageDetail ruleDetail104) throws Exception; +} + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/SysSframeHandler.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/SysSframeHandler.java new file mode 100644 index 0000000..d0758ee --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/SysSframeHandler.java @@ -0,0 +1,62 @@ +package com.yfd.platform.component.iec104.server.handler; + +import com.yfd.platform.component.iec104.common.Iec104Constant; +import com.ydl.iec.util.Iec104Util; +import com.ydl.iec.util.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.util.ReferenceCountUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * +* @ClassName: SysSframeInboundHandler +* @Description: 处理S帧的问题 +* @author YDL +* @date 2020年5月13日 + */ +public class SysSframeHandler extends ChannelInboundHandlerAdapter { + + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class); + + /** + * 拦截系统消息 + */ + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + ByteBuf result = (ByteBuf) msg; + byte[] bytes = new byte[result.readableBytes()]; + result.readBytes(bytes); + if (isSysInstruction(bytes)) { + LOGGER.info("收到S帧" + Iec104Util.getAccept(ByteUtil.getByte(bytes, 2, 4))); + ReferenceCountUtil.release(result); + return; + } + result.writeBytes(bytes); +// LOGGER.info("普通指令"); + ctx.fireChannelRead(result); + } + + /** + * + * @Title: isSysInstruction + * @Description: TODO 判断是否是 系统报文 + * @param @param bytes + * @param @return + * @return boolean + * @throws + */ + private boolean isSysInstruction(byte[] bytes) { + if (bytes.length != Iec104Constant.APCI_LENGTH) { + return false; + } + if (bytes[Iec104Constant.ACCEPT_LOW_INDEX] == 1 && bytes[Iec104Constant.ACCEPT_HIGH_INDEX] == 0) { + // 判断S帧的方法 + return true; + } + // U帧只有6字节 + return false; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/Unpack104Handler.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/Unpack104Handler.java new file mode 100644 index 0000000..65254bf --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/handler/Unpack104Handler.java @@ -0,0 +1,52 @@ +package com.yfd.platform.component.iec104.server.handler; + +import com.yfd.platform.component.iec104.common.Iec104Constant; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; + +import java.util.List; + +/** + * +* @ClassName: Unpack104Util +* @Description: 解决TCP 拆包和沾包的问题 +* @author YDL +* @date 2020年5月13日 + */ +public class Unpack104Handler extends ByteToMessageDecoder { + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List out) throws Exception { + // 记录包头开始的index + int beginReader; + int newDataLength = 0; + while (true) { + // 获取包头开始的index + beginReader = buffer.readerIndex(); + // 记录一个标志用于重置 + buffer.markReaderIndex(); + // 读到了协议的开始标志,结束while循环 + if (buffer.readByte() == Iec104Constant.HEAD_DATA) { + // 标记当前包为新包 + //读取包长度 + byte newDataLengthByte = buffer.readByte(); + newDataLength = newDataLengthByte & 0xFF; + break; + } + continue; + } + + if (buffer.readableBytes() < newDataLength) { + buffer.readerIndex(beginReader); + return; + } + + newDataLength = newDataLength + 2; + //恢复指针 + buffer.readerIndex(beginReader); + ByteBuf data = buffer.readBytes(newDataLength); + out.add(data); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/BootNettyClientChannel.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/BootNettyClientChannel.java new file mode 100644 index 0000000..0322b52 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/BootNettyClientChannel.java @@ -0,0 +1,62 @@ +package com.yfd.platform.component.iec104.server.master; + +import com.yfd.platform.component.iec104.core.ScheduledTaskPool; +import io.netty.channel.Channel; +import io.netty.channel.EventLoopGroup; + +/** + * 建立channel保存多客户端BootNettyClientChannel + */ +public class BootNettyClientChannel { + + // 连接客户端唯一的code + private String code; + + // 客户端最新发送的消息内容 + private String last_data; + + private transient volatile Channel channel; + + // 连接客户端资源组 + private transient volatile EventLoopGroup group; + + private transient volatile ScheduledTaskPool scheduledTaskPool; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public Channel getChannel() { + return channel; + } + + public void setChannel(Channel channel) { + this.channel = channel; + } + + public EventLoopGroup getGroup() { + return group; + } + public void setGroup(EventLoopGroup group) { + this.group = group; + } + + public ScheduledTaskPool getScheduledTaskPool() { + return scheduledTaskPool; + } + public void setScheduledTaskPool(ScheduledTaskPool scheduledTaskPool) { + this.scheduledTaskPool = scheduledTaskPool; + } + + public String getLast_data() { + return last_data; + } + + public void setLast_data(String last_data) { + this.last_data = last_data; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/BootNettyClientChannelCache.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/BootNettyClientChannelCache.java new file mode 100644 index 0000000..dd7ee7d --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/BootNettyClientChannelCache.java @@ -0,0 +1,32 @@ +package com.yfd.platform.component.iec104.server.master; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * TCP通道缓存 + */ +public class BootNettyClientChannelCache { + + public static volatile Map channelMapCache = new ConcurrentHashMap(); + + public static void add(String code, BootNettyClientChannel channel){ + channelMapCache.put(code,channel); + } + + public static BootNettyClientChannel get(String code){ + return channelMapCache.get(code); + } + + public static void remove(String code){ + channelMapCache.remove(code); + } + + public static void save(String code, BootNettyClientChannel channel) { + if(channelMapCache.get(code) == null) { + add(code,channel); + } + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/EventLoopGroupManager.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/EventLoopGroupManager.java new file mode 100644 index 0000000..4411f9a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/EventLoopGroupManager.java @@ -0,0 +1,40 @@ +package com.yfd.platform.component.iec104.server.master; + +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; + +import java.util.HashMap; +import java.util.Map; + +public class EventLoopGroupManager { + private static final Map eventLoopGroups = new HashMap<>(); + + public static EventLoopGroup createEventLoopGroup(String key) { + EventLoopGroup group = new NioEventLoopGroup(); + eventLoopGroups.put(key, group); + return group; + } + + public static EventLoopGroup getEventLoopGroup(String key) { + EventLoopGroup group = eventLoopGroups.get(key); + return group; + + } + + public static void shutdownEventLoopGroup(String key) { + EventLoopGroup group = eventLoopGroups.get(key); + if (group != null) { + group.shutdownGracefully(); + eventLoopGroups.remove(key); + } + } + + // 如果需要手动关闭所有的EventLoopGroup + public static void shutdownAllEventLoopGroups() { + for (EventLoopGroup group : eventLoopGroups.values()) { + group.shutdownGracefully(); + } + eventLoopGroups.clear(); + } +} + diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/Iec104ClientInitializer.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/Iec104ClientInitializer.java new file mode 100644 index 0000000..197fb25 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/Iec104ClientInitializer.java @@ -0,0 +1,59 @@ +package com.yfd.platform.component.iec104.server.master; + +import com.yfd.platform.component.iec104.config.DefaultIec104Config; +import com.yfd.platform.component.iec104.config.Iec104Config; +import com.yfd.platform.component.iec104.core.Iec104ThreadLocal; +import com.yfd.platform.component.iec104.server.handler.*; +import com.yfd.platform.component.iec104.server.master.handler.Iec104ClientHandler; +import com.yfd.platform.component.iec104.server.master.handler.SysUframeClientHandler; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import lombok.Setter; +import lombok.experimental.Accessors; + +/** + * +* @ClassName: Iec104ServerInitializer +* @Description: 104协议 处理链 +* @author YDL +* @date 2020年5月13日 + */ +@Setter +@Accessors(chain = true) +public class Iec104ClientInitializer extends ChannelInitializer { + + + private DataHandler dataHandler; + + private Iec104Config iec104Config; + + /** + * 初始化处理链 + */ + @Override + public void initChannel(SocketChannel ch) throws Exception { + if (iec104Config != null) { + Iec104ThreadLocal.setIec104Config(iec104Config); + } else { + Iec104ThreadLocal.setIec104Config(new DefaultIec104Config()); + } + ChannelPipeline pipeline = ch.pipeline(); + // 沾包拆包工具 + pipeline.addLast("unpack", new Unpack104Handler()); + // 数据检查工具 + pipeline.addLast("check", new Check104Handler()); +// /拦截 U帧处理器 + pipeline.addLast("uframe", new SysUframeClientHandler()); + //拦截 S帧处理器 + pipeline.addLast("sframe", new SysSframeHandler()); + //编码器 + pipeline.addLast("byteencoder", new BytesEncoder()); + //编码器 + pipeline.addLast("encoder", new DataEncoder()); + +// 解码器 + pipeline.addLast("decoder", new DataDecoder()); + pipeline.addLast("handler", new Iec104ClientHandler(dataHandler)); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/Iec104TcpClientMaster.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/Iec104TcpClientMaster.java new file mode 100644 index 0000000..53dfbfb --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/Iec104TcpClientMaster.java @@ -0,0 +1,67 @@ +package com.yfd.platform.component.iec104.server.master; + + +import com.yfd.platform.component.iec104.config.Iec104Config; +import com.yfd.platform.component.iec104.server.Iec104Master; +import com.yfd.platform.component.iec104.server.handler.DataHandler; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import io.netty.channel.ChannelInboundHandlerAdapter; +/** + * + * @ClassName: Iec104TcpClientMaster + * @Description: 104 TCP 客户端 主机 + * @author: YDL + * @date: 2020年5月19日 上午10:44:35 + */ +public class Iec104TcpClientMaster implements Iec104Master { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class); + + private int port; + private String host; + private DataHandler dataHandler; + private Iec104Config iec104Config; + private EventLoopGroup group; + + public Iec104TcpClientMaster(String host, int port) { + this.port = port; + this.host = host; + this.group = EventLoopGroupManager.createEventLoopGroup(host); + } + + @Override + public void run() throws Exception { + try { + Bootstrap bs = new Bootstrap(); + bs.group(group) + .channel(NioSocketChannel.class) + .option(ChannelOption.SO_KEEPALIVE, true) + .handler(new Iec104ClientInitializer().setDataHandler(dataHandler).setIec104Config(iec104Config)); + Channel channel = bs.connect(host, port).sync().channel(); + channel.closeFuture().sync(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.error("Exception caught", e); + } + + } + + @Override + public Iec104Master setDataHandler(DataHandler dataHandler) { + this.dataHandler = dataHandler; + return this; + } + + @Override + public Iec104Master setConfig(Iec104Config iec104Config) { + this.iec104Config = iec104Config; + return this; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/handler/Iec104ClientHandler.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/handler/Iec104ClientHandler.java new file mode 100644 index 0000000..95d97db --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/handler/Iec104ClientHandler.java @@ -0,0 +1,98 @@ +package com.yfd.platform.component.iec104.server.master.handler; + +import cn.hutool.core.util.ObjUtil; +import com.yfd.platform.component.iec104.core.CachedThreadPool; +import com.yfd.platform.component.iec104.core.ControlManageUtil; +import com.yfd.platform.component.iec104.core.Iec104ThreadLocal; +import com.yfd.platform.component.iec104.core.ScheduledTaskPool; +import com.yfd.platform.component.iec104.message.MessageDetail; +import com.yfd.platform.component.iec104.server.handler.ChannelHandlerImpl; +import com.yfd.platform.component.iec104.server.handler.DataHandler; +import com.yfd.platform.component.iec104.server.master.BootNettyClientChannel; +import com.yfd.platform.component.iec104.server.master.BootNettyClientChannelCache; +import com.yfd.platform.component.iec104.server.master.EventLoopGroupManager; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.SimpleChannelInboundHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +public class Iec104ClientHandler extends SimpleChannelInboundHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class); + + + private DataHandler dataHandler; + + public Iec104ClientHandler(DataHandler dataHandler) { + this.dataHandler = dataHandler; + } + + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + //------------获取当前激活的从站连接,添加到全局列表中 -----------------// + BootNettyClientChannel Channel= new BootNettyClientChannel(); + String slave_ip=Iec104ThreadLocal.getIec104Conig().getSlaveIP(); + Channel.setCode(slave_ip); + Channel.setChannel(ctx.channel()); + BootNettyClientChannelCache.save(ctx.channel().id().asShortText(),Channel); + //------------------------添加完成------------------------------// + + // 启动成功后一直发启动链路命令 + ScheduledTaskPool scheduledTaskPool=new ScheduledTaskPool(ctx) ; + Iec104ThreadLocal.setScheduledTaskPool(scheduledTaskPool); + Iec104ThreadLocal.getScheduledTaskPool().sendStartFrame(); + Iec104ThreadLocal.setControlPool(new ControlManageUtil(ctx).setFrameAmountMax(Iec104ThreadLocal.getIec104Conig().getFrameAmountMax())); + Iec104ThreadLocal.getControlPool().startSendFrameTask(); + + Channel.setScheduledTaskPool(scheduledTaskPool); + BootNettyClientChannelCache.remove(ctx.channel().id().asShortText()); + BootNettyClientChannelCache.save(ctx.channel().id().asShortText(),Channel); + + if (dataHandler != null) { + CachedThreadPool.getCachedThreadPool().execute(new Runnable() { + @Override + public void run() { + try { + dataHandler.handlerAdded(new ChannelHandlerImpl(ctx)); + } catch (Exception e) { + LOGGER.error("Exception caught", e); + } + } + }); + } + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + //关闭通道时移除 + BootNettyClientChannel bootNettyChannel = BootNettyClientChannelCache.get(ctx.channel().id().asShortText()); + if(ObjUtil.isNotEmpty(bootNettyChannel)){ + bootNettyChannel.getScheduledTaskPool().stopSendCommandTask(); + EventLoopGroupManager.shutdownEventLoopGroup(bootNettyChannel.getCode()); + BootNettyClientChannelCache.remove(ctx.channel().id().asShortText()); + LOGGER.info(bootNettyChannel.getCode()+"的连接被设备关闭!"); + } + } + + @Override + public void channelRead0(ChannelHandlerContext ctx, MessageDetail ruleDetail104) throws IOException { + if (dataHandler != null) { + CachedThreadPool.getCachedThreadPool().execute(new Runnable() { + @Override + public void run() { + try { + dataHandler.channelRead(new ChannelHandlerImpl(ctx), ruleDetail104); + } catch (Exception e) { + // TODO Auto-generated catch block + LOGGER.error("Exception caught", e); + } + } + }); + } + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/handler/SysUframeClientHandler.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/handler/SysUframeClientHandler.java new file mode 100644 index 0000000..f392fca --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/master/handler/SysUframeClientHandler.java @@ -0,0 +1,92 @@ +package com.yfd.platform.component.iec104.server.master.handler; + +import com.yfd.platform.component.iec104.core.Iec104ThreadLocal; +import com.yfd.platform.component.iec104.enums.UControlEnum; +import com.yfd.platform.component.iec104.server.master.BootNettyClientChannel; +import com.yfd.platform.component.iec104.server.master.BootNettyClientChannelCache; +import com.ydl.iec.util.ByteUtil; +import com.ydl.iec.util.Iec104Util; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * +* @ClassName: SysUframeInboundHandler +* @Description: 处理U帧的报文 +* @author YDL +* @date 2020年5月13日 + */ +public class SysUframeClientHandler extends ChannelInboundHandlerAdapter { + + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class); + + /** + * 拦截系统消息 + */ + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + ByteBuf result = (ByteBuf) msg; + byte[] bytes = new byte[result.readableBytes()]; + result.readBytes(bytes); + if (isSysInstruction(bytes)) { + UControlEnum uControlEnum = Iec104Util.getUcontrol(ByteUtil.getByte(bytes, 2, 4)); + if (uControlEnum != null) { + uInstructionHandler(ctx, result, uControlEnum); + if(uControlEnum != UControlEnum.TESTFR_YES){//放行测试确认帧(不拦截) zhengsl added at 20240517 + return; + } + } + } + + result.writeBytes(bytes); + ctx.fireChannelRead(result); + } + + /** + * + * @Title: isSysInstruction + * @Description: TODO 判断是否是 系统报文 + * @param @param bytes + * @param @return + * @return boolean + * @throws + */ + private boolean isSysInstruction(byte[] bytes) { + // U帧只有6字节 + return bytes.length == 6; + } + + /** + * + * @Title: uInstructionHandler + * @Description: 处理U帧 + * @param ctx + * @param result + * @param uControlEnum + * @return void + * @throws + */ + private void uInstructionHandler(ChannelHandlerContext ctx, ByteBuf result, UControlEnum uControlEnum) { + result.readBytes(new byte[result.readableBytes()]); + BootNettyClientChannel Channel= BootNettyClientChannelCache.get(ctx.channel().id().asShortText()); + String slave_ip= Channel.getCode(); + if (uControlEnum == UControlEnum.TESTFR_YES) { + LOGGER.info(String.format("接收到从站[%s]的测试确认指令!",slave_ip)); + } else if (uControlEnum == UControlEnum.STOPDT_YES) { + LOGGER.info(String.format("接收到从站[%s]的停止确认指令!",slave_ip)); + } + else if (uControlEnum == UControlEnum.TESTFR) { + LOGGER.info(String.format("接收到从站[%s]的发出的测试命令!",slave_ip)); + } + else if (uControlEnum == UControlEnum.STARTDT_YES) { + LOGGER.info(String.format("接收到从站[%s]的启动指令确认指令!",slave_ip)); + Iec104ThreadLocal.getScheduledTaskPool().startTestLinkTask(); + Iec104ThreadLocal.getScheduledTaskPool().startGeneralCallTask(); + } else { + LOGGER.error("U报文无效:"+uControlEnum); + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/Iec104ServerInitializer.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/Iec104ServerInitializer.java new file mode 100644 index 0000000..6637c04 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/Iec104ServerInitializer.java @@ -0,0 +1,62 @@ +package com.yfd.platform.component.iec104.server.slave; + +import com.yfd.platform.component.iec104.config.DefaultIec104Config; +import com.yfd.platform.component.iec104.config.Iec104Config; +import com.yfd.platform.component.iec104.core.Iec104ThreadLocal; +import com.yfd.platform.component.iec104.server.Iec104Slave; +import com.yfd.platform.component.iec104.server.handler.*; +import com.yfd.platform.component.iec104.server.slave.handler.Iec104TcpSlaveHandler; +import com.yfd.platform.component.iec104.server.slave.handler.SysUframeServerHandler; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import lombok.Setter; +import lombok.experimental.Accessors; + +/** + * +* @ClassName: Iec104ServerInitializer +* @Description: 104协议 处理链 +* @author YDL +* @date 2020年5月13日 + */ +@Setter +@Accessors(chain = true) +public class Iec104ServerInitializer extends ChannelInitializer { + + private DataHandler dataHandler; + + + private Iec104Config iec104Config; + + /** + * 初始化处理链 + */ + @Override + public void initChannel(SocketChannel ch) { + if (iec104Config != null) { + Iec104ThreadLocal.setIec104Config(iec104Config); + } else { + Iec104ThreadLocal.setIec104Config(new DefaultIec104Config()); + } + ChannelPipeline pipeline = ch.pipeline(); + // 沾包拆包工具 + pipeline.addLast("unpack", new Unpack104Handler()); + // 数据检查工具 + pipeline.addLast("check", new Check104Handler()); + // byte[] 编码器 + pipeline.addLast("bytesEncoder", new BytesEncoder()); + + //编码器 将对象编码成 字节码 + pipeline.addLast("encoder", new DataEncoder()); + + //拦截 U帧处理器 + pipeline.addLast("uframe", new SysUframeServerHandler()); + //拦截 S帧处理器 + pipeline.addLast("sframe", new SysSframeHandler()); + //解码器 + pipeline.addLast("decoder", new DataDecoder()); + // 具体的处理器 + pipeline.addLast("handler", new Iec104TcpSlaveHandler(dataHandler)); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/Iec104TcpServerSlave.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/Iec104TcpServerSlave.java new file mode 100644 index 0000000..8f92243 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/Iec104TcpServerSlave.java @@ -0,0 +1,85 @@ +package com.yfd.platform.component.iec104.server.slave; + +import com.yfd.platform.component.iec104.config.DefaultIec104Config; +import com.yfd.platform.component.iec104.config.Iec104Config; +import com.yfd.platform.component.iec104.core.Iec104ThreadLocal; +import com.yfd.platform.component.iec104.server.Iec104Slave; +import com.yfd.platform.component.iec104.server.handler.DataHandler; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; + + +/** + * iec 104 TCP 协议 服务端做从机 + * @author Admin + * + */ +public class Iec104TcpServerSlave implements Iec104Slave { + + private int port; + private Iec104Config iec104Config; + + + private DataHandler dataHandler; + + public Iec104TcpServerSlave(int port) { + this.port = port; + } + + /** + * 启动 从机 + */ + @Override + public void run() throws Exception { + + Iec104ThreadLocal.setIec104Config(iec104Config); + //1 EventLoopGroup是一个线程组,它包含了一组nio线程,专门用于网络事件的处理,实际上他们就是Reactor线程组 + EventLoopGroup bossGroup = new NioEventLoopGroup(); +// 这 里创建2个的原因是一个用于服务端接收客户的连接,另一个用于SockentChannel的网络读写。 + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + // 1 + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + // (3) NIO + .channel(NioServerSocketChannel.class) + //(4) + .childHandler(getIec104ServerInitializer()) + //(5) 请求的队列的最大长度 + .option(ChannelOption.SO_BACKLOG, 128) + // (6) 保持长连接 + .childOption(ChannelOption.SO_KEEPALIVE, true); + //7绑定端口,开始接收进来的连接 + ChannelFuture f = b.bind(port).sync(); + // 等待服务器 socket 关闭 。 + // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。 + f.channel().closeFuture().sync(); + } finally { + workerGroup.shutdownGracefully(); + bossGroup.shutdownGracefully(); + } + } + + private Iec104ServerInitializer getIec104ServerInitializer() { + Iec104ServerInitializer iec104ServerInitializer = new Iec104ServerInitializer(); + iec104ServerInitializer.setDataHandler(dataHandler).setIec104Config(iec104Config); + return iec104ServerInitializer; + } + + + @Override + public Iec104Slave setDataHandler(DataHandler dataHandler) { + this.dataHandler = dataHandler; + return this; + } + + @Override + public Iec104Slave setConfig(Iec104Config iec104Config) { + this.iec104Config = iec104Config; + return this; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/handler/Iec104TcpSlaveHandler.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/handler/Iec104TcpSlaveHandler.java new file mode 100644 index 0000000..0a24d8b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/handler/Iec104TcpSlaveHandler.java @@ -0,0 +1,108 @@ +package com.yfd.platform.component.iec104.server.slave.handler; + + +import com.yfd.platform.component.iec104.core.CachedThreadPool; +import com.yfd.platform.component.iec104.core.ControlManageUtil; +import com.yfd.platform.component.iec104.core.Iec104ThreadLocal; +import com.yfd.platform.component.iec104.message.MessageDetail; +import com.yfd.platform.component.iec104.server.handler.ChannelHandlerImpl; +import com.yfd.platform.component.iec104.server.handler.DataHandler; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import io.netty.channel.ChannelInboundHandlerAdapter; + +/** + * +* @ClassName: Iec104TcpSlaveHandler +* @Description: 从机处理类 +* @author YDL +* @date 2020年5月13日 + */ +public class Iec104TcpSlaveHandler extends SimpleChannelInboundHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class); + + private DataHandler dataHandler; + + + public Iec104TcpSlaveHandler(DataHandler dataHandler) { + this.dataHandler = dataHandler; + } + + /** + * 新连接 + */ + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + Iec104ThreadLocal.setControlPool(new ControlManageUtil(ctx).setFrameAmountMax(Iec104ThreadLocal.getIec104Conig().getFrameAmountMax())); + /** + * 启动 + */ + Iec104ThreadLocal.getControlPool().startSendFrameTask(); + if (dataHandler != null) { + Runnable runnable = () -> { + try { + dataHandler.handlerAdded(new ChannelHandlerImpl(ctx)); + } catch (Exception e) { + LOGGER.error("Exception caught", e); + } + }; + + CachedThreadPool.getCachedThreadPool().execute(runnable); + } + } + + /** + * 断开连接 + */ + @Override + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + } + + /** + * 收到消息 + */ + @Override + protected void channelRead0(ChannelHandlerContext ctx, MessageDetail ruleDetail104) throws Exception { + if (dataHandler != null) { + Runnable runnable = () -> { + try { + dataHandler.channelRead(new ChannelHandlerImpl(ctx), ruleDetail104); + } catch (Exception e) { + LOGGER.error("Exception caught", e); + } + }; + CachedThreadPool.getCachedThreadPool().execute(runnable); + } + } + + /** + * 连接 + * + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + Channel incoming = ctx.channel(); + LOGGER.error("SimpleChatClient:" + incoming.remoteAddress() + "在线"); + } + + /** + * 关闭 + */ + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + Channel incoming = ctx.channel(); + LOGGER.error("SimpleChatClient:" + incoming.remoteAddress() + "掉线"); + } + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + Channel incoming = ctx.channel(); + LOGGER.error("SimpleChatClient:" + incoming.remoteAddress() + "异常"); + // 当出现异常就关闭连接 + //cause.printStackTrace(); + LOGGER.error("Exception caught", cause); + ctx.close(); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/handler/SysUframeServerHandler.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/handler/SysUframeServerHandler.java new file mode 100644 index 0000000..55cb175 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/server/slave/handler/SysUframeServerHandler.java @@ -0,0 +1,89 @@ +package com.yfd.platform.component.iec104.server.slave.handler; + +import com.yfd.platform.component.iec104.common.BasicInstruction104; +import com.ydl.iec.util.Iec104Util; +import com.yfd.platform.component.iec104.enums.UControlEnum; +import com.ydl.iec.util.ByteUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * +* @ClassName: SysUframeInboundHandler +* @Description: 处理U帧的报文 +* @author YDL +* @date 2020年5月13日 + */ +public class SysUframeServerHandler extends ChannelInboundHandlerAdapter { + + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class); + + /** + * 拦截系统消息 + */ + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + ByteBuf result = (ByteBuf) msg; + byte[] bytes = new byte[result.readableBytes()]; + result.readBytes(bytes); + if (isSysInstruction(bytes)) { + UControlEnum uControlEnum = Iec104Util.getUcontrol(ByteUtil.getByte(bytes, 2, 4)); + if (uControlEnum != null) { + uInstructionHandler(ctx, result, uControlEnum); + return; + } + } + result.writeBytes(bytes); +// LOGGER.info("普通指令"); + ctx.fireChannelRead(result); + } + + /** + * + * @Title: isSysInstruction + * @Description: TODO 判断是否是 系统报文 + * @param @param bytes + * @param @return + * @return boolean + * @throws + */ + private boolean isSysInstruction(byte[] bytes) { + // U帧只有6字节 + return bytes.length == 6; + } + + /** + * + * @Title: uInstructionHandler + * @Description: 处理U帧 + * @param ctx + * @param result + * @param uControlEnum + * @return void + * @throws + */ + private void uInstructionHandler(ChannelHandlerContext ctx, ByteBuf result, UControlEnum uControlEnum) { + result.readBytes(new byte[result.readableBytes()]); + byte[] resultBytes = null; + if (uControlEnum == UControlEnum.TESTFR) { + LOGGER.error("收到测试指令"); + resultBytes = BasicInstruction104.TESTFR_YES; + } else if (uControlEnum == UControlEnum.STOPDT) { + LOGGER.error("收到停止指令"); + resultBytes = BasicInstruction104.STOPDT_YES; + } else if (uControlEnum == UControlEnum.STARTDT) { + LOGGER.error("收到启动指令"); + resultBytes = BasicInstruction104.STARTDT_YES; + } else { + LOGGER.error("U报文无效" + uControlEnum); + } + if (resultBytes != null) { + result.writeBytes(resultBytes); + LOGGER.error("回复U报"); + ctx.writeAndFlush(result); + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/util/ByteUtil.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/util/ByteUtil.java new file mode 100644 index 0000000..0a84074 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/util/ByteUtil.java @@ -0,0 +1,247 @@ +package com.ydl.iec.util; + +import java.io.ByteArrayOutputStream; +import java.util.Calendar; +import java.util.Date; + +/** + * +* @ClassName: ByteUtil +* @Description: byte 工具类 +* @author YDL +* @date 2020年5月13日 + */ +public class ByteUtil { + + /** + * + * @Title: intToByteArray + * @Description: int 转换成 byte数组 + * @param @param i + * @param @return + * @return byte[] + * @throws + */ + public static byte[] intToByteArray(int i) { + byte[] result = new byte[4]; + result[0] = (byte) ((i >> 24) & 0xFF); + result[1] = (byte) ((i >> 16) & 0xFF); + result[2] = (byte) ((i >> 8) & 0xFF); + result[3] = (byte) (i & 0xFF); + return result; + } + + /** + * @Title: shortToByteArray + * @Description: short 转换成 byte[] + * @param @param val + * @param @return + * @return byte[] + * @throws + */ + public static byte[] shortToByteArray(short val) { + byte[] b = new byte[2]; + b[0] = (byte) ((val >> 8) & 0xff); + b[1] = (byte) (val & 0xff); + return b; + } + + /** + * + * @Title: byteArrayToInt + * @Description: byte[] 转换成 int + * @param @param bytes + * @param @return + * @return int + * @throws + */ + public static int byteArrayToInt(byte[] bytes) { + int value = 0; + for (int i = 0; i < 4; i++) { + int shift = (3 - i) * 8; + value += (bytes[i] & 0xFF) << shift; + } + return value; + } + + /** + * + * @Title: byteArrayToShort + * @Description: byte[] 转换成short + * @param @param bytes + * @param @return + * @return short + * @throws + */ + public static short byteArrayToShort(byte[] bytes) { + short value = 0; + for (int i = 0; i < 2; i++) { + int shift = (1 - i) * 8; + value += (bytes[i] & 0xFF) << shift; + } + return value; + } + + +// /** +// * +// * @Title: listToBytes +// * @Description: TODO +// * @param @param byteList +// * @param @return +// * @return byte[] +// * @throws +// */ +// public static byte[] listToBytes(List byteList) { +// byte[] bytes = new byte[byteList.size()]; +// int index = 0; +// for (Byte item : byteList) { +// bytes[index++] = item; +// } +// return bytes; +// } + + /** + * + * @Title: date2HByte + * @Description: 日期转换成 CP56Time2a + * @param @param date + * @param @return + * @return byte[] + * @throws + */ + public static byte[] date2Hbyte(Date date) { + ByteArrayOutputStream bOutput = new ByteArrayOutputStream(); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + // 毫秒需要转换成两个字节其中 低位在前高位在后 + // 先转换成short + int millisecond = calendar.get(Calendar.SECOND) * 1000 + calendar.get(Calendar.MILLISECOND); + + // 默认的高位在前 + byte[] millisecondByte = intToByteArray(millisecond); + bOutput.write((byte) millisecondByte[3]); + bOutput.write((byte) millisecondByte[2]); + + // 分钟 只占6个比特位 需要把前两位置为零 + bOutput.write((byte) calendar.get(Calendar.MINUTE)); + // 小时需要把前三位置零 + bOutput.write((byte) calendar.get(Calendar.HOUR_OF_DAY)); + // 星期日的时候 week 是0 + int week = calendar.get(Calendar.DAY_OF_WEEK); + if (week == Calendar.SUNDAY) { + week = 7; + } else { + week--; + } + // 前三个字节是 星期 因此需要将星期向左移5位 后五个字节是日期 需要将两个数字相加 相加之前需要先将前三位置零 + bOutput.write((byte) (week << 5) + (calendar.get(Calendar.DAY_OF_MONTH))); + // 前四字节置零 + bOutput.write((byte) ((byte) calendar.get(Calendar.MONTH) + 1)); + bOutput.write((byte) (calendar.get(Calendar.YEAR) - 2000)); + return bOutput.toByteArray(); + } + + + /** + * + * @Title: date2HByte + * @Description:CP56Time2a转换成 时间 + * @param @param date + * @param @return + * @return byte[] + * @throws + */ + public static Date byte2Hdate(byte[] dataByte) { + int year = (dataByte[6] & 0x7F) + 2000; + int month = dataByte[5] & 0x0F; + int day = dataByte[4] & 0x1F; + int hour = dataByte[3] & 0x1F; + int minute = dataByte[2] & 0x3F; + int second = dataByte[1] > 0 ? dataByte[1] : (int) (dataByte[1] & 0xff); + int millisecond = dataByte[0] > 0 ? dataByte[0] : (int) (dataByte[0] & 0xff); + millisecond = (second << 8) + millisecond; + second = millisecond / 1000; + millisecond = millisecond % 1000; + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, month); + calendar.set(Calendar.DAY_OF_MONTH, day); + calendar.set(Calendar.HOUR_OF_DAY, hour); + calendar.set(Calendar.MINUTE, minute); + calendar.set(Calendar.SECOND, second); + calendar.set(Calendar.MILLISECOND, millisecond); + return calendar.getTime(); + } + + public static String byteArrayToHexString(byte[] array) { + return byteArray2HexString(array, Integer.MAX_VALUE, false); + } + + public static String byteArray2HexString(byte[] arrBytes, int count, boolean blank) { + String ret = ""; + if (arrBytes == null || arrBytes.length < 1) { + return ret; + } + if (count > arrBytes.length) { + count = arrBytes.length; + } + StringBuilder builder = new StringBuilder(); + + for (int i = 0; i < count; i++) { + ret = Integer.toHexString(arrBytes[i] & 0xFF).toUpperCase(); + if (ret.length() == 1) { + builder.append("0").append(ret); + } else { + builder.append(ret); + } + if (blank) { + builder.append(" "); + } + } + + return builder.toString(); + + } + + /** + * 返回指定位置的数组 + * @param bytes + * @param start 开始位置 + * @param length 截取长度 + * @return + */ + public static byte[] getByte(byte[] bytes, int start, int length) { + byte[] ruleByte = new byte[length]; + int index = 0; + while (index < length) { + ruleByte[index++] = bytes[start++]; + } + return ruleByte; + } + + + /** + * 十六进制字符串转换成byte数组 + * @param hexStr + * @return + */ + public static byte[] hexStringToBytes(String hexStr){ + hexStr = hexStr.replaceAll(" ", ""); + hexStr = hexStr.toUpperCase(); + int len = (hexStr.length() / 2); + byte[] result = new byte[len]; + char[] achar = hexStr.toCharArray(); + for (int i = 0; i < len; i++) { + int pos = i * 2; + result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1])); + } + return result; + } + + private static int toByte(char c) { + byte b = (byte) "0123456789ABCDEF".indexOf(c); + return b; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/iec104/util/Iec104Util.java b/riis-system/src/main/java/com/yfd/platform/component/iec104/util/Iec104Util.java new file mode 100644 index 0000000..1a7363c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/iec104/util/Iec104Util.java @@ -0,0 +1,258 @@ +package com.ydl.iec.util; + +import com.yfd.platform.component.iec104.message.MessageDetail; +import com.yfd.platform.component.iec104.enums.TypeIdentifierEnum; +import com.yfd.platform.component.iec104.enums.UControlEnum; + +/** + * +* @ClassName: Iec104Util +* @Description: 工具类 +* @author YDL +* @date 2020年5月13日 + */ +public class Iec104Util { + + private static int controlLength = 4; + + /** + * I 格式 低位在前 + * @param accept 接收序列号 + * @param send 发送序列号 + * @return + */ + public static byte[] getIcontrol(short accept, short send) { + byte[] control = new byte[4]; + // 向左移动一位 保证低位的D0 是0 + send = (short) (send << 1); + control[0] = (byte) ((send)); + control[1] = (byte) ((send >> 8)); + accept = (short) (accept << 1); + control[2] = (byte) ((accept)); + control[3] = (byte) ((accept >> 8)); + return control; + } + + /** + * 返回控制域中的接收序号 + * @param control + * @return + */ + public static short getAccept(byte[] control) { + int accept = 0; + short acceptLow = (short) (control[2] & 0xff); + short acceptHigh = (short) (control[3] & 0xff); + accept += acceptLow; + accept += acceptHigh << 8; + accept = accept >> 1; + return (short) accept; + + } + + /** + * 返回控制域中的发送序号 + * @param control + * @return + */ + public static short getSend(byte[] control) { + int send = 0; + short acceptLow = (short) (control[0] & 0xff); + short acceptHigh = (short) (control[1] & 0xff); + send += acceptLow; + send += acceptHigh << 8; + send = send >> 1; + return (short) send; + } + + /** + * S 格式 + * @param accept + * @return + */ + public static byte[] getScontrol(short accept) { + byte[] control = new byte[4]; + // 向左移动一位 保证低位的D0 是0 + short send = 1; + control[0] = (byte) ((send)); + control[1] = (byte) ((send >> 8)); + accept = (short) (accept << 1); + control[2] = (byte) ((accept)); + control[3] = (byte) ((accept >> 8)); + return control; + } + + /** + * + * @Title: 返回U帧 + * @Description: 判断是否是 + * @param @param control + * @param @return + * @return boolean + * @throws + */ + public static UControlEnum getUcontrol(byte[] control) { + if (control.length < controlLength || control[1] != 0 || control[2] != 0 || control[3] != 0) { + return null; + } + int controlInt = ByteUtil.byteArrayToInt(control); + for (UControlEnum ucontrolEnum : UControlEnum.values()) { + if (ucontrolEnum.getValue() == controlInt) { + return ucontrolEnum; + } + } + return null; + } + + + + + + /** + * 返回消息地址 其中低位在前 + * @param i + * @return + */ + public static byte[] intToMessageAddress(int i) { + byte[] result = new byte[3]; + result[0] = (byte) (i & 0xFF); + result[1] = (byte) ((i >> 8) & 0xFF); + result[2] = (byte) ((i >> 16) & 0xFF); + return result; + } + + + + /** + * 消息地址 只有三个 + * @param bytes + * @return + */ + public static int messageAddressToInt(byte[] bytes) { + int value = 0; + for (int i = 2; i >= 0; i--) { + int shift = (2 - i) * 8; + value += (bytes[2 - i] & 0xFF) << shift; + } + return value; + } + + /** + * 设置可以变限定词 + * @param ruleDetail104 + * @param byteItem + */ + public static void setChanged(MessageDetail ruleDetail104, byte byteItem) { + // 第一位是 0 则是有序的 + ruleDetail104.setContinuous((byteItem & 0x80) == 0 ? false : true); + // 先将第一位数置零 然后转换成int + ruleDetail104.setMeasgLength(byteItem & (byte) 0x7F); + } + + /** + * 返回可变限定词数组 + * @param ruleDetail104 + * @return + */ + public static byte getChangedQualifiers(MessageDetail ruleDetail104) { + // 将长度转换成 byte + byte changedQualifiers = (byte) ruleDetail104.getMeasgLength(); + // 判断SQ 置 isContinuous false SQ = 0;否则 SQ =1 , 同时将SQ置 设置在 可变限定词的 D7位置 + int sq = ruleDetail104.isContinuous() ? 0x80 : 0; + changedQualifiers = (byte) (sq | changedQualifiers); + return changedQualifiers; + } + + + public static void setMeaageAttribute(MessageDetail ruleDetail104) { + boolean isMessage = !(TypeIdentifierEnum.generalCall.equals(ruleDetail104.getTypeIdentifier()) //总召唤无此项 + || TypeIdentifierEnum.timeSynchronization.equals(ruleDetail104.getTypeIdentifier()) // 时钟同步 + || TypeIdentifierEnum.resetPprocess.equals(ruleDetail104.getTypeIdentifier()) // 复位进程 + || TypeIdentifierEnum.initEnd.equals(ruleDetail104.getTypeIdentifier())); + ruleDetail104.setMessage(isMessage); + + boolean isQualifiers = !(TypeIdentifierEnum.timeSynchronization.equals(ruleDetail104.getTypeIdentifier()) // 时钟同步 + || TypeIdentifierEnum.onePointTeleindication.equals(ruleDetail104.getTypeIdentifier()) // 单点 遥信 + || TypeIdentifierEnum.twoPointTeleindication.equals(ruleDetail104.getTypeIdentifier()) // 双点 遥信 + || TypeIdentifierEnum.onePointTimeTeleindication.equals(ruleDetail104.getTypeIdentifier()) // 单点 遥信(带时标) + || TypeIdentifierEnum.twoPointTimeTeleindication.equals(ruleDetail104.getTypeIdentifier()) // 双点 遥信(带时标) + || TypeIdentifierEnum.onePointTelecontrol.equals(ruleDetail104.getTypeIdentifier()) // 单命令遥控 + || TypeIdentifierEnum.twoPointTelecontrol.equals(ruleDetail104.getTypeIdentifier())); // 双命令遥控 + ruleDetail104.setQualifiers(isQualifiers); + + boolean isTimeScale = TypeIdentifierEnum.timeSynchronization.equals(ruleDetail104.getTypeIdentifier()) // 时钟同步 + || TypeIdentifierEnum.onePointTimeTeleindication.equals(ruleDetail104.getTypeIdentifier()) // 摇信带时标 单点 + || TypeIdentifierEnum.twoPointTimeTeleindication.equals(ruleDetail104.getTypeIdentifier()) //摇信带时标 双点 + || TypeIdentifierEnum.normalizedTimeTelemetry.equals(ruleDetail104.getTypeIdentifier()) //遥测带时标 归一化值 + || TypeIdentifierEnum.scaledTimeTelemetry.equals(ruleDetail104.getTypeIdentifier()) //遥测带时标 标度化值 + || TypeIdentifierEnum.shortFloatingPointTimeTelemetry.equals(ruleDetail104.getTypeIdentifier()); //遥测带时标 短浮点数 + ruleDetail104.setTimeScaleExit(isTimeScale); + } + + /** + * short 转换成两个 字节后是163 00 也就是 value[1] 中才有值 + * test 在D7位置 因此 值应该和 01000000 做与运算 + * P/N 0肯定确认 1否定确认 + * @return 肯定或否定确认 + */ + public static boolean isYes(byte[] values) { + return (values[0] & 1 << 6) == 0; + } + /** + * short 转换成两个 字节后是163 00 也就是 value[1] 中才有值 + * test 在D7位置 因此 值应该和 10000000 做与运算 + * tets 0 为试验 1 试验 + * @return 是否试验 + */ + public static boolean isTets(byte[] values) { + return (values[0] & 1 << 7) != 0; + } + + /** + * 返回具体的原因 + * @param values + * @return + */ + public static short getTransferReasonShort(byte[] values) { + byte transferReason = values[0]; + // 前两位置零 + transferReason = (byte) (transferReason & 0x3E); + return transferReason; + } + + + public static short getTransferReasonShort(boolean isTets, boolean isYes, short transferReason) { + int t = isTets ? 1 : 0; + int y = isYes ? 0 : 1; + int transferReasonInt = t << 7 | transferReason; + transferReasonInt = y << 6 | transferReasonInt; + + short transferReasonShort = (short) (transferReasonInt << 8); + return transferReasonShort; + } + + + /** + * 返回终端地址对应的byte数组 其中低位在前 + * @param terminalAddress + * @return + */ + public static byte[] getTerminalAddressByte(short terminalAddress) { + byte[] b = new byte[2]; + b[1] = (byte) ((terminalAddress >> 8) & 0xff); + b[0] = (byte) (terminalAddress & 0xff); + return b; + } + + + /** + * 返回回终端地址 其中低位在前 + * @param terminalAddress + * @return + */ + public static short getTerminalAddressShort(byte[] terminalAddress) { + short value = 0; + value += (terminalAddress[0] & 0xFF); + value += (terminalAddress[1] & 0xFF) << 8; + return value; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceAlarmParameterController.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceAlarmParameterController.java new file mode 100644 index 0000000..0dbff0c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceAlarmParameterController.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.auxcontrol.controller; + + +import org.springframework.web.bind.annotation.RequestMapping; + +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 变电站-辅控设备-告警参数设置 前端控制器 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@RestController +@RequestMapping("/auxcontrol/device-alarm-parameter") +public class DeviceAlarmParameterController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceAlarmRecordController.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceAlarmRecordController.java new file mode 100644 index 0000000..f6474ad --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceAlarmRecordController.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.auxcontrol.controller; + + +import org.springframework.web.bind.annotation.RequestMapping; + +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 变电站-辅控设备-告警记录 前端控制器 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@RestController +@RequestMapping("/auxcontrol/device-alarm-record") +public class DeviceAlarmRecordController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceSignalController.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceSignalController.java new file mode 100644 index 0000000..9e7ab2c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceSignalController.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.auxcontrol.controller; + + +import org.springframework.web.bind.annotation.RequestMapping; + +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 变电站-辅控设备-信号 前端控制器 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@RestController +@RequestMapping("/auxcontrol/device-signal") +public class DeviceSignalController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceWorkDataController.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceWorkDataController.java new file mode 100644 index 0000000..0da6657 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/DeviceWorkDataController.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.auxcontrol.controller; + + +import org.springframework.web.bind.annotation.RequestMapping; + +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 变电站-设备-运行数据 前端控制器 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@RestController +@RequestMapping("/auxcontrol/device-work-data") +public class DeviceWorkDataController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/GatewayDeviceController.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/GatewayDeviceController.java new file mode 100644 index 0000000..19db61c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/GatewayDeviceController.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.auxcontrol.controller; + + +import org.springframework.web.bind.annotation.RequestMapping; + +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 变电站-通讯网关设备 前端控制器 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@RestController +@RequestMapping("/auxcontrol/gateway-device") +public class GatewayDeviceController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/MeterDeviceController.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/MeterDeviceController.java new file mode 100644 index 0000000..858171b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/controller/MeterDeviceController.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.auxcontrol.controller; + + +import org.springframework.web.bind.annotation.RequestMapping; + +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 变电站二次设备中的智能仪表监控设备 前端控制器 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@RestController +@RequestMapping("/auxcontrol/meter-device") +public class MeterDeviceController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceAlarmParameter.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceAlarmParameter.java new file mode 100644 index 0000000..f7cce03 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceAlarmParameter.java @@ -0,0 +1,115 @@ +package com.yfd.platform.modules.auxcontrol.domain; + +import com.baomidou.mybatisplus.annotation.TableName; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * 变电站-辅控设备-告警参数设置 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("fk_device_alarm_parameter") +public class DeviceAlarmParameter implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 参数ID + */ + private String paramId; + + /** + * 设备ID + */ + private String deviceId; + + /** + * 信号ID + */ + private String signalId; + + /** + * 信号名称 + */ + private String signalName; + + /** + * 告警类型:如:温度过高、压力异常、电流过载 + */ + private String alarmType; + + /** + * 告警等级:一般、严重、危急 + */ + private String alarmLevel; + + /** + * 告警触发条件 + */ + private String alarmCondition; + + /** + * 告警抑制条件 + */ + private String inhibitionCondition; + + /** + * 告警描述模板 + */ + private String messageTemplate; + + /** + * 01-组件变色 02-系统弹窗 ,03-邮件通知 ,04-触发联动 可多选:01,02 + */ + private String noticeType; + + /** + * 触发联动:[{name:"XX联动",id:"12345678"}] + */ + private String noticeLinkages; + + /** + * 邮件通知:[{name:"张三",email:"1122@163.com"}] + */ + private String noticeEmails; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceAlarmRecord.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceAlarmRecord.java new file mode 100644 index 0000000..b4766c3 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceAlarmRecord.java @@ -0,0 +1,160 @@ +package com.yfd.platform.modules.auxcontrol.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import java.time.LocalDateTime; +import java.io.Serializable; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * 变电站-辅控设备-告警记录 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("fk_device_alarm_record") +public class DeviceAlarmRecord implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 记录ID + */ + private String recordId; + + /** + * 变电站ID + */ + private String stationId; + + /** + * 所属系统:01-一次设备监控 02-消防系统 03-安防系统 04-动环系统 05-锁控系统 06-视频系统 07-照明系统 + */ + private String systemcode; + + /** + * 告警时间 + */ + private LocalDateTime alarmTime; + + /** + * 告警设备ID + */ + private String deviceId; + + /** + * 告警设备名称 + */ + private String deviceName; + + /** + * 告警信号ID + */ + private String signalId; + + /** + * 告警信号名称 + */ + private String signalName; + + /** + * 告警类型 + */ + private String alarmType; + + /** + * 告警等级:一般、严重、危急 + */ + private String alarmLevel; + + /** + * 告警值 + */ + private String alarmValue; + + /** + * 值单位 + */ + private String signalUnit; + + /** + * 正常范围 + */ + private String normalRange; + + /** + * 告警信息 + */ + private String alarmMessage; + + /** + * 01-组件变色 02-系统弹窗 ,03-邮件通知 ,04-触发联动 可多选:01,02 + */ + private String noticeAction; + + /** + * 邮件通知:[{name:"张三",email:"1122@163.com"}] + */ + private String noticeEmails; + + /** + * 触发联动:[{name:"XX联动",id:"12345678"}] + */ + private String noticeLinkages; + + /** + * 状态:01-初始生成 02-已执行动作 03-手动关闭 09-故障消除 + */ + private String status; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + /** + * 告警分类:1-越限告警 2-设备报警 + */ + private String alarmClass; + + /** + * 故障消除时间 + */ + private LocalDateTime fixTime; + + /** + * 区域标识 + */ + private String region; + + /** + * 是否关闭告警0为关闭 1关闭 + */ + private String confirmClosure; + + /** + * 操作人 + */ + private String operator; + + /** + * 操作时间 + */ + private LocalDateTime operationTime; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceSignal.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceSignal.java new file mode 100644 index 0000000..89c2236 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceSignal.java @@ -0,0 +1,141 @@ +package com.yfd.platform.modules.auxcontrol.domain; + +import java.math.BigDecimal; +import com.baomidou.mybatisplus.annotation.TableName; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * 变电站-辅控设备-信号 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("fk_device_signal") +public class DeviceSignal implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 信号ID + */ + private String signalId; + + /** + * 辅控设备ID--fk_meter_device + */ + private String meterDeviceId; + + /** + * 变电站一次设备id:iis_substation_maindevice + */ + private String mainDeviceId; + + /** + * 关联一次设备中部件ID,iis_substation_component + */ + private String mainCompnentId; + + /** + * 信号类型 0-普通信号 1-告警信号 + */ + private String signalType; + + /** + * 信号编号 + */ + private String signalCode; + + /** + * 信号名称 + */ + private String signalName; + + /** + * 信号单位:类似 ℃ + */ + private String signalUnit; + + /** + * 正常范围 + */ + private String normalRange; + + /** + * 顺序号 + */ + private Integer orderno; + + /** + * 遥控遥调类型:01-单点遥控 02- -双点遥控 03-归一化值遥调 04-标度化遥调 05-浮点数遥调 + */ + private String ykytType; + + /** + * 遥控地址(不测可为空) + */ + private String ykAddr; + + /** + * 遥信地址(不测可为空) + */ + private String yxAddr; + + /** + * 遥测地址(不测可为空) + */ + private String ycAddr; + + /** + * 遥调地址(不测可为空) + */ + private String ytAddr; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp lastmodifydate; + + /** + * 信号当前状态 + */ + private Integer yxValue; + + /** + * 信号当前状态 + */ + private BigDecimal ycValue; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceWorkData.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceWorkData.java new file mode 100644 index 0000000..725e6e6 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/DeviceWorkData.java @@ -0,0 +1,91 @@ +package com.yfd.platform.modules.auxcontrol.domain; + +import java.math.BigDecimal; +import com.baomidou.mybatisplus.annotation.TableName; +import java.time.LocalDateTime; +import java.io.Serializable; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * 变电站-设备-运行数据 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("fk_device_work_data") +public class DeviceWorkData implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + private String id; + + /** + * 所属系统:01-一次设备监控 02-消防系统 03-安防系统 04-动环系统 05-锁控系统 06-视频系统 07-照明系统 + */ + private String systemcode; + + /** + * 变电站ID + */ + private String stationId; + + /** + * 监控设备ID + */ + private String deviceId; + + /** + * 监控设备名称 + */ + private String deviceName; + + /** + * 监控信号ID + */ + private String signalId; + + /** + * 监控信号名称 + */ + private String signalName; + + /** + * 采集时间 + */ + private LocalDateTime startTime; + + /** + * 单位 + */ + private String unit; + + /** + * 值 + */ + private BigDecimal value; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/GatewayDevice.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/GatewayDevice.java new file mode 100644 index 0000000..49f0569 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/GatewayDevice.java @@ -0,0 +1,152 @@ +package com.yfd.platform.modules.auxcontrol.domain; + +import com.baomidou.mybatisplus.annotation.TableName; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.TableField; +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * 变电站-通讯网关设备 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("fk_gateway_device") +public class GatewayDevice implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + private String id; + + /** + * 变电站ID + */ + private String stationId; + + /** + * 设备类型:01-监控主机 02-网关机 03-通信服务器 + */ + private String deviceType; + + /** + * 设备编号 + */ + private String deviceCode; + + /** + * 设备名称 + */ + private String deviceName; + + /** + * 中文描述 + */ + private String deviceDesc; + + /** + * 设备型号 + */ + private String deviceModel; + + /** + * 出厂编号 + */ + private String productionNum; + + /** + * 生产日期 + */ + private String productionDate; + + /** + * 生产厂家 + */ + private String manufacturer; + + /** + * 生产国家 + */ + private String madein; + + /** + * 实物ID + */ + private String phyassetId; + + /** + * 以太网IP地址 + */ + private String ipAddr; + + /** + * 以太网Mac地址 + */ + private String macAddr; + + /** + * 设备状态:00-停用 01-使用-在线 02-使用-不在线 + */ + private String status; + + /** + * IEC104协议地址 + */ + private Integer iecAddr; + + /** + * 心跳保持更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp keepliveTime; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + /** + * 遥测类报表控制块 + */ + private String urcb; + + /** + * 遥信类报告控制块 + */ + private String brcb; + + /** + * 设备使用的协议类型 IEC104,IEC61850 + */ + @TableField("Protocol") + private String protocol; + + /** + * 采集频率 + */ + private String frequency; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/MeterDevice.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/MeterDevice.java new file mode 100644 index 0000000..e75cae5 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/domain/MeterDevice.java @@ -0,0 +1,150 @@ +package com.yfd.platform.modules.auxcontrol.domain; + +import com.baomidou.mybatisplus.annotation.TableName; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * 变电站二次设备中的智能仪表监控设备 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("fk_meter_device") +public class MeterDevice implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 设备ID + */ + private String deviceId; + + /** + * 变电站ID + */ + private String stationId; + + /** + * 所属系统:01-一次设备监控 02-消防系统 03-安防系统 04-动环系统 05-锁控系统 06-视频系统 07-照明系统 + */ + private String systemcode; + + /** + * 设备类型:01-温度传感器、02-湿度传感器、03-SF?传感器、04-水浸传感器、05-风速传感器、06-空调控制器、07-门禁、08-电子围栏、09-红外、10-火灾告警控制器、11-智能感温控制器、12-烟感探测器、13声光报警器、14-照明 + */ + private String deviceType; + + /** + * 设备编号 + */ + private String deviceCode; + + /** + * 设备名称 + */ + private String deviceName; + + /** + * 中文描述 + */ + private String deviceDesc; + + /** + * 设备型号 + */ + private String deviceModel; + + /** + * 出厂编号 + */ + private String productionNum; + + /** + * 出厂日期 + */ + private String productionDate; + + /** + * 生产厂家 + */ + private String manufacturer; + + /** + * 生产国家 + */ + private String madein; + + /** + * 实物ID + */ + private String phyassetId; + + /** + * 区域标识 + */ + private String region; + + /** + * 安装位置(预留) + */ + private String place; + + /** + * 以太网IP地址 + */ + private String ipAddr; + + /** + * 以太网Mac地址 + */ + private String macAddr; + + /** + * 对应网关设备IP + */ + private String netdeviceIp; + + /** + * 设备状态:00-停用 01-使用-在线 02-使用-不在线 + */ + private String status; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceAlarmParameterMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceAlarmParameterMapper.java new file mode 100644 index 0000000..710a62b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceAlarmParameterMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.auxcontrol.mapper; + +import com.yfd.platform.modules.auxcontrol.domain.DeviceAlarmParameter; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * 变电站-辅控设备-告警参数设置 Mapper 接口 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +public interface DeviceAlarmParameterMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceAlarmRecordMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceAlarmRecordMapper.java new file mode 100644 index 0000000..98930ec --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceAlarmRecordMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.auxcontrol.mapper; + +import com.yfd.platform.modules.auxcontrol.domain.DeviceAlarmRecord; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * 变电站-辅控设备-告警记录 Mapper 接口 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +public interface DeviceAlarmRecordMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceSignalMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceSignalMapper.java new file mode 100644 index 0000000..a630d77 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceSignalMapper.java @@ -0,0 +1,23 @@ +package com.yfd.platform.modules.auxcontrol.mapper; + +import com.yfd.platform.modules.auxcontrol.domain.DeviceSignal; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 变电站-辅控设备-信号 Mapper 接口 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +public interface DeviceSignalMapper extends BaseMapper { + + List selectDeviceSignalYx(String slave_ip, String address); + List selectDeviceSignalYc(String slave_ip, String address); + List> selectDeviceSignal_Map(String slave_ip, String type, String address); + List> selectDeviceSignal_Map2(String type,String address); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceWorkDataMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceWorkDataMapper.java new file mode 100644 index 0000000..c62701c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/DeviceWorkDataMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.auxcontrol.mapper; + +import com.yfd.platform.modules.auxcontrol.domain.DeviceWorkData; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * 变电站-设备-运行数据 Mapper 接口 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +public interface DeviceWorkDataMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/GatewayDeviceMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/GatewayDeviceMapper.java new file mode 100644 index 0000000..4d3ffaf --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/GatewayDeviceMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.auxcontrol.mapper; + +import com.yfd.platform.modules.auxcontrol.domain.GatewayDevice; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * 变电站-通讯网关设备 Mapper 接口 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +public interface GatewayDeviceMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/MeterDeviceMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/MeterDeviceMapper.java new file mode 100644 index 0000000..56c419a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/mapper/MeterDeviceMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.auxcontrol.mapper; + +import com.yfd.platform.modules.auxcontrol.domain.MeterDevice; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * 变电站二次设备中的智能仪表监控设备 Mapper 接口 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +public interface MeterDeviceMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceAlarmParameterService.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceAlarmParameterService.java new file mode 100644 index 0000000..21a435a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceAlarmParameterService.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.auxcontrol.service; + +import com.yfd.platform.modules.auxcontrol.domain.DeviceAlarmParameter; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 变电站-辅控设备-告警参数设置 服务类 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +public interface IDeviceAlarmParameterService extends IService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceAlarmRecordService.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceAlarmRecordService.java new file mode 100644 index 0000000..d7a8ec0 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceAlarmRecordService.java @@ -0,0 +1,27 @@ +package com.yfd.platform.modules.auxcontrol.service; + +import com.yfd.platform.modules.auxcontrol.domain.DeviceAlarmRecord; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 变电站-辅控设备-告警记录 服务类 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +public interface IDeviceAlarmRecordService extends IService { + + /********************************** + * 用途说明: 生成报警记录 + * 参数说明 + * from 'IEC104','IEC61850' + * type 'yx','yc' + * slave_ip 服务器IP + * address 地址 + * value 值 + * 返回值说明: 无返回值 + ***********************************/ + void doAlaramRecord(String from,String type,String slaveIp,String address,String value); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceSignalService.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceSignalService.java new file mode 100644 index 0000000..bf78add --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceSignalService.java @@ -0,0 +1,29 @@ +package com.yfd.platform.modules.auxcontrol.service; + +import com.yfd.platform.modules.auxcontrol.domain.DeviceSignal; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.text.ParseException; + +/** + *

+ * 变电站-辅控设备-信号 服务类 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +public interface IDeviceSignalService extends IService { + + /********************************** + * 用途说明: 遥测 、遥信数据更新到数据库 + * 参数说明 + * netaddres 网关机设备地址 + * address 信号地址 + * type 信号类型 yx-遥信 yc-遥测 + * value 数据采集结果 + * dateTime 数据采集时标 + * 返回值说明: bool 更新成功或者失败 + ***********************************/ + boolean updateDeviceSignalValue(String slaveIp, String address, String type, String value, String dateTime) throws ParseException; +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceWorkDataService.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceWorkDataService.java new file mode 100644 index 0000000..1a67014 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IDeviceWorkDataService.java @@ -0,0 +1,27 @@ +package com.yfd.platform.modules.auxcontrol.service; + +import com.yfd.platform.modules.auxcontrol.domain.DeviceWorkData; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 变电站-设备-运行数据 服务类 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +public interface IDeviceWorkDataService extends IService { + + /********************************** + * 用途说明: 从设备中采集数据,并保存到数据库中 + * 参数说明 + * from 'IEC104','IEC61850' + * slave_ip 通信网关机地址 + * address 信号地址 + * value 数据采集结果 + * dateTime 数据采集时标 + * 返回值说明: 无 + ***********************************/ + void insertData(String from, String slave_ip, String address, String value, String dateTimeString); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IGatewayDeviceService.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IGatewayDeviceService.java new file mode 100644 index 0000000..bff8894 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IGatewayDeviceService.java @@ -0,0 +1,23 @@ +package com.yfd.platform.modules.auxcontrol.service; + +import com.yfd.platform.modules.auxcontrol.domain.GatewayDevice; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 变电站-通讯网关设备 服务类 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +public interface IGatewayDeviceService extends IService { + + /********************************** + * 用途说明: 根据IP,更新心跳保持时间 + * 参数说明 + * ip 终端对应IP + * 返回值说明: 无 + ***********************************/ + void updateKeepLiveTime(String ip); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IMeterDeviceService.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IMeterDeviceService.java new file mode 100644 index 0000000..91d45ab --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/IMeterDeviceService.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.auxcontrol.service; + +import com.yfd.platform.modules.auxcontrol.domain.MeterDevice; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 变电站二次设备中的智能仪表监控设备 服务类 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +public interface IMeterDeviceService extends IService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceAlarmParameterServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceAlarmParameterServiceImpl.java new file mode 100644 index 0000000..c812011 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceAlarmParameterServiceImpl.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.auxcontrol.service.impl; + +import com.yfd.platform.modules.auxcontrol.domain.DeviceAlarmParameter; +import com.yfd.platform.modules.auxcontrol.mapper.DeviceAlarmParameterMapper; +import com.yfd.platform.modules.auxcontrol.service.IDeviceAlarmParameterService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + *

+ * 变电站-辅控设备-告警参数设置 服务实现类 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@Service +public class DeviceAlarmParameterServiceImpl extends ServiceImpl implements IDeviceAlarmParameterService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceAlarmRecordServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceAlarmRecordServiceImpl.java new file mode 100644 index 0000000..ea5eec2 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceAlarmRecordServiceImpl.java @@ -0,0 +1,438 @@ +package com.yfd.platform.modules.auxcontrol.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.modules.auxcontrol.domain.DeviceAlarmParameter; +import com.yfd.platform.modules.auxcontrol.domain.DeviceAlarmRecord; +import com.yfd.platform.modules.auxcontrol.domain.MeterDevice; +import com.yfd.platform.modules.auxcontrol.mapper.DeviceAlarmParameterMapper; +import com.yfd.platform.modules.auxcontrol.mapper.DeviceAlarmRecordMapper; +import com.yfd.platform.modules.auxcontrol.mapper.DeviceSignalMapper; +import com.yfd.platform.modules.auxcontrol.mapper.MeterDeviceMapper; +import com.yfd.platform.modules.auxcontrol.service.IDeviceAlarmRecordService; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.mapper.SysDictionaryItemsMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

+ * 变电站-辅控设备-告警记录 服务实现类 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@Service +public class DeviceAlarmRecordServiceImpl extends ServiceImpl implements IDeviceAlarmRecordService { + + @Resource + private DeviceSignalMapper deviceSignalMapper; + + @Resource + private MeterDeviceMapper meterDeviceMapper; + + @Resource + private DeviceAlarmParameterMapper deviceAlarmParameterMapper; + + @Resource + private DeviceAlarmRecordMapper deviceAlarmRecordMapper; + + @Resource + private SysDictionaryItemsMapper sysDictionaryItemsMapper; + + /********************************** + * 用途说明: 生成报警记录(IEC104) + * 参数说明 + * from 'IEC104','IEC61850' + * type 'yx','yc' + * slave_ip IP + * address 遥信、要测地址 + * value 值 + * 返回值说明: 无返回值 + ***********************************/ + @Override + @Transactional + public void doAlaramRecord(String from, String type, String slaveIp, String address, String value) { + List> list = new ArrayList<>(); + if ("IEC104".equals(from)) { + list = deviceSignalMapper.selectDeviceSignal_Map(slaveIp, "yc", address); + } else if ("IEC61850".equals(from)) { + list = deviceSignalMapper.selectDeviceSignal_Map2(type, address); + } + if (list.size() == 0) { + return; + } + Map map = list.get(0); + //如果是yx 遥信 (设备自身报警记录) + if ("yx".equals(type)) { + //如果信号类型是1 设备告警 + if ("1".equals(map.get("signal_type").toString())) { + //紧接者判断 value (1是告警 0是正常) status + if (1 == Integer.parseInt(value)) { + //填充告警记录信息接口 + DeviceAlarmRecord deviceAlarmRecord = FillingDeviceAlarmRecord(type, null, map, value); + //首先判断 在不在时间范围之内 判断时间在不在时间范围之内接口 + Map timeInterval = getTimeInterval(7); + //如果在时间范围内 + if ("1".equals(timeInterval.get("beforeclock").toString())) { + //查询表中是否存在该报警记录 + List alarmRecords = queryDeviceAlarmRecord(deviceAlarmRecord, timeInterval); + //如果没有有报警记录 新增记录 + if (alarmRecords.size() == 0) { + doAlarmAction(deviceAlarmRecord); + } else if (alarmRecords.size() > 1) { + this.removeById(alarmRecords.get(0).getRecordId()); + } + } else { + List alarmRecords = queryDeviceAlarmRecord(deviceAlarmRecord, timeInterval); + if (alarmRecords.size() == 0) { + doAlarmAction(deviceAlarmRecord); + } else if (alarmRecords.size() > 1) { + this.removeById(alarmRecords.get(0).getRecordId()); + } + } + } else { + //紧接者判断 value (1是告警 0是正常) status 同一信号监测到报警状态消除,对当前信号所有未处理历史报警打上已处理标签,并记录消除时间 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(DeviceAlarmRecord::getSignalId, map.get("signal_id").toString()); + queryWrapper.eq(DeviceAlarmRecord::getAlarmClass, "2"); + queryWrapper.in(DeviceAlarmRecord::getStatus, "01", "02"); + DeviceAlarmRecord deviceAlarmRecord = new DeviceAlarmRecord(); + deviceAlarmRecord.setStatus("09"); + //状态 故障消除 + deviceAlarmRecord.setFixTime(DateUtil.toLocalDateTime(DateUtil.date())); + //故障消除时间 + this.update(deviceAlarmRecord, queryWrapper); + } + } + } else if ("yc".equals(type)) { + //1.通过设备id 和信号id 查询告警参数设置表(告警规则) + LambdaQueryWrapper queryWrapperDAP = new LambdaQueryWrapper<>(); + if (StrUtil.isNotEmpty(map.get("device_id").toString())) { + queryWrapperDAP.eq(DeviceAlarmParameter::getDeviceId, map.get("device_id").toString()); + } + if (StrUtil.isNotEmpty(map.get("signal_id").toString())) { + queryWrapperDAP.eq(DeviceAlarmParameter::getSignalId, map.get("signal_id").toString()); + } + List deviceAlarmParameters = deviceAlarmParameterMapper.selectList(queryWrapperDAP); + if (deviceAlarmParameters != null && deviceAlarmParameters.size() > 0) { + for (DeviceAlarmParameter deviceAlarmParameter : deviceAlarmParameters) { + //如果告警触发条件 不为空 1<=value<=100 + if (deviceAlarmParameter.getAlarmCondition() != null) { + //根据告警规则 判断是否触发告警 + boolean alarmTrigger = TriggerAlarm(deviceAlarmParameter, value); + + //2.通过告警触发条件 和 value比较 满足条件 触发告警 + if (alarmTrigger) { + DeviceAlarmRecord deviceAlarmRecord = FillingDeviceAlarmRecord(type, deviceAlarmParameter + , map, value); + //首先判断 在不在时间范围之内 判断时间在不在时间范围之内接口 + Map timeInterval = getTimeInterval(7); + //如果在时间范围内 + if ("1".equals(timeInterval.get("beforeclock").toString())) { + //查询表中是否存在该报警记录 + List alarmRecords = queryDeviceAlarmRecord(deviceAlarmRecord, + timeInterval); + //如果没有有报警记录 新增记录 + if (alarmRecords.size() == 0) { + // 新增 + deviceAlarmRecordMapper.insert(deviceAlarmRecord); + //TODO 邮件推送暂时删除了 + } + } else { + List alarmRecords = queryDeviceAlarmRecord(deviceAlarmRecord, + timeInterval); + if (alarmRecords.size() == 0) { + deviceAlarmRecordMapper.insert(deviceAlarmRecord); + //TODO 邮件推送暂时删除了 + } + } + } else { + //不满足条件 同一信号监测到报警状态消除,对当前信号所有未处理历史报警打上已处理标签,并记录消除时间 + LambdaUpdateWrapper queryWrapper = new LambdaUpdateWrapper<>(); + queryWrapper.eq(DeviceAlarmRecord::getSignalId, map.get("signal_id").toString()); + queryWrapper.eq(DeviceAlarmRecord::getAlarmClass, "1"); + queryWrapper.eq(DeviceAlarmRecord::getAlarmLevel, deviceAlarmParameter.getAlarmLevel()); + queryWrapper.in(DeviceAlarmRecord::getStatus, "01", "02") + .set(DeviceAlarmRecord::getStatus, "09") + .set(DeviceAlarmRecord::getFixTime, LocalDateTime.now()); + //故障消除时间 + deviceAlarmRecordMapper.update(null, queryWrapper); + } + } + } + } + } + + } + + /** + * 填充告警记录表 + * + * @return 返回告警记录信息 + */ + public DeviceAlarmRecord FillingDeviceAlarmRecord(String type, DeviceAlarmParameter deviceAlarmParameter, + Map singaldata, String value) { + DeviceAlarmRecord deviceAlarmRecord = new DeviceAlarmRecord(); + MeterDevice device = meterDeviceMapper.selectById(singaldata.get("device_id").toString()); + + // 创建 SimpleDateFormat 对象,指定日期格式 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + if ("yx".equals(type)) { + deviceAlarmRecord.setRecordId(IdUtil.fastSimpleUUID()); + deviceAlarmRecord.setStationId(singaldata.get("station_id").toString()); + //变电站id + deviceAlarmRecord.setSystemcode(singaldata.get("systemcode").toString()); + //所属系统 + deviceAlarmRecord.setAlarmTime(DateUtil.toLocalDateTime(DateUtil.date())); + //告警时间 + deviceAlarmRecord.setDeviceId(singaldata.get("device_id").toString()); + //告警设备ID + deviceAlarmRecord.setDeviceName(singaldata.get("device_name").toString()); + //告警设名称 + deviceAlarmRecord.setSignalId(singaldata.get("signal_id").toString()); + //告警信号id + deviceAlarmRecord.setSignalName(singaldata.get("signal_name").toString()); + //告警信号名称 + deviceAlarmRecord.setAlarmValue(value); + //告警值 + deviceAlarmRecord.setSignalUnit(""); + //值单位 + String alarmMessage = String.format("[%s]发生了%s!", singaldata.get("device_name").toString(), + singaldata.get("signal_name").toString()); + deviceAlarmRecord.setAlarmMessage(alarmMessage); + //告警信息 + deviceAlarmRecord.setStatus("01"); + //状态 + deviceAlarmRecord.setAlarmLevel("03"); + deviceAlarmRecord.setAlarmClass("2"); + //2-设备报警 + deviceAlarmRecord.setRegion(device.getRegion()); + //区域标识 + } + if ("yc".equals(type)) { + deviceAlarmRecord.setRecordId(IdUtil.fastSimpleUUID()); + //变电站id + deviceAlarmRecord.setStationId(singaldata.get("station_id").toString()); + //所属系统 + deviceAlarmRecord.setSystemcode(singaldata.get("systemcode").toString()); + //告警时间 + deviceAlarmRecord.setAlarmTime(DateUtil.toLocalDateTime(DateUtil.date())); + //告警设备ID + deviceAlarmRecord.setDeviceId(singaldata.get("device_id").toString()); + //告警设备名称 + deviceAlarmRecord.setDeviceName(singaldata.get("device_name").toString()); + //告警信号id + deviceAlarmRecord.setSignalId(singaldata.get("signal_id").toString()); + //告警信号名称 + deviceAlarmRecord.setSignalName(singaldata.get("signal_name").toString()); + //告警类型 + deviceAlarmRecord.setAlarmType(deviceAlarmParameter.getAlarmType()); + //告警等级 + deviceAlarmRecord.setAlarmLevel(deviceAlarmParameter.getAlarmLevel()); + //告警值 + deviceAlarmRecord.setAlarmValue(value); + String sinalUnit = ObjUtil.isNotEmpty(singaldata.get("signal_unit")) ? + singaldata.get("signal_unit").toString() : ""; + //值单位 + deviceAlarmRecord.setSignalUnit(sinalUnit); + //正常范围 + deviceAlarmRecord.setNormalRange(deviceAlarmParameter.getAlarmCondition()); + + String alarmLevelname = getDictName(deviceAlarmParameter.getAlarmLevel(), "alarmLevel"); + String alarmMessage = String.format("信号[%s]监测值为[%s]%s,达到了设置的报警阈值范围[%s],发生告警,告警级别为[%s]!", + singaldata.get("signal_name").toString(), value, sinalUnit, + deviceAlarmParameter.getAlarmCondition(), alarmLevelname + ); + //告警信息 + deviceAlarmRecord.setAlarmMessage(alarmMessage); + //告警方式 + deviceAlarmRecord.setNoticeAction(deviceAlarmParameter.getNoticeType()); + //邮件通知 + deviceAlarmRecord.setNoticeEmails(deviceAlarmParameter.getNoticeEmails()); + //触发联动 + deviceAlarmRecord.setNoticeLinkages(deviceAlarmParameter.getNoticeLinkages()); + //状态 + deviceAlarmRecord.setStatus("01"); + //1-越限报警 + deviceAlarmRecord.setAlarmClass("1"); + //区域标识 + deviceAlarmRecord.setRegion(device.getRegion()); + + } + return deviceAlarmRecord; + } + + //获取字典名称 根据项目编码和父项编码 + public String getDictName(String itemcode, String parentcode) { + QueryWrapper queryWrapperSysDictionaryItems = new QueryWrapper<>(); + String dictname = ""; + queryWrapperSysDictionaryItems.eq("itemcode", itemcode); + queryWrapperSysDictionaryItems.eq("parentcode", parentcode); + SysDictionaryItems sysDictionaryItems = sysDictionaryItemsMapper.selectOne(queryWrapperSysDictionaryItems); + if (sysDictionaryItems != null) { + dictname = sysDictionaryItems.getDictName(); + } + return dictname; + } + + /** + * 根据告警规则 判断是否触发告警 + * + * @return true 触发恭敬规则 + * false 没有触发告警规则 + */ + public boolean TriggerAlarm(DeviceAlarmParameter deviceAlarmParameter, String value) { + boolean alramTrigger = true; + List listSymbol = new ArrayList<>(); + List values = new ArrayList<>(); + // //获取告警触发条件 + String str = deviceAlarmParameter.getAlarmCondition(); + // 定义正则表达式 + String regex = "([0-9]+(\\.[0-9]+)?|value)([<>=!]{1,2})([0-9]+(\\.[0-9]+)?|value)"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(str); + + // 查找并输出比较符号和数值 + while (matcher.find()) { + String value1 = matcher.group(1); + String operator = matcher.group(3); + String value2 = matcher.group(4); + + if ("value".equals(value1)) { + values.add(Double.parseDouble(value)); + } else { + values.add(Double.valueOf(value1)); + } + + listSymbol.add(operator); + + if ("value".equals(value2)) { + values.add(Double.parseDouble(value)); + } else { + values.add(Double.valueOf(value2)); + } + } + + // 比较操作 + boolean result = compare(values.get(0), listSymbol.get(0), values.get(1)); + boolean resultTwo = true; + if (listSymbol.size() > 1) { + resultTwo = compare(values.get(1), listSymbol.get(1), values.get(2)); + } + + if (result && resultTwo) { + alramTrigger = true; + } else { + alramTrigger = false; + } + + return alramTrigger; + } + + private static boolean compare(double value1, String operator, double value2) { + switch (operator) { + case "<": + return value1 < value2; + case "<=": + return value1 <= value2; + case ">": + return value1 > value2; + case ">=": + return value1 >= value2; + case "==": + return value1 == value2; + case "!=": + return value1 != value2; + case "=": + return value1 == value2; + default: + throw new IllegalArgumentException("Unsupported comparison operator: " + operator); + } + } + + //执行告警相关动作 插入数据库 + private void doAlarmAction(DeviceAlarmRecord alarmRecord) { + //1.通过设备id 和信号id 查询告警参数设置表(告警规则) + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(DeviceAlarmParameter::getDeviceId, alarmRecord.getDeviceId()); + queryWrapper.eq(DeviceAlarmParameter::getSignalId, alarmRecord.getSignalId()); + List alarmParameters = deviceAlarmParameterMapper.selectList(queryWrapper); + for (DeviceAlarmParameter alarmParameter : alarmParameters) { + alarmRecord.setAlarmType(alarmParameter.getAlarmType()); + alarmRecord.setNoticeAction(alarmParameter.getNoticeType()); + alarmRecord.setNoticeLinkages(alarmParameter.getNoticeLinkages()); + //todo 发送邮件 + // pushEmail(alarmRecord,alarmParameter); + } + this.saveOrUpdate(alarmRecord); + } + + /** + * 判断当前时间在不在区间范围之内 + * + * @return true 在区间范围之内 + * false 不在时间范围之内 + */ + public Map getTimeInterval(int clock) { + int beforeclock = 0; + Map map = new HashMap<>(); + // 获取当前时间 + LocalDateTime now = LocalDateTime.now(); + + // 获取昨天的 7 点 + LocalDateTime yesterday7AM = LocalDateTime.of(now.minusDays(1).toLocalDate(), LocalTime.of(clock, 0)); + map.put("starttime", yesterday7AM); + // 获取今天的 7 点 + LocalDateTime today7AM = LocalDateTime.of(now.toLocalDate(), LocalTime.of(clock, 0)); + LocalDateTime nextday7AM = LocalDateTime.of(now.plusDays(1).toLocalDate(), LocalTime.of(clock, 0)); + map.put("endtime", today7AM); + // 判断时间是否在范围内 + if (now.isAfter(yesterday7AM) && now.isBefore(today7AM)) { + beforeclock = 1; + } else { + beforeclock = 0; + map.put("starttime", today7AM); + map.put("endtime", nextday7AM); + } + map.put("beforeclock", beforeclock); + return map; + } + + /** + * 根据条件查询告警记录表 判断是否存在告警记录 + * + * @return true 存在告警记录 + * false 不存在告警记录 + */ + public List queryDeviceAlarmRecord(DeviceAlarmRecord deviceAlarmRecord, + Map timeInterval) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(DeviceAlarmRecord::getSignalId, deviceAlarmRecord.getSignalId()); + queryWrapper.eq(DeviceAlarmRecord::getAlarmLevel, deviceAlarmRecord.getAlarmLevel()); + LocalDateTime starttime = (LocalDateTime) timeInterval.get("starttime"); + LocalDateTime endtime = (LocalDateTime) timeInterval.get("endtime"); + queryWrapper.between(DeviceAlarmRecord::getAlarmTime, starttime, endtime); + queryWrapper.in(DeviceAlarmRecord::getStatus, "01", "02"); + return this.list(queryWrapper); + + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceSignalServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceSignalServiceImpl.java new file mode 100644 index 0000000..be9c5e9 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceSignalServiceImpl.java @@ -0,0 +1,85 @@ +package com.yfd.platform.modules.auxcontrol.service.impl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import com.yfd.platform.modules.auxcontrol.domain.DeviceSignal; +import com.yfd.platform.modules.auxcontrol.mapper.DeviceSignalMapper; +import com.yfd.platform.modules.auxcontrol.service.IDeviceSignalService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +/** + *

+ * 变电站-辅控设备-信号 服务实现类 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@Service +public class DeviceSignalServiceImpl extends ServiceImpl implements IDeviceSignalService { + + @Resource + private DeviceSignalMapper deviceSignalMapper; + + /********************************** + * 用途说明: 修改变电站辅控设备信号 + * 参数说明 deviceSignal 变电站辅控设备信号 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回修改成功或者失败 + ***********************************/ + @Override + public boolean updateDeviceSignalValue(String slaveIp, String address, String type, String value, + String dateString) throws ParseException { + List list = null; + + if ("yx".equals(type)) { + // 遥信 + list = deviceSignalMapper.selectDeviceSignalYx(slaveIp, address); + } else if ("yc".equals(type)) { + // 遥测 + list = deviceSignalMapper.selectDeviceSignalYc(slaveIp, address); + } + + if (list != null && !list.isEmpty()) { + DeviceSignal deviceSignal = list.get(0); + + try { + if ("yx".equals(type)) { + // 遥信 + deviceSignal.setYxValue(Integer.parseInt(value)); + } else { + // 遥测 + deviceSignal.setYcValue(Convert.toBigDecimal(value)); + } + } catch (NumberFormatException e) { + // 处理转换异常,例如返回false或抛出更具体的异常 + return false; + } + + deviceSignal.setLastmodifier("系统更新"); + + // 设置最后修改时间 + Date parsedDate; + if (StrUtil.isEmpty(dateString) || "null".equals(dateString)) { + parsedDate = new Date(); + } else { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + parsedDate = dateFormat.parse(dateString); + } + Timestamp timestamp = new Timestamp(parsedDate.getTime()); + deviceSignal.setLastmodifydate(timestamp); + + // 更新并检查结果 + return this.updateById(deviceSignal); + } + + return false; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceWorkDataServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceWorkDataServiceImpl.java new file mode 100644 index 0000000..f151ed7 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/DeviceWorkDataServiceImpl.java @@ -0,0 +1,72 @@ +package com.yfd.platform.modules.auxcontrol.service.impl; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import com.yfd.platform.modules.auxcontrol.domain.DeviceWorkData; +import com.yfd.platform.modules.auxcontrol.mapper.DeviceSignalMapper; +import com.yfd.platform.modules.auxcontrol.mapper.DeviceWorkDataMapper; +import com.yfd.platform.modules.auxcontrol.service.IDeviceWorkDataService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + *

+ * 变电站-设备-运行数据 服务实现类 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@Service +public class DeviceWorkDataServiceImpl extends ServiceImpl implements IDeviceWorkDataService { + @Resource + private DeviceSignalMapper deviceSignalMapper; + + /********************************** + * 用途说明: 导出变电站设备运行数据 + * 参数说明 + * deviceWorkDatas 所需导出的变电站设备运行数据项集合 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回导出成功或者失败 + ***********************************/ + @Override + public void insertData(String from, String slaveIp, String address, String value, String dateTimeString) { + DeviceWorkData workdata = new DeviceWorkData(); + List> list = new ArrayList<>(); + if ("IEC104".equals(from)) { + list = deviceSignalMapper.selectDeviceSignal_Map(slaveIp, "yc", address); + } else if ("IEC61850".equals(from)) { + list = deviceSignalMapper.selectDeviceSignal_Map2("yc", address); + } + if (list.size() > 0) { + Map map = list.get(0); + workdata.setId(IdUtil.fastSimpleUUID()); + workdata.setSystemcode(map.get("systemcode").toString()); + workdata.setStationId(map.get("station_id").toString()); + workdata.setDeviceId(map.get("device_id").toString()); + workdata.setDeviceName(map.get("device_name").toString()); + workdata.setSignalId(map.get("signal_id").toString()); + workdata.setSignalName(map.get("signal_name").toString()); + workdata.setUnit(ObjUtil.isNotEmpty(map.get("signal_unit")) ? map.get("signal_unit").toString() : ""); + workdata.setValue(new BigDecimal(value)); + if (StrUtil.isEmpty(dateTimeString) || "null".equals(dateTimeString)) { + workdata.setStartTime(LocalDateTime.now()); + } else { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + LocalDateTime localDateTime = LocalDateTime.parse(dateTimeString, formatter); + workdata.setStartTime(localDateTime); + } + this.saveOrUpdate(workdata); + + } + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/GatewayDeviceServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/GatewayDeviceServiceImpl.java new file mode 100644 index 0000000..89713c9 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/GatewayDeviceServiceImpl.java @@ -0,0 +1,38 @@ +package com.yfd.platform.modules.auxcontrol.service.impl; + +import cn.hutool.core.util.ObjUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.yfd.platform.modules.auxcontrol.domain.GatewayDevice; +import com.yfd.platform.modules.auxcontrol.mapper.GatewayDeviceMapper; +import com.yfd.platform.modules.auxcontrol.service.IGatewayDeviceService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +import java.sql.Timestamp; + +/** + *

+ * 变电站-通讯网关设备 服务实现类 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@Service +public class GatewayDeviceServiceImpl extends ServiceImpl implements IGatewayDeviceService { + + /********************************** + * 用途说明: 根据IP,更新心跳保持时间 + * 参数说明 + * ip 终端对应IP + * 返回值说明: 无 + ***********************************/ + @Override + public void updateKeepLiveTime(String ip) { + LambdaUpdateWrapper queryWrapper = new LambdaUpdateWrapper<>(); + queryWrapper.eq(GatewayDevice::getIpAddr, ip).set(GatewayDevice::getStatus, "01").set(GatewayDevice::getKeepliveTime, new Timestamp(System.currentTimeMillis())); + this.update(queryWrapper); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/MeterDeviceServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/MeterDeviceServiceImpl.java new file mode 100644 index 0000000..8edde78 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/auxcontrol/service/impl/MeterDeviceServiceImpl.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.auxcontrol.service.impl; + +import com.yfd.platform.modules.auxcontrol.domain.MeterDevice; +import com.yfd.platform.modules.auxcontrol.mapper.MeterDeviceMapper; +import com.yfd.platform.modules.auxcontrol.service.IMeterDeviceService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + *

+ * 变电站二次设备中的智能仪表监控设备 服务实现类 + *

+ * + * @author zhengsl + * @since 2025-04-23 + */ +@Service +public class MeterDeviceServiceImpl extends ServiceImpl implements IMeterDeviceService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/CodeGenerator.java b/riis-system/src/main/java/com/yfd/platform/utils/CodeGenerator.java index dd9abe0..de2da24 100644 --- a/riis-system/src/main/java/com/yfd/platform/utils/CodeGenerator.java +++ b/riis-system/src/main/java/com/yfd/platform/utils/CodeGenerator.java @@ -63,7 +63,7 @@ public class CodeGenerator { // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); - dsc.setUrl("jdbc:mysql://43.138.168.68:3306/riisdb500_zhuangzhou?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai"); + dsc.setUrl("jdbc:mysql://43.138.168.68:3306/smartsubstationdb?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("ylfw20230626@"); @@ -171,7 +171,7 @@ public class CodeGenerator { //rca_project,rca_projectanalysisrd,rca_projectinvestigaterd,rca_projectreport,rca_projectsummary,rca_projecttarget,rca_projecttrackrd,rca_analysisguide,rca_eventeffect,rca_failurecase,rca_failurecause,rca_failureclass,rca_failuremode strategy.setInclude(scanner("表名,多个英文逗号分割").split(",")); strategy.setControllerMappingHyphenStyle(true); - strategy.setTablePrefix("iis_"); + strategy.setTablePrefix("fk_"); mpg.setStrategy(strategy); mpg.setTemplateEngine(new FreemarkerTemplateEngine()); mpg.execute(); diff --git a/riis-system/src/main/resources/mapper/auxcontrol/DeviceAlarmParameterMapper.xml b/riis-system/src/main/resources/mapper/auxcontrol/DeviceAlarmParameterMapper.xml new file mode 100644 index 0000000..46d775f --- /dev/null +++ b/riis-system/src/main/resources/mapper/auxcontrol/DeviceAlarmParameterMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/auxcontrol/DeviceAlarmRecordMapper.xml b/riis-system/src/main/resources/mapper/auxcontrol/DeviceAlarmRecordMapper.xml new file mode 100644 index 0000000..84c86f8 --- /dev/null +++ b/riis-system/src/main/resources/mapper/auxcontrol/DeviceAlarmRecordMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/auxcontrol/DeviceSignalMapper.xml b/riis-system/src/main/resources/mapper/auxcontrol/DeviceSignalMapper.xml new file mode 100644 index 0000000..6421434 --- /dev/null +++ b/riis-system/src/main/resources/mapper/auxcontrol/DeviceSignalMapper.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + diff --git a/riis-system/src/main/resources/mapper/auxcontrol/DeviceWorkDataMapper.xml b/riis-system/src/main/resources/mapper/auxcontrol/DeviceWorkDataMapper.xml new file mode 100644 index 0000000..9481316 --- /dev/null +++ b/riis-system/src/main/resources/mapper/auxcontrol/DeviceWorkDataMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/auxcontrol/GatewayDeviceMapper.xml b/riis-system/src/main/resources/mapper/auxcontrol/GatewayDeviceMapper.xml new file mode 100644 index 0000000..45c6d49 --- /dev/null +++ b/riis-system/src/main/resources/mapper/auxcontrol/GatewayDeviceMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/auxcontrol/MeterDeviceMapper.xml b/riis-system/src/main/resources/mapper/auxcontrol/MeterDeviceMapper.xml new file mode 100644 index 0000000..63192b9 --- /dev/null +++ b/riis-system/src/main/resources/mapper/auxcontrol/MeterDeviceMapper.xml @@ -0,0 +1,5 @@ + + + + +