当前位置: 首页 > news >正文

IEC 61850标准协议解读 2.基于Java的MMS实现

专栏文章目录

第一章 IEC 61850标准协议解读 0.导言
第二章 IEC 61850标准协议解读 1.建模讲解
第三章 IEC 61850标准协议解读 2.基于Java的MMS实现


目录

  • 专栏文章目录
  • 前言
  • 1 依赖库引入
  • 2 创建服务端
  • 3 创建客户端
  • 4 读写模型
    • 4.1 服务端读写
    • 4.2 客户端读写
  • 5.报告
  • 6 文件服务
    • 6.1 读取文件目录
    • 6.2 读取文件内容
    • 6.3 删除文件
    • 6.4 上传(写)文件(建设中)
  • 7 Goose服务(建设中)
  • 附录&参考资料

前言

1 依赖库引入

这个依赖库起先于beanit/iec61850bean,博主在使用过程中,有一些问题(没有文件服务、对接南瑞的1.0客户端测试软件有问题、模型中枚举类型索引和code不能同时支持等),所以修复这些问题并发布到了maven的中央仓库。有兴趣的小伙伴欢迎参与建设,提交pr和issues

<!-- https://github.com/mujave/iec61850bean -->
<dependency><groupId>com.github.mujave</groupId><artifactId>iec61850bean</artifactId><version>1.9.1.11</version>
</dependency>

2 创建服务端

public class SimpleServerClientTest{private static final Logger log = LoggerFactory.getLogger(SimpleServerClientTest.class);private static final int PORT = 102;private static final String ICD_FILE = "src/test/resources/simple-test.icd";//服务端能力对象private ServerSap serverSap;//模型文件能力对象private ServerModel serverModel;private void startServer() throws SclParseException, IOException {//从本地加载一个icd文件,并在102端口暴漏一个mms服务端serverSap = new ServerSap(PORT, 0, null, SclParser.parse(ICD_FILE).get(0), null);this.serverModel = this.serverSap.getModelCopy();}
}

3 创建客户端

public class SimpleServerClientTest implements ClientEventListener{private static final Logger log = LoggerFactory.getLogger(SimpleServerClientTest.class);ClientAssociation clientAssociation;private ServerModel clientModel;private void startClient() throws IOException, ServiceError {//创建客户端能力对象ClientSap clientSap = new ClientSap();//与服务端建立连接this.clientAssociation =clientSap.associate(InetAddress.getByName("localhost"), PORT, "", this);//获取模型文件能力对象this.clientModel = this.clientAssociation.retrieveModel();// 客户端也可以离线的方式读取本地的模型文件//this.clientModel = SclParser.parse(ICD_FILE).get(0);}@Overridepublic void newReport(Report report) {// 这一部分在4.2章节详细说明}@Overridepublic void associationClosed(IOException e) {log.error("Iec61850 mms server has closed");}
}

4 读写模型

4.1 服务端读写

