增加IEC61850协议代码

This commit is contained in:
weitang 2025-04-27 11:07:16 +08:00
parent 621b252d6a
commit 667a637d92
11 changed files with 350 additions and 11 deletions

View File

@ -17,7 +17,7 @@ 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.OptimizedThreadPool;
import com.yfd.platform.component.utils.OptimizedThreadPool;
import com.yfd.platform.component.iec104.server.Iec104Master;
import com.yfd.platform.component.iec104.server.Iec104MasterFactory;
import com.yfd.platform.modules.auxcontrol.domain.GatewayDevice;

View File

@ -1,11 +1,9 @@
package com.yfd.platform.component.iec104.core;
import com.yfd.platform.component.iec104.client.MasterSysDataHandler;
import com.yfd.platform.component.iec104.common.Iec104Constant;
import com.yfd.platform.component.iec104.message.MessageDetail;
import com.ydl.iec.util.Iec104Util;
import com.yfd.platform.component.iec104.server.Iec104Master;
import com.yfd.platform.component.iec104.server.Iec104MasterFactory;
import com.yfd.platform.component.utils.OptimizedThreadPool;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -1,13 +1,11 @@
package com.yfd.platform.component.iec104.core;
import cn.hutool.core.util.ObjUtil;
import com.yfd.platform.component.iec104.client.MasterSysDataHandler;
import com.yfd.platform.component.iec104.common.BasicInstruction104;
import com.yfd.platform.component.iec104.message.MessageDetail;
import com.yfd.platform.component.iec104.server.Iec104Master;
import com.yfd.platform.component.iec104.server.Iec104MasterFactory;
import com.yfd.platform.component.iec104.server.master.BootNettyClientChannel;
import com.yfd.platform.component.iec104.server.master.BootNettyClientChannelCache;
import com.yfd.platform.component.utils.OptimizedThreadPool;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;

View File

@ -1,7 +1,6 @@
package com.yfd.platform.component.iec104.server.master.handler;
import cn.hutool.core.util.ObjUtil;
import com.yfd.platform.component.iec104.common.BasicInstruction104;
import com.yfd.platform.component.iec104.core.*;
import com.yfd.platform.component.iec104.message.MessageDetail;
import com.yfd.platform.component.iec104.server.handler.ChannelHandlerImpl;
@ -9,6 +8,7 @@ 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 com.yfd.platform.component.utils.OptimizedThreadPool;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;

View File

@ -2,7 +2,7 @@ package com.yfd.platform.component.iec104.server.slave.handler;
import com.yfd.platform.component.iec104.core.ControlManageUtil;
import com.yfd.platform.component.iec104.core.Iec104ThreadLocal;
import com.yfd.platform.component.iec104.core.OptimizedThreadPool;
import com.yfd.platform.component.utils.OptimizedThreadPool;
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;

View File

@ -0,0 +1,21 @@
package com.yfd.platform.component.iec61850.client;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ClientConnetContext {
private static Map<String, Object> contextMap = new ConcurrentHashMap<>();
public static void put(String key, Object value) {
contextMap.put(key, value);
}
public static Object get(String key) {
return contextMap.get(key);
}
public static void remove(String key) {
contextMap.remove(key);
}
}

View File

