当前位置: 首页 > 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) {// 这一部分在5章节详细说明}@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月底完成,持续更新


附录&参考资料

暂无,更新中

相关文章:

  • 1.7无穷级数
  • 使用jasypt加密配置文件信息
  • 如何解决 Linux 文件系统挂载失败的问题
  • 人物5_My roommate
  • torch.nn.Parameter 与 torch.Tensor
  • 浅谈链表的优化技巧
  • 如何修复卡在恢复模式下的 iPhone:简短指南
  • Linux(用户管用户与用户组管理理)
  • 在 Windows 环境下测试 8100 端口是否开放和正在监听
  • 【Java面试笔记:进阶】26.如何监控和诊断JVM堆内和堆外内存使用?
  • Redis 学习笔记 | 常用命令
  • 香港科技大学广州|生命科学与生物医学工程学域博士项目招生宣讲会—南京大学专场!!!(暨全额奖学金政策)
  • 测试—概念篇
  • BT131-ASEMI无人机专用功率器件BT131
  • 如何快速在idea中希望Spark程序
  • JAVA基础:Collections 工具类实战指南-从排序到线程安全
  • Vtable
  • 如何用postman进行批量操作
  • 根据用户出生日期计算年龄
  • 从暴力到优化:解决「分数严格小于k的子数组数目」问题
  • 交通运输部:预计今年五一假期全社会跨区域人员流动量将再创新高
  • 中使馆:奉劝菲方有关人士不要在台湾问题上挑衅,玩火者必自焚
  • TAE联手加州大学开发出新型核聚变装置:功率提升百倍,成本减半
  • A股三大股指收跌:地产股领跌,银行股再度走强
  • 黄永年:说狄仁杰的奏毁淫祠
  • 十大券商看后市|A股风险偏好有望边际改善,市场仍处黄金坑