 public void testSetValueForServer() throws IOException, ServiceError, InterruptedException {List<BasicDataAttribute> writeList = CollUtil.newArrayList();//通过模型中DA的引用名称找到对应的对象BdaBoolean v1 = (BdaBoolean) serverModel.findModelNode("FKMONT/GGIO1.Ind1.stVal", Fc.ST);//设置对应要写入的值v1.setValue(false);//将DA加入到待写入集合中writeList.add(v1);BdaFloat32 v2 = (BdaFloat32) serverModel.findModelNode("FKMONT/GGIO2.AnInd1.mag.f", Fc.MX);//读取模型该节点的当前值System.out.println(v2.getFloat().floatValue());v2.setFloat(1.2f);writeList.add(v2);//服务端通过服务端能力对象将数据集写入到模型serverSap.setValues(writeList);
}

4.2 客户端读写

public void testSetValueForClient() throws IOException, ServiceError, InterruptedException {BdaBoolean v1 = (BdaBoolean) clientModel.findModelNode("FKMONT/GGIO1.Ind1.stVal", Fc.ST);System.out.println(v1.getValue()); //falseBdaFloat32 v2 = (BdaFloat32) clientModel.findModelNode("FKMONT/GGIO2.AnInd1.mag.f", Fc.MX);System.out.println(v2.getFloat());//0.0// 调用4.1的方法模拟服务端数据变化testSetValueForServer();//通过客户端能力对象读取服务端模型中的最新数据clientAssociation.getDataValues(v1); System.out.println(v1.getValue());//trueclientAssociation.getDataValues(v2);System.out.println(v2.getFloat().floatValue()); //1.2
}

5.报告

在上一篇博客在3.3.3部分里介绍到关于报告的模型定义,这里我在复制一下

<LN0 inst="" lnClass="LLN0" lnType="GEXIN_LLN0"><!-- 数据集:顾名思义就是数对对象的集合,定义数据集之后,使用 ReportControl报告定义这些数据发生变动时发送报告FCDA 各属性ldInst:逻辑设备实例名称lnClass:逻辑节点类型 lnInst:逻辑节点实例号 对应DO的name.目前国内规范一般按照遥信、遥测分为两各数据集,一个报告遥测量浮点型,一个报告遥信量布尔型--><DataSet name="ds01Din" desc="遥信单点信息数据集(含可控点)"><FCDA doName="Ind1" fc="ST" ldInst="MONT" lnClass="GGIO" lnInst="1"/><FCDA doName="Ind1" fc="ST" ldInst="MONT" lnClass="GGIO" lnInst="3"/></DataSet><!-- 引用ds01Din数据集--><ReportControl bufTime="0" buffered="false" confRev="1" datSet="ds01Din" intgPd="30000" name="brcb01Din" rptID="MONT/LLN0$BR$brcb01Din"><!-- 其中数据对象的dchg、qchg当数据变化、品质变化时都触发报告进行上送 --><TrgOps dchg="true" dupd="true" period="false" qchg="true"/><OptFields dataSet="true" entryID="true" reasonCode="true" seqNum="true" timeStamp="true"/><!-- max属性是IED可以支持的报告实例个数。IED初始化时为每个报告生成max个实例,分别以报告控制块名+实例号(01,02...)进行区分,如brcb01DinO1、brcb01Din02。每个client在连接时以不同的报告实例号占用一个报告实例。每个报告实例按照client指定的属性上送报告--><RptEnabled max="5"/></ReportControl>
</LN0>
@Test
public void reportEnableTest() throws ServiceError, IOException {HashSet<Object> enableReportNamees = new HashSet<>();Collection<Urcb> urcbs = this.clientModel.getUrcbs();for (Urcb urcb : urcbs) {clientAssociation.getRcbValues(urcb);String rptId = urcb.getRptId().getStringValue();log.info("1.{}(rptID:{}) {}", urcb.getName(), rptId, urcb.getRptEna().getValue());if (!enableReportNamees.contains(rptId)) {// 同一个rptId开启一次报告使能就可以clientAssociation.enableReporting(urcb);enableReportNamees.add(rptId);}}for (Urcb urcb : urcbs) {clientAssociation.getRcbValues(urcb);log.info("2.{}(rptID:{}) {}", urcb.getName(), urcb.getRptId().getStringValue(), urcb.getRptEna().getValue());}}

输出打印如下:

1.brcb01Din05(rptID:MONT/LLN0$BR$brcb01Din) false
1.brcb01Din02(rptID:MONT/LLN0$BR$brcb01Din) false
1.brcb01Din01(rptID:MONT/LLN0$BR$brcb01Din) false
1.brcb01Din04(rptID:MONT/LLN0$BR$brcb01Din) false
1.brcb01Din03(rptID:MONT/LLN0$BR$brcb01Din) false2.brcb01Din05(rptID:MONT/LLN0$BR$brcb01Din) true
2.brcb01Din02(rptID:MONT/LLN0$BR$brcb01Din) false
2.brcb01Din01(rptID:MONT/LLN0$BR$brcb01Din) false
2.brcb01Din04(rptID:MONT/LLN0$BR$brcb01Din) false
2.brcb01Din03(rptID:MONT/LLN0$BR$brcb01Din) false

开启报告使能之后,对应数据集的数据在服务端变更时(或者开启周期发送后)客户端就会收到报告了,这里你的客户端需要实现一下ClientEventListener接口,在newReport方法中就会有回调数据过来了,这里就不再赘述,打印进行解析就可以了

@Test
public void reportTest() throws ServiceError, IOException, InterruptedException {//让客户端开启报告reportEnableTest();//调用服务端发送数据testSetValueForServer();
}@Override
public void newReport(Report report) {System.out.println("got a report.");System.out.println(report);
}

输出打印如下:

got a report.
Report ID: MONT/LLN0$BR$brcb01Din
Data set reference: FKMONT/LLN0.ds01Din
Sequence number: 0
Time of entry (unix timestamp): 1745846525390
Reported data set members:
FKMONT/GGIO1.Ind1 [ST]
FKMONT/GGIO1.Ind1.stVal: false
FKMONT/GGIO1.Ind1.q: 0000
FKMONT/GGIO1.Ind1.t: 1970-01-01T00:00:00Z, reason: data-change

6 文件服务

文件服务在原本的beanit/iec61850bean中,仅支持客户端的实现,如果你是服务端的话,需要换成我在文章开头的pom,因为我修改了源码,使其服务端支持了文件(目录)读取的能力。
服务端实现的时候,需要在服务端启动之后设置文件服务的根路径,具体以代码如下

//客户端读取到的所有文件内容都是基于这个路径下的,如果不设置的话,就是程序的启动目录
serverSap.setFileServiceParentPath("/home/test");
//客户端读取目录时,是否上报子文件夹,如果设置false,则进上报根目录下的文件
serverSap.setReportFileDirectory(false);

6.1 读取文件目录

@Test
public void testGetFileDirectory() throws IOException, ServiceError, InterruptedException {List<FileInformation> fileDirectory = this.clientAssociation.getFileDirectory("COMTRADE");int i = 0;for (FileInformation fileInformation : fileDirectory) {log.info("{} - {} sizeof: {} {}", ++i, fileInformation.getFilename(), fileInformation.getFileSize(),DateUtil.formatDateTime(fileInformation.getLastModified().getTime()));}
}

6.2 读取文件内容

@Test
public void testGetFile() throws IOException, ServiceError, InterruptedException {this.clientAssociation.getFile("/chart.txt", (byte[] fileData, boolean moreFollows) -> {log.info("Received {} bytes of file data. More data follows: {}", fileData.length, moreFollows);//这里直接进行保存,可以使用追加方式写文件log.info("\n{}", new String(fileData));return moreFollows;});
}

6.3 删除文件

@Test
public void testDeleteFile() throws ServiceError, IOException {this.clientAssociation.deleteFile("要删除的文件名字");
}

6.4 上传(写)文件(建设中)

客户端写文件到服务端正在开发中,预计在2025.05月底完成,持续更新

7 Goose服务(建设中)

客户端写文件到服务端正在开发中,预计在2025.07月底完成,持续更新


附录&参考资料

暂无,更新中

http://www.xdnf.cn/news/194311.html

相关文章:

  • Python爬虫实战:获取猫yan电影网最新热门电影数据并做分析,为51观影做参考
  • ArcGIS arcpy代码工具——根据属性结构表创建shape图层
  • OpenGL----OpenGL纹理与纹理缓存区
  • ICH CTD中ISS的关键内容与作用
  • ubuntu新增磁盘挂载
  • 如何将现有资源导入到 Terraform 管理?
  • BT131-ASEMI无人机专用功率器件BT131
  • 【更新】LLM Interview (2)
  • [特殊字符] 基于Docker部署Nacos注册中心及微服务注册发现详解(含MySQL持久化配置)
  • Android常见仓库与国内仓库对应关系
  • MaxScript二维图形布尔(并)运算
  • Hadoop和Spark大数据挖掘与实战
  • JQuery 使用技巧
  • Leetcode - 双周赛155
  • UE 滚动提示条材质制作
  • 遥控器双频天线技术及信号传输科普!
  • Linux进程7-signal信号处理方式验证、可重入函数举例、信号集函数验证、信号集阻塞验证
  • K8S学习笔记01
  • 嵌入式面试八股文(十二)·FreeRTOS中·堆和栈
  • Oracle备份和恢复
  • 论文速报《Enhancing Autonomous Driving Systems...:LLM-MPC混合架构增强自动驾驶》
  • C语言基础—(函数,指针与形参实参,字符串与指针,结构体)
  • Golang|使用函数作为参数和使用接口的联系
  • 23种设计模式
  • STM32N6570-DK ISP调试
  • UDP 报文结构与注意事项总结
  • 每日c/c++题 备战蓝桥杯(P1093 [NOIP 2007 普及组] 奖学金)
  • 勘破养生伪常识,开启科学养生新篇
  • 发那科机器人(基本操作、坐标系、I/O通信)
  • JVM——引入