@ -0,0 +1,77 @@
package com.yfd.platform.component.iec61850.client;
import com.beanit.iec61850bean.*;
import com.yfd.platform.modules.auxcontrol.mapper.DeviceSignalMapper;
import com.yfd.platform.modules.auxcontrol.service.IDeviceAlarmRecordService;
import com.yfd.platform.modules.auxcontrol.service.IDeviceWorkDataService;
import com.yfd.platform.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
@Component
@RequiredArgsConstructor
public class IEC61850ClientListener extends Thread implements ClientEventListener {
private static final Logger log = LoggerFactory.getLogger(StringUtils.class);
private final DeviceSignalMapper deviceSignalMapper;
private final IDeviceAlarmRecordService deviceAlarmRecordService;
private final IDeviceWorkDataService deviceWorkDataService;
@Override
public void newReport(Report report) {
//遥测 测量值浮点数
for (FcModelNode value : report.getValues()) {
if ("MX".equals(value.getFc().name())) {
String node_name = value.getChild("mag").getChild("f").toString().split(":")[0].replace(".mag.f", "");
BdaFloat32 f = (BdaFloat32) value.getChild("mag").getChild("f");
String node_value = f.getValueString();
BdaTimestamp t = (BdaTimestamp) value.getChild("t");
String datestr = changeUTCtoLocalTime(t.getValueString());
log.info(node_name + ":" + node_value + ":" + datestr);
//更新信号数据值
deviceSignalMapper.updateDeviceSignalValue_yc(node_name, node_value, datestr);
//插入历史数据
deviceWorkDataService.insertData("IEC61850", null, node_name, node_value, datestr);
//执行报警处理
deviceAlarmRecordService.doAlaramRecord("IEC61850", "yc", null, node_name, node_value);
} else if ("ST".equals(value.getFc().name())) {
//遥信 状态值
String node_name = value.getChild("stVal").toString().split(":")[0].replace(".stVal", "");
BdaBoolean state = (BdaBoolean) value.getChild("stVal");
String node_value = state.getValue() ? "1" : "0";
BdaTimestamp t = (BdaTimestamp) value.getChild("t");
String datestr = changeUTCtoLocalTime(t.getValueString());
log.info(node_name + ":" + node_value + ":" + datestr);
//更新信号状态
deviceSignalMapper.updateDeviceSignalValue_yx(node_name, node_value, datestr);
//执行报警处理生成设备自身报警记录status=1
deviceAlarmRecordService.doAlaramRecord("IEC61850", "yx", null, node_name, node_value);
}
}
}
private String changeUTCtoLocalTime(String utctime) {
// 解析UTC时间字符串为ZonedDateTime对象
ZonedDateTime utcDateTime = ZonedDateTime.parse(utctime);
// 将UTC时间转换为本地时间假设本地时区为Asia/Shanghai
ZonedDateTime localDateTime = utcDateTime.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
DateTimeFormatter LocalFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
return localDateTime.format(LocalFormatter);
}
@Override
public void associationClosed(IOException e) {
log.info("客户端已连接到服务器!");
}
}

View File

