百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

简单实用的Modbus类库,支持从站和DTU

bigegpt 2024-10-07 06:32 12 浏览

一、简介

ThingsGateway.Foundation.Modbus 用于Modbus协议通讯,支持主站/从站、ModbusTcp/ModbusRtu,通讯链路支持串口/Tcp/Udp、被动连接(Dtu)

优势:

1、通讯链路与协议解析类松耦合设计

2、支持被动连接(Dtu)设备

3、内置打包算法

4、实体通讯结果映射,并支持打包连读

二、nuget安装

<PackageReference Include="ThingsGateway.Foundation.Modbus" Version="6.0.3.47" />

三、使用指南

3.1、创建通道

var clientConfig = new TouchSocketConfig();

//tcp服务

//var clientChannel = clientConfig.GetTcpServiceWithBindIPHost("tcp://127.0.0.1:502");

//串口

//var clientChannel = clientConfig.GetSerialPortWithOption("COM1");

//udp

//var clientChannel = clientConfig.GetUdpSessionWithIPHost("127.0.0.1:502",);


//tcp客户端

var clientChannel = clientConfig.GetTcpClientWithIPHost("127.0.0.1:502");


3.2、创建协议类

modbus主站

ModbusMaster modbusMaster = new(clientChannel)

{

//modbus协议格式

ModbusType = Modbus.ModbusTypeEnum.ModbusRtu,

//ModbusType = Modbus.ModbusTypeEnum.ModbusTcp,


//默认站号

Station = 1,

//默认数据格式

DataFormat = DataFormatEnum.ABCD,

//读写超时

Timeout = 3000,

};

modbus从站

ModbusSlave modbusSlave = new(clientChannel)

{

//modbus协议格式

ModbusType = Modbus.ModbusTypeEnum.ModbusRtu,

//ModbusType = Modbus.ModbusTypeEnum.ModbusTcp,


//默认站号

Station = 1,

//默认数据格式

DataFormat = DataFormatEnum.ABCD,

};

3.3、读写

读取

//单独读取Int16类型,其他类型操作类似

var data = await modbusMaster.ReadInt16Async("40001");

//批量读取Int16类型,其他类型操作类似

var data = await modbusMaster.ReadInt16Async("40001", 10);

//对于4字节的解析规则,可在协议类中设置 ``DataFormat`` 属性,也可以读取方式时传入字符串

var data = await modbusMaster.ReadSingleAsync("40001;data=ABCD");

//传入的寄存器地址规则,可以通过文档查看,或者调用GetAddressDescription方法

//查看modbus驱动地址说明

Console.WriteLine(modbusMaster.GetAddressDescription());

//批量读取字节数组

var data = await modbusMaster.ReadAsync("40001", 10);

//也可以直接通过字节数组,获取需要的数据类型

if(data.IsSuccess)

{

ValueByteBlock valueByteBlock = new(data.Content);

//读取float

valueByteBlock.ReadFloat(EndianType.LittleSwap);

}

写入

//调用WriteAsync方法

var result = await modbusMaster.WriteAsync("40001",(ushort)1);


3.4、打包读取

IVariable接口实现

1、实现IVariable接口

2、实现IVariableSource接口

3、调用LoadSourceRead,返回打包封装类

4、调用ReadAsync读取数据,并调用PraseStructContent解析数据

5、解析数据后,可以通过IVariable获取解析后的数据


//通过数据库等方式获取寄存器/数据类型等配置

List<VariableClass> variableClasses = new()

{

new VariableClass()

{

DataType=DataTypeEnum.Int16,

RegisterAddress="40001",

IntervalTime=1000,

},

new VariableClass()

{

DataType=DataTypeEnum.Int32,

RegisterAddress="40011",

IntervalTime=1000,

},

};

//调用LoadSourceRead,返回打包封装类

var deviceVariableSourceReads = modbusMaster.LoadSourceRead<VariableSourceClass>(variableClasses, 100, 1000);


//IVariableSource列表,调用ReadAsync读取字节数据,并调用PraseStructContent解析数据

foreach (var item in deviceVariableSourceReads)

