辅控项目代码整合初版
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