@ -0,0 +1,55 @@
package com.yfd.platform.component.iec61850.client;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yfd.platform.component.utils.OptimizedThreadPool;
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 IEC61850ClientRunner implements ApplicationRunner {
private static final Logger log = LoggerFactory.getLogger(IEC61850ClientRunner.class);
private final GatewayDeviceMapper gatewayDeviceMapper;
private final IEC61850Service iec61850Service;
/**
* 项目启动时激活启用的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, "IEC61850");
queryWrapper.select(GatewayDevice::getId, GatewayDevice::getDeviceCode, GatewayDevice::getDeviceName,
GatewayDevice::getIpAddr, GatewayDevice::getIecAddr, GatewayDevice::getUrcb, GatewayDevice::getBrcb);
queryWrapper.orderByAsc(GatewayDevice::getDeviceCode);
List<GatewayDevice> list = gatewayDeviceMapper.selectList(queryWrapper);
if (!list.isEmpty()) {
for (GatewayDevice systemDevice : list) {
OptimizedThreadPool threadPool = OptimizedThreadPool.getInstance();
threadPool.execute(() -> iec61850Service.connect(systemDevice));
}
}
}
}

View File

@ -0,0 +1,186 @@
package com.yfd.platform.component.iec61850.client;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.beanit.iec61850bean.*;
import com.yfd.platform.modules.auxcontrol.domain.DeviceSignal;
import com.yfd.platform.modules.auxcontrol.domain.GatewayDevice;
import com.yfd.platform.modules.auxcontrol.domain.MeterDevice;
import com.yfd.platform.modules.auxcontrol.service.IDeviceSignalService;
import com.yfd.platform.modules.auxcontrol.service.IGatewayDeviceService;
import com.yfd.platform.modules.auxcontrol.service.IMeterDeviceService;
import com.yfd.platform.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.net.InetAddress;
@Service
public class IEC61850Service {
private static final Logger log = LoggerFactory.getLogger(StringUtils.class);
@Resource
private IMeterDeviceService meterDeviceService;
@Resource
private IGatewayDeviceService gatewayDeviceService;
@Resource
private IDeviceSignalService deviceSignalService;
@Resource
private IEC61850ClientListener clientListener;
/**
* 连接到设备并检索服务器模型
*
* @param gatewayDevice 设备信息
*/
public boolean connect(GatewayDevice gatewayDevice) {
ClientSap clientSap = new ClientSap();
clientSap.setTSelRemote(new byte[]{0, 1});
clientSap.setTSelLocal(new byte[]{0, 0});
clientSap.setApTitleCalled(new int[]{1, 1, 999, 1});
ClientAssociation clientAssociation = null;
try {
clientAssociation = clientSap.associate(InetAddress.getByName(gatewayDevice.getIpAddr()), 102, null,
clientListener);
//保存到系统上下文中
ClientConnetContext.put(gatewayDevice.getIpAddr(), clientAssociation);
ServerModel serverModel = clientAssociation.retrieveModel();
//"TEMPLATELD_Device0/LLN0.urcbAin1p01"
String urcbs = gatewayDevice.getUrcb();
if (StrUtil.isNotEmpty(urcbs)) {
String[] urcbArray = urcbs.split(",");
for (String s : urcbArray) {
Urcb urcb = serverModel.getUrcb(s);
clientAssociation.getRcbValues(urcb);
clientAssociation.reserveUrcb(urcb);
clientAssociation.enableReporting(urcb);
}
}
//"TEMPLATELD_Device8/LLN0.brcbDin1p01"
String brcbs = gatewayDevice.getBrcb();
if (StrUtil.isNotEmpty(brcbs)) {
String[] brcbArray = brcbs.split(",");
for (String s : brcbArray) {
Brcb brcb = serverModel.getBrcb(s);
clientAssociation.getRcbValues(brcb);
clientAssociation.enableReporting(brcb);
}
}
//sendCommand("", "a397bf1d4042ae948d248b1f1856937e", "yk", "true");
//sendCommand("", "a397bf1d4042ae948d248b1f1856937e", "yt", "25.6");
} catch (ServiceError | IOException e) {
log.info("未能关联或检索设备的模型 %s: %s", gatewayDevice.getDeviceCode(),
e.getMessage());
}
return true;
}
/**
* 从服务器断开连接
*
* @param gatewayDevice 设备信息
*/
public boolean disconnect(GatewayDevice gatewayDevice) {
ClientAssociation association = (ClientAssociation) ClientConnetContext.get(gatewayDevice.getIpAddr());
association.disconnect();
return true;
}
/**
* 给设备发送遥控遥调命令
* <p>
* systemcode 系统编号
* signalid 信号id
* type 类型ykyt
* value
*/
public boolean sendCommand(String systemcode, String signalid, String type, String value) throws IOException,
ServiceError {
DeviceSignal deviceSignal = deviceSignalService.getById(signalid);
//辅控系统下属网关机状态监控
if (StrUtil.isNotEmpty(systemcode) && "10".equals(systemcode)) {
if (ObjUtil.isNotEmpty(deviceSignal)) {
LambdaQueryWrapper<GatewayDevice> queryWrapper = new LambdaQueryWrapper<>();
//类型
queryWrapper.eq(GatewayDevice::getDeviceType, "30");
//通讯网关机的iec地址
queryWrapper.eq(GatewayDevice::getIecAddr, deviceSignal.getYxAddr());
//查找网关机
GatewayDevice gatewayDevice = gatewayDeviceService.getOne(queryWrapper);
//连接到主机
if ("1".equals(value)) {
//连接到子系统的通讯网关
connect(gatewayDevice);
} else {
//断开与子系统通讯网关的连接
disconnect(gatewayDevice);
}
}
} else {
if (ObjUtil.isNotEmpty(deviceSignal)) {
//查找设备
MeterDevice meterDevice = meterDeviceService.getById(deviceSignal.getMeterDeviceId());
QueryWrapper<GatewayDevice> queryWrapper = new QueryWrapper<>();
//通讯网关机的IP地址
queryWrapper.eq("ip_addr", meterDevice.getNetdeviceIp());
//查找网关机
GatewayDevice gatewayDevice = gatewayDeviceService.getOne(queryWrapper);
String convalue = value;
if (ObjUtil.isNotEmpty(gatewayDevice)) {
String address = "";
if ("yk".equals(type)) {
convalue = "1".equals(value) ? "true" : "false";
address = deviceSignal.getYkAddr();
} else if ("yt".equals(type)) {
address = deviceSignal.getYtAddr();
}
//获取连接
ClientAssociation association =
(ClientAssociation) ClientConnetContext.get(meterDevice.getNetdeviceIp());
if (ObjUtil.isNotEmpty(association)) {
//检索模型
ServerModel serverModel = association.retrieveModel();
if ("true".equals(convalue) || "false".equals(convalue)) {
FcModelNode fcModelNode = (FcModelNode) serverModel.findModelNode(address, Fc.CO);
ModelNode oper = fcModelNode.getChild("Oper");
BdaBoolean ctlVal = (BdaBoolean) oper.getChild("ctlVal");
boolean b_value = Boolean.parseBoolean(convalue);
ctlVal.setValue(b_value);
association.operate(fcModelNode);
} else {
FcModelNode fcModelNode = (FcModelNode) serverModel.findModelNode(address, Fc.SP);
if (fcModelNode == null) {
throw new IllegalArgumentException("Model node not found for address: " + address);
}
// 获取 "SetVal" 子节点
ModelNode setValNode = fcModelNode.getChild("SetVal");
if (!(setValNode instanceof BdaFloat32)) {
throw new IllegalArgumentException("'SetVal' child node not found or not of type " +
"BdaFloat32 for model node: " + address);
}
// "SetVal" 子节点强制转换为 BdaFloat32 并设置值
BdaFloat32 setVal = (BdaFloat32) setValNode;
float temperature = Float.parseFloat(convalue); // 目标温度值例如 22.5 摄氏度
setVal.setFloat(temperature);
// 执行操作命令
association.setDataValues(fcModelNode);
}
}
}
}
}
return false;
}
}

View File

@ -1,4 +1,4 @@
package com.yfd.platform.component.iec104.core;
package com.yfd.platform.component.utils;
import org.jetbrains.annotations.NotNull;

View File

@ -2,6 +2,7 @@ package com.yfd.platform.modules.auxcontrol.mapper;
import com.yfd.platform.modules.auxcontrol.domain.DeviceSignal;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Update;
import java.util.List;
import java.util.Map;
@ -18,6 +19,9 @@ public interface DeviceSignalMapper extends BaseMapper<DeviceSignal> {
DeviceSignal selectOneDeviceSignal(String slaveIp, String type, String address);
Map<String,Object> selectDeviceSignalMap(String slaveIp, String type, String address);
List<Map<String, Object>> getDeviceSignalMaps(String mainDeviceId, String signalName);
@Update("UPDATE fk_device_signal SET yc_value = #{value}, lastmodifydate = #{datetime} WHERE yc_addr = #{node_addr}")
boolean updateDeviceSignalValue_yc(String node_addr,String value,String datetime);
@Update("UPDATE fk_device_signal SET yx_value = #{value}, lastmodifydate = #{datetime} WHERE yx_addr = #{node_addr}")
boolean updateDeviceSignalValue_yx(String node_addr,String value,String datetime);
}