{

var result = await modbusMaster.ReadAsync(item.RegisterAddress, item.Length);

if (result.IsSuccess)

{

try

{

var result1 = item.VariableRunTimes.PraseStructContent(modbusMaster, result.Content, exWhenAny: true);

if (!result1.IsSuccess)

{

//失败日志

item.LastErrorMessage = result1.ErrorMessage;

item.VariableRunTimes.ForEach(a => a.SetValue(, isOnline: false));

modbusMaster.Logger.Warning(result1.ToString());

}

}

catch (Exception ex)

{

modbusMaster.Logger.Exception(ex);

}

}

else

{

//通讯失败日志

item.LastErrorMessage = result.ErrorMessage;

item.VariableRunTimes.ForEach(a => a.SetValue(, isOnline: false));

modbusMaster.Logger.Warning(result.ToString());

}

}

VariableObject实体类实现

1、实现VariableObject虚类,添加业务属性,业务属性需添加VariableRuntime特性,指定寄存器地址、特定数据类型(不填默认为C#属性类型)、读写表达式(原始值为raw,可填:raw*100+10,做一些数学转换))

2、实例化业务实体类,需传入协议对象与连读打包的最大数量

3、可以看到源代码生成器已经自动生成了写入方法,可直接调用WriteAlarmLimitAsync法写入数据

4、调用MultiReadAsync方法执行连读,结果会自动解析到业务实体类的属性中

实体类配置属性VariableRuntime特性


[GeneratorVariable]

public partial class ModbusVariable : VariableObject

{

[VariableRuntime(RegisterAddress = "400051",ReadExpressions ="raw*10")]

public ushort AlarmLevel { get; set; }


[VariableRuntime(RegisterAddress = "400061;len=10")]

public string AlarmText { get; set; }


[VariableRuntime(RegisterAddress = "400071")]

public float AlarmLimit { get; set; }


public ModbusVariable(IProtocol protocol, int maxPack) : base(protocol, maxPack)

{

}

}

//构造实体类对象,传入协议对象与连读打包的最大数量

ModbusVariable modbusVariable = new(modbusMaster, 100);


//读取,成功结果会映射到实体属性中

var result= await modbusVariable.MultiReadAsync();


//写入

//源生成器自动生成了``(Write{属性名称})Async``方法,直接调用即可

await modbusVariable.WriteAlarmLimitAsync(100);

//输出实体json查看

Console.WriteLine(modbusVariable.ToJsonString());

四、性能测试

下面可以看到与HslCommunication的基准测试对比

采集100个连续寄存器,采用ModSim32作为测试模拟设备端

[Benchmark]

public async Task ThingsGateway()

{

for (int i = 0; i < Program.NumberOfItems; i++)

{

var result = await thingsgatewaymodbus.ReadAsync("40001", 100);

if (!result.IsSuccess)

{

throw new Exception(result.ToString());

}

}

}


[Benchmark]

public async Task HslCommunication()

{

for (int i = 0; i < Program.NumberOfItems; i++)

{

var result = await modbusTcpNet.ReadAsync("0", 100);

if (!result.IsSuccess)

{

throw new Exception(result.Message);

}

}

}

得益于字节池与Socket异步,虽然都采用字符串解析寄存器地址的方式,但ThingsGateway的内存耗用比较低,并且采集通讯速度更快

五、总结

ThingsGateway.Foundation.Modbus,非常好用的modbus协议库,尤其是实体通讯类以及自动打包的功能,非常适用于上位机业务使用。

Gitee仓库地址 https://gitee.com/diego2098/ThingsGateway

Github仓库地址 https://github.com/kimdiego2098/ThingsGateway


相关推荐

Dify「模板转换」节点终极指南:动态文本生成进阶技巧(附代码)Jinja2引擎解析

这篇文章是关于Dify「模板转换」节点的终极指南,解析了基于Jinja2模板引擎的动态文本生成技巧,涵盖多源文本整合、知识检索结构化、动态API构建及个性化内容生成等六大应用场景,助力开发者高效利用模...

我用C#造了个AI程序员:自动调试+重构代码实战

在软件开发的世界里,调试和重构代码往往占据了程序员大量的时间。我一直梦想着能有一个智能助手,帮我处理这些繁琐的工作。于是,我决定用C#打造一个AI程序员,让它具备自动调试和重构代码的能力。系统架构设计...

公文自动排版vba代码(公文自动排版vba代码)

Sub公文自动排版()'设置页面参数(单位:厘米)WithActiveDocument.PageSetup.TopMargin=CentimetersToPoints(3.7)&#...

Anthropic最强代码神器:Claude Code系统提示词

最近,在融合Opus-4之后,ClaudeCode的整体能力直线飙升.甚至一度把曾经的最强开发工具——Cursor打的抬不起头来。无论是代码生成的准确度,还是智能补全的丝滑体验,都让人印象深...

使用 Ruff 进行 Python 代码格式化与静态检查

随着Python项目的规模增大,保持一致的代码风格和高质量的代码变得尤为重要。Ruff是一个现代、高性能、支持lint和格式化的Python工具,能帮助你快速发现并修复常见代码问题。本文...

基础语法篇:格式化输出 含完整示例代码

所谓格式化输出就是按照一定格式来输出对应的内容,在Python的语法中格式化输出包含两种:格式化符号、格式化字符串一、格式化符号常用的格式化符号包括%s(将内容转换为字符串,放入占位位置)、%d(将内...

代码整洁如诗!Keil 插件上线,一键格式化代码,告别风格混乱!

引言:代码格式不统一?你的团队还在为“括号位置”吵架吗?嵌入式开发者们,你是否经历过这些抓狂瞬间?代码风格“百花齐放”:同事的代码缩进用空格,你的用Tab,合并时冲突频发!手动调整耗时费力:为了通过C...

[信捷PLC] 信捷PLC之C函数编程(一)

前言写PLC程序,越来越觉得结构化文本编程语言(ST)给PC编程带来的便利,在处理一些数据上,可以写的更加灵活。所以,在项目PLC选型上,我都会优先选择支持结构化文本的PLC。国内有些厂商推出了一些较...

C语言-HelloWorld解析(c语言的helloworld怎么写)

使用VisualStudio2017开发工具新创建一个项目,编写第一个C语言程序。#include<stdio.h>voidmain(){printf("HelloW...

VSCode 配置 C++ 开发环境!教程详解

第一步、安装VSCode应用程序打开VSCode官网,下载对应安装包并默认安装(这里指明:安装路径可以修改)第二步、安装相关插件此时的VSCode仅仅是一个英文文本编辑器,还称不上开发工具,所以需要...

C语言进阶教程:C语言与汇编语言交互

C语言和汇编语言的交互是底层编程和性能优化中的一个重要方面。理解它们如何协同工作,可以帮助开发者更好地控制硬件、优化关键代码段以及理解编译器的行为。为什么需要在C语言中嵌入汇编?尽管C语言已经提供了相...

C语言如何处理平台相关代码(c语言的开发平台)

在进行跨平台C编程时,不可避免地会遇到需要针对不同操作系统或硬件架构编写特定代码的情况。C语言通过预处理器指令,特别是条件编译指令,为我们提供了处理平台相关代码的有效机制。最常用的就是利用预定义的宏(...

C语言:hello world(c语言helloworld代码)

环境:a.初学者建议用“啊哈C”,这款软件简单易装;b.devc.visualstdiod.Vc6.0第一行代码:#include<stdio.h>#<stdio.h&g...

C语言之编译器集合(编写c语言编译器)

C语言有多种不同的编译器,以下是常见的编译工具及其特点:一、主流C语言编译器1.GCC(GNUCompilerCollection)特点:开源、跨平台,支持多种语言(C、C++、Fortran...

适合零基础初学者学习C语言第一课教程,揭开C语言的神秘面纱

一、C语言简介我刚接触编程,首先想要学习的就是C语言,这次我就把我的感悟用我自己理解的文字表述出来,这样对刚学C语言的人来说,才是比较友好的。因为我们都没有C语言的基础,不懂啥是编程,啥事代码。我们...