辅控项目代码整合初版
This commit is contained in:
		
							parent
							
								
									fcf11fe63c
								
							
						
					
					
						commit
						1248e5543b
					
				@ -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<GatewayDevice> 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<GatewayDevice> 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);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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<MessageInfo> 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<MessageInfo> 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);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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<MessageInfo> 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<MessageInfo> 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<MessageInfo> 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<MessageInfo> 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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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<MessageInfo> 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<MessageInfo> 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();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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<ScheduledTaskPool> scheduledTaskPoolThreadLocal = new ThreadLocal<>();
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 返回 发送序号 和接收序号  定时发送S帧
 | 
			
		||||
	 */
 | 
			
		||||
	private static ThreadLocal<ControlManageUtil> controlPoolThreadLocal = new ThreadLocal<>();
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 存放相关配置文件
 | 
			
		||||
	 */
 | 
			
		||||
	private static ThreadLocal<Iec104Config> 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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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(); // 保持中断状态
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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<MessageInfo> 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<MessageInfo> 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<>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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<byte[]> {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void encode(ChannelHandlerContext ctx, byte[] msg, ByteBuf out) throws Exception {
 | 
			
		||||
		out.writeBytes(msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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<Object> 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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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<MessageDetail> {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	@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);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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<Object> 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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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<String, BootNettyClientChannel> channelMapCache = new ConcurrentHashMap<String, BootNettyClientChannel>();
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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<String, EventLoopGroup> 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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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<SocketChannel> {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	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));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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<MessageDetail> {
 | 
			
		||||
	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);
 | 
			
		||||
					}
 | 
			
		||||
    			}
 | 
			
		||||
    		});
 | 
			
		||||
    	}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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<SocketChannel> {
 | 
			
		||||
 | 
			
		||||
	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));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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<MessageDetail> {
 | 
			
		||||
    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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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<Byte> 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-告警参数设置 前端控制器
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("/auxcontrol/device-alarm-parameter")
 | 
			
		||||
public class DeviceAlarmParameterController {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-告警记录 前端控制器
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("/auxcontrol/device-alarm-record")
 | 
			
		||||
public class DeviceAlarmRecordController {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-信号 前端控制器
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("/auxcontrol/device-signal")
 | 
			
		||||
public class DeviceSignalController {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-设备-运行数据 前端控制器
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("/auxcontrol/device-work-data")
 | 
			
		||||
public class DeviceWorkDataController {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-通讯网关设备 前端控制器
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("/auxcontrol/gateway-device")
 | 
			
		||||
public class GatewayDeviceController {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站二次设备中的智能仪表监控设备 前端控制器
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
@RestController
 | 
			
		||||
@RequestMapping("/auxcontrol/meter-device")
 | 
			
		||||
public class MeterDeviceController {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-告警参数设置
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-告警记录
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-信号
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-设备-运行数据
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-通讯网关设备
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站二次设备中的智能仪表监控设备
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-告警参数设置 Mapper 接口
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
public interface DeviceAlarmParameterMapper extends BaseMapper<DeviceAlarmParameter> {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-告警记录 Mapper 接口
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
public interface DeviceAlarmRecordMapper extends BaseMapper<DeviceAlarmRecord> {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-信号 Mapper 接口
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
public interface DeviceSignalMapper extends BaseMapper<DeviceSignal> {
 | 
			
		||||
 | 
			
		||||
    List<DeviceSignal> selectDeviceSignalYx(String slave_ip, String address);
 | 
			
		||||
    List<DeviceSignal> selectDeviceSignalYc(String slave_ip, String address);
 | 
			
		||||
    List<Map<String,Object>> selectDeviceSignal_Map(String slave_ip, String type, String address);
 | 
			
		||||
    List<Map<String,Object>> selectDeviceSignal_Map2(String type,String address);
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-设备-运行数据 Mapper 接口
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
public interface DeviceWorkDataMapper extends BaseMapper<DeviceWorkData> {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-通讯网关设备 Mapper 接口
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
public interface GatewayDeviceMapper extends BaseMapper<GatewayDevice> {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站二次设备中的智能仪表监控设备 Mapper 接口
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
public interface MeterDeviceMapper extends BaseMapper<MeterDevice> {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-告警参数设置 服务类
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
public interface IDeviceAlarmParameterService extends IService<DeviceAlarmParameter> {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-告警记录 服务类
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
public interface IDeviceAlarmRecordService extends IService<DeviceAlarmRecord> {
 | 
			
		||||
 | 
			
		||||
    /**********************************
 | 
			
		||||
     * 用途说明: 生成报警记录
 | 
			
		||||
     * 参数说明
 | 
			
		||||
     * from  'IEC104','IEC61850'
 | 
			
		||||
     * type  'yx','yc'
 | 
			
		||||
     * slave_ip   服务器IP
 | 
			
		||||
     * address   地址
 | 
			
		||||
     * value         值
 | 
			
		||||
     * 返回值说明: 无返回值
 | 
			
		||||
     ***********************************/
 | 
			
		||||
    void doAlaramRecord(String from,String type,String slaveIp,String address,String value);
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-信号 服务类
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
public interface IDeviceSignalService extends IService<DeviceSignal> {
 | 
			
		||||
 | 
			
		||||
    /**********************************
 | 
			
		||||
     * 用途说明: 遥测 、遥信数据更新到数据库
 | 
			
		||||
     * 参数说明
 | 
			
		||||
     * netaddres  网关机设备地址
 | 
			
		||||
     * address  信号地址
 | 
			
		||||
     * type  信号类型  yx-遥信 yc-遥测
 | 
			
		||||
     * value  数据采集结果
 | 
			
		||||
     * dateTime  数据采集时标
 | 
			
		||||
     * 返回值说明: bool 更新成功或者失败
 | 
			
		||||
     ***********************************/
 | 
			
		||||
    boolean updateDeviceSignalValue(String slaveIp, String address, String type, String value, String dateTime) throws ParseException;
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-设备-运行数据 服务类
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
public interface IDeviceWorkDataService extends IService<DeviceWorkData> {
 | 
			
		||||
 | 
			
		||||
    /**********************************
 | 
			
		||||
     * 用途说明: 从设备中采集数据,并保存到数据库中
 | 
			
		||||
     * 参数说明
 | 
			
		||||
     *  from  'IEC104','IEC61850'
 | 
			
		||||
     * slave_ip  通信网关机地址
 | 
			
		||||
     * address  信号地址
 | 
			
		||||
     * value  数据采集结果
 | 
			
		||||
     * dateTime  数据采集时标
 | 
			
		||||
     * 返回值说明: 无
 | 
			
		||||
     ***********************************/
 | 
			
		||||
    void insertData(String from, String slave_ip, String address, String value, String dateTimeString);
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-通讯网关设备 服务类
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
public interface IGatewayDeviceService extends IService<GatewayDevice> {
 | 
			
		||||
 | 
			
		||||
    /**********************************
 | 
			
		||||
     * 用途说明: 根据IP,更新心跳保持时间
 | 
			
		||||
     * 参数说明
 | 
			
		||||
     * ip 终端对应IP
 | 
			
		||||
     * 返回值说明: 无
 | 
			
		||||
     ***********************************/
 | 
			
		||||
    void updateKeepLiveTime(String ip);
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站二次设备中的智能仪表监控设备 服务类
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
public interface IMeterDeviceService extends IService<MeterDevice> {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-告警参数设置 服务实现类
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
@Service
 | 
			
		||||
public class DeviceAlarmParameterServiceImpl extends ServiceImpl<DeviceAlarmParameterMapper, DeviceAlarmParameter> implements IDeviceAlarmParameterService {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-告警记录 服务实现类
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
@Service
 | 
			
		||||
public class DeviceAlarmRecordServiceImpl extends ServiceImpl<DeviceAlarmRecordMapper, DeviceAlarmRecord> 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<Map<String, Object>> 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<String, Object> 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<String, Object> timeInterval = getTimeInterval(7);
 | 
			
		||||
                    //如果在时间范围内
 | 
			
		||||
                    if ("1".equals(timeInterval.get("beforeclock").toString())) {
 | 
			
		||||
                        //查询表中是否存在该报警记录
 | 
			
		||||
                        List<DeviceAlarmRecord> alarmRecords = queryDeviceAlarmRecord(deviceAlarmRecord, timeInterval);
 | 
			
		||||
                        //如果没有有报警记录 新增记录
 | 
			
		||||
                        if (alarmRecords.size() == 0) {
 | 
			
		||||
                            doAlarmAction(deviceAlarmRecord);
 | 
			
		||||
                        } else if (alarmRecords.size() > 1) {
 | 
			
		||||
                            this.removeById(alarmRecords.get(0).getRecordId());
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        List<DeviceAlarmRecord> 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<DeviceAlarmRecord> 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<DeviceAlarmParameter> 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<DeviceAlarmParameter> 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<String, Object> timeInterval = getTimeInterval(7);
 | 
			
		||||
                            //如果在时间范围内
 | 
			
		||||
                            if ("1".equals(timeInterval.get("beforeclock").toString())) {
 | 
			
		||||
                                //查询表中是否存在该报警记录
 | 
			
		||||
                                List<DeviceAlarmRecord> alarmRecords = queryDeviceAlarmRecord(deviceAlarmRecord,
 | 
			
		||||
                                        timeInterval);
 | 
			
		||||
                                //如果没有有报警记录 新增记录
 | 
			
		||||
                                if (alarmRecords.size() == 0) {
 | 
			
		||||
                                    //  新增
 | 
			
		||||
                                    deviceAlarmRecordMapper.insert(deviceAlarmRecord);
 | 
			
		||||
                                    //TODO 邮件推送暂时删除了
 | 
			
		||||
                                }
 | 
			
		||||
                            } else {
 | 
			
		||||
                                List<DeviceAlarmRecord> alarmRecords = queryDeviceAlarmRecord(deviceAlarmRecord,
 | 
			
		||||
                                        timeInterval);
 | 
			
		||||
                                if (alarmRecords.size() == 0) {
 | 
			
		||||
                                    deviceAlarmRecordMapper.insert(deviceAlarmRecord);
 | 
			
		||||
                                    //TODO 邮件推送暂时删除了
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            //不满足条件 同一信号监测到报警状态消除,对当前信号所有未处理历史报警打上已处理标签,并记录消除时间
 | 
			
		||||
                            LambdaUpdateWrapper<DeviceAlarmRecord> 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<SysDictionaryItems> 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<String> listSymbol = new ArrayList<>();
 | 
			
		||||
        List<Double> 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<DeviceAlarmParameter> queryWrapper = new LambdaQueryWrapper<>();
 | 
			
		||||
        queryWrapper.eq(DeviceAlarmParameter::getDeviceId, alarmRecord.getDeviceId());
 | 
			
		||||
        queryWrapper.eq(DeviceAlarmParameter::getSignalId, alarmRecord.getSignalId());
 | 
			
		||||
        List<DeviceAlarmParameter> 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<String, Object> getTimeInterval(int clock) {
 | 
			
		||||
        int beforeclock = 0;
 | 
			
		||||
        Map<String, Object> 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<DeviceAlarmRecord> queryDeviceAlarmRecord(DeviceAlarmRecord deviceAlarmRecord,
 | 
			
		||||
                                                          Map<String, Object> timeInterval) {
 | 
			
		||||
        LambdaQueryWrapper<DeviceAlarmRecord> 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);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-辅控设备-信号 服务实现类
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
@Service
 | 
			
		||||
public class DeviceSignalServiceImpl extends ServiceImpl<DeviceSignalMapper, DeviceSignal> 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<DeviceSignal> 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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-设备-运行数据 服务实现类
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
@Service
 | 
			
		||||
public class DeviceWorkDataServiceImpl extends ServiceImpl<DeviceWorkDataMapper, DeviceWorkData> 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<Map<String, Object>> 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<String, Object> 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);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站-通讯网关设备 服务实现类
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
@Service
 | 
			
		||||
public class GatewayDeviceServiceImpl extends ServiceImpl<GatewayDeviceMapper, GatewayDevice> implements IGatewayDeviceService {
 | 
			
		||||
 | 
			
		||||
    /**********************************
 | 
			
		||||
     * 用途说明: 根据IP,更新心跳保持时间
 | 
			
		||||
     * 参数说明
 | 
			
		||||
     * ip 终端对应IP
 | 
			
		||||
     * 返回值说明: 无
 | 
			
		||||
     ***********************************/
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateKeepLiveTime(String ip) {
 | 
			
		||||
        LambdaUpdateWrapper<GatewayDevice> queryWrapper = new LambdaUpdateWrapper<>();
 | 
			
		||||
        queryWrapper.eq(GatewayDevice::getIpAddr, ip).set(GatewayDevice::getStatus, "01").set(GatewayDevice::getKeepliveTime, new Timestamp(System.currentTimeMillis()));
 | 
			
		||||
        this.update(queryWrapper);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 变电站二次设备中的智能仪表监控设备 服务实现类
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author zhengsl
 | 
			
		||||
 * @since 2025-04-23
 | 
			
		||||
 */
 | 
			
		||||
@Service
 | 
			
		||||
public class MeterDeviceServiceImpl extends ServiceImpl<MeterDeviceMapper, MeterDevice> implements IMeterDeviceService {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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();
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 | 
			
		||||
<mapper namespace="com.yfd.platform.modules.auxcontrol.mapper.DeviceAlarmParameterMapper">
 | 
			
		||||
 | 
			
		||||
</mapper>
 | 
			
		||||
@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 | 
			
		||||
<mapper namespace="com.yfd.platform.modules.auxcontrol.mapper.DeviceAlarmRecordMapper">
 | 
			
		||||
 | 
			
		||||
</mapper>
 | 
			
		||||
@ -0,0 +1,72 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 | 
			
		||||
<mapper namespace="com.yfd.platform.modules.auxcontrol.mapper.DeviceSignalMapper">
 | 
			
		||||
 | 
			
		||||
    <!-- 查询辅控设备信号表-->
 | 
			
		||||
    <select id="selectDeviceSignalYx" resultType="com.yfd.platform.modules.auxcontrol.domain.DeviceSignal">
 | 
			
		||||
    SELECT
 | 
			
		||||
        b.*
 | 
			
		||||
    FROM
 | 
			
		||||
        fk_device a
 | 
			
		||||
        JOIN fk_device_signal b ON ( a.device_id = b.device_id )
 | 
			
		||||
        JOIN fk_system_device c ON ( a.netdevice_ip = c.ip_addr )
 | 
			
		||||
    WHERE
 | 
			
		||||
        c.ip_addr = #{slave_ip}
 | 
			
		||||
 | 
			
		||||
        AND b.yx_addr = #{address}
 | 
			
		||||
    </select>
 | 
			
		||||
 | 
			
		||||
    <select id="selectDeviceSignalYc" resultType="com.yfd.platform.modules.auxcontrol.domain.DeviceSignal">
 | 
			
		||||
    SELECT
 | 
			
		||||
        b.*
 | 
			
		||||
    FROM
 | 
			
		||||
        fk_device a
 | 
			
		||||
        JOIN fk_device_signal b ON ( a.device_id = b.device_id )
 | 
			
		||||
        JOIN fk_system_device c ON ( a.netdevice_ip = c.ip_addr )
 | 
			
		||||
    WHERE
 | 
			
		||||
        c.ip_addr = #{slave_ip}
 | 
			
		||||
 | 
			
		||||
        AND b.yc_addr = #{address}
 | 
			
		||||
    </select>
 | 
			
		||||
 | 
			
		||||
    <!-- 查询辅控设备信号表-->
 | 
			
		||||
    <select id="selectDeviceSignal_Map" resultType="java.util.Map">
 | 
			
		||||
        SELECT
 | 
			
		||||
        a.station_id,
 | 
			
		||||
        a.systemcode,
 | 
			
		||||
        a.device_name,
 | 
			
		||||
        b.*
 | 
			
		||||
        FROM
 | 
			
		||||
        fk_device a
 | 
			
		||||
        JOIN fk_device_signal b ON ( a.device_id = b.device_id )
 | 
			
		||||
        JOIN fk_system_device c ON ( a.netdevice_ip = c.ip_addr )
 | 
			
		||||
        WHERE
 | 
			
		||||
        c.ip_addr = #{slave_ip}
 | 
			
		||||
        <if test="type=='yx'">
 | 
			
		||||
            and b.yx_addr = #{address}
 | 
			
		||||
        </if>
 | 
			
		||||
        <if test="type=='yc'">
 | 
			
		||||
            and b.yc_addr = #{address}
 | 
			
		||||
        </if>
 | 
			
		||||
    </select>
 | 
			
		||||
 | 
			
		||||
    <!-- 查询辅控设备信号表-->
 | 
			
		||||
    <select id="selectDeviceSignal_Map2" resultType="java.util.Map">
 | 
			
		||||
        SELECT
 | 
			
		||||
        a.station_id,
 | 
			
		||||
        a.systemcode,
 | 
			
		||||
        a.device_name,
 | 
			
		||||
        b.*
 | 
			
		||||
        FROM
 | 
			
		||||
        fk_device a
 | 
			
		||||
        JOIN fk_device_signal b ON ( a.device_id = b.device_id )
 | 
			
		||||
        WHERE
 | 
			
		||||
        1 =1
 | 
			
		||||
        <if test="type=='yx'">
 | 
			
		||||
            and b.yx_addr = #{address}
 | 
			
		||||
        </if>
 | 
			
		||||
        <if test="type=='yc'">
 | 
			
		||||
            and b.yc_addr = #{address}
 | 
			
		||||
        </if>
 | 
			
		||||
    </select>
 | 
			
		||||
</mapper>
 | 
			
		||||
@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 | 
			
		||||
<mapper namespace="com.yfd.platform.modules.auxcontrol.mapper.DeviceWorkDataMapper">
 | 
			
		||||
 | 
			
		||||
</mapper>
 | 
			
		||||
@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 | 
			
		||||
<mapper namespace="com.yfd.platform.modules.auxcontrol.mapper.GatewayDeviceMapper">
 | 
			
		||||
 | 
			
		||||
</mapper>
 | 
			
		||||
@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 | 
			
		||||
<mapper namespace="com.yfd.platform.modules.auxcontrol.mapper.MeterDeviceMapper">
 | 
			
		||||
 | 
			
		||||
</mapper>
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user