工业通信的开源项目 HslCommunication 介绍

前言:


本项目的孵化说来也是机缘巧合的事,本人于13年大学毕业后去了一家大型的国企工作,慢慢的走上了工业软件,上位机软件开发的道路。于14年正式开发基于windows的软件,当时可选的技术栈就是MFC和C#的winform,后来就发现C#的更为简单一些,那就直接干,先做再说。工厂的基本主要需求就是工艺参数的历史追述,附带一些实时监控设备的功能,那么第一道拦路虎就是如何将三菱的PLC(逻辑控制器,通常作为设备的核心控制单元)的数据给拿到我的软件中来呢?这真是一个棘手的问题啊,首先就是百度,搜索到了MX component组件,初步试了试,真的比较麻烦。然后就去看看有没有其他的方式实现,后来就搜索到了三菱的以太网模块,下载到了支持的通讯说明,长篇大论。边测试边开发,勉勉强强读到了我想要读的数据(当然,这时候的代码基本都是写死的),在接下来的两三年里,接触并开发了好几个类似的项目,通常工业软件的需求是采集,分析,存数据库,显示。后来对通信的理解深入,由单机软件发展成了CS架构的软件。后来在17年趁着换工作和考驾照的间隙,梳理了上份工作积累的经验,和实际的需求,整理成了HslCommunication,并将之开源出来,初步的功能是三菱PLC的数据读写,C#软件之间的数据通信。后来又集成了modbus协议,西门子,欧姆龙,ab plc,三菱串口等等,都是后话的事了。

 

做这个项目的目标和开源的初衷是方便广大的像我这种的在工厂一线的软件工程师,我一直觉得我们不应该把自己看做是程序员,程序员的角色更像是码农,主要工作就是敲代码,而软件工程师应该是更大的定义,设计软件的整体架构和开发的。这几年大多数工作都开始意识到工业软件,上位机软件,数据追述系统,SCADA软件,MES软件开发的重要性,所以像我这样的有通信需求的人应该不在少数,况且开源有助于别人来一起改进,和代码测试。所以在开源之后,在博客园就陆陆续续的写了一些文章,比如如何使用C#和三菱PLC通讯,C#和西门子通讯等等。从博客园的点击量来看,确实有大量的工厂的程序员有这方便的需求,而直接采用socket来开发,比较晦涩难懂,坑又比较多,事实上确实有很多人来报告了bug。帮助我修复了这个组件,提高了稳定性。

 

HslCommunication 能干什么?


相比大多数人比较关心这个问题,简单的说,这个组件主要是用于工业通信的,也有两个程序之间的通讯,还有其他杂七杂八的功能,更像是我的工具插件。各种小功能,扩展的小功能等等。直接上图:

640?wx_fmt=png

这是这个开源项目的demo程序,基本上将80%的功能列举出来了,当然还有一些小功能没有列举。大多数支持的设备都在上面进行显示了,可以方便的进行测试,看看是不是可以实现读写的操作(对现场实际在生产的设备应当注意写入不正确的数据会导致意外事故发生)。比如我们来看看三菱的PLC的demo程序:

640?wx_fmt=png

其他的截图画面就不一一举例了,都是类似或是基本类似的。可以方便的使用demo进行测试。

特别注意,本组件实现的所有的通讯都是基于socket直接实现的,通信部分不依赖任何第三方通讯库或是组件安装,也就是说,你拿个dll可以直接和PLC通讯,这对于部署,开发调试,升级都是非常方便的。

 

通讯核心说明


整个网络类的核心在于 NetworkBase类

640?wx_fmt=png

这个类实现了基础的字节收发功能和连接断开功能。接下来就是 NetworkDoubleBase 类的实现,实现了长短连接的操作,在我们实际读写设备的过程中,网络状况往往是差别很大,所以本项目的初衷就是同时支持长连接和短连接。

640?wx_fmt=png

实现了长短的连接后,还要实现设备的BCL类型的读写,本质是基于byte数组和C#基础类型的转换,但是这里有个问题,不同的PLC,modbus协议对于转换的格式不是固定的,有可能是一样的,有可能不是一样的,所以又抽象出来一个 IByteTransform 接口

 640?wx_fmt=png

 这个接口集成到了下面的设备交互的基类 NetworkDeviceBase 里,这个基类实现了一些基础的类型的数据读写。

640?wx_fmt=png

所以到这里可以看到,从NetworkDeviceBase类继承出去的设备类(大部分的设备通信协议都是从这个继承出去的),其基本的读写代码都是一致的,关于解析协议,通信的底层都是封装完毕。 

 

通讯举例说明


先举例说明三菱PLC的读写操作:


// 实例化对象,指定PLC的ip地址和端口号
MelsecMcNet melsecMc = new MelsecMcNet( "192.168.1.110", 6000 );
// 连接对象
OperateResult connect = melsecMc.ConnectServer( );
if (!connect.IsSuccess)
{
Console.WriteLine( "connect failed:" + connect.Message );
return;
}
// 举例读取D100的值
short D100 = melsecMc.ReadInt16( "D100" ).Content;
melsecMc.ConnectClose( );

经过层层封装后,读写的逻辑精简为,实例化,连接,读写,关闭。无论是三菱的PLC,还是西门子的PLC,都是一致的,因为基类的模型都是一致的。


// 实例化对象,指定PLC的ip地址和端口号
SiemensS7Net siemens = new SiemensS7Net( SiemensPLCS.S1200, " 192.168.1.110" );
// 连接对象
OperateResult connect = siemens.ConnectServer( );
if (!connect.IsSuccess)
{
Console.WriteLine( "connect failed:" + connect.Message );
return;
}
// 举例读取M100的值
short M100 = siemens.ReadInt16( "M100" ).Content;
siemens.ConnectClose( );

当然,支持大多数的C#类型数据读写


MelsecMcNet melsec_net = new MelsecMcNet( "192.168.0.100", 6000 );
// 此处以D寄存器作为示例
short short_D1000 = melsec_net.ReadInt16( "D1000" ).Content;         // 读取D1000的short值
ushort ushort_D1000 = melsec_net.ReadUInt16( "D1000" ).Content;      // 读取D1000的ushort值
int int_D1000 = melsec_net.ReadInt32( "D1000" ).Content;             // 读取D1000-D1001组成的int数据
uint uint_D1000 = melsec_net.ReadUInt32( "D1000" ).Content;          // 读取D1000-D1001组成的uint数据
float float_D1000 = melsec_net.ReadFloat( "D1000" ).Content;         // 读取D1000-D1001组成的float数据
long long_D1000 = melsec_net.ReadInt64( "D1000" ).Content;           // 读取D1000-D1003组成的long数据
ulong ulong_D1000 = melsec_net.ReadUInt64( "D1000" ).Content;        // 读取D1000-D1003组成的long数据
double double_D1000 = melsec_net.ReadDouble( "D1000" ).Content;      // 读取D1000-D1003组成的double数据
string str_D1000 = melsec_net.ReadString( "D1000", 10 ).Content;     // 读取D1000-D1009组成的条码数据
// 读取数组
short[] short_D1000_array = melsec_net.ReadInt16( "D1000", 10 ).Content;         // 读取D1000的short值
ushort[] ushort_D1000_array = melsec_net.ReadUInt16( "D1000", 10 ).Content;      // 读取D1000的ushort值
int[] int_D1000_array = melsec_net.ReadInt32( "D1000", 10 ).Content;             // 读取D1000-D1001组成的int数据
uint[] uint_D1000_array = melsec_net.ReadUInt32( "D1000", 10 ).Content;          // 读取D1000-D1001组成的uint数据
float[] float_D1000_array = melsec_net.ReadFloat( "D1000", 10 ).Content;         // 读取D1000-D1001组成的float数据
long[] long_D1000_array = melsec_net.ReadInt64( "D1000", 10 ).Content;           // 读取D1000-D1003组成的long数据
ulong[] ulong_D1000_array = melsec_net.ReadUInt64( "D1000", 10 ).Content;        // 读取D1000-D1003组成的long数据
double[] double_D1000_array = melsec_net.ReadDouble( "D1000", 10 ).Content;      // 读取D1000-D1003组成的double数据

写入的操作:


MelsecMcNet melsec_net = new MelsecMcNet( "192.168.0.100", 6000 );
// 此处以D寄存器作为示例
melsec_net.Write( "D1000", (short)1234 );                // 写入D1000  short值  ,W3C0,R3C0 效果是一样的
melsec_net.Write( "D1000", (ushort)45678 );              // 写入D1000  ushort值
melsec_net.Write( "D1000", 1234566 );                    // 写入D1000  int值
melsec_net.Write( "D1000", (uint)1234566 );               // 写入D1000  uint值
melsec_net.Write( "D1000", 123.456f );                    // 写入D1000  float值
melsec_net.Write( "D1000", 123.456d );                    // 写入D1000  double值
melsec_net.Write( "D1000", 123456661235123534L );          // 写入D1000  long值
melsec_net.Write( "D1000", 523456661235123534UL );          // 写入D1000  ulong值
melsec_net.Write( "D1000", "K123456789" );                // 写入D1000  string值
// 读取数组
melsec_net.Write( "D1000", new short[] { 123, 3566, -123 } );                // 写入D1000  short值  ,W3C0,R3C0 效果是一样的
melsec_net.Write( "D1000", new ushort[] { 12242, 42321, 12323 } );              // 写入D1000  ushort值
melsec_net.Write( "D1000", new int[] { 1234312312, 12312312, -1237213 } );                    // 写入D1000  int值
melsec_net.Write( "D1000", new uint[] { 523123212, 213,13123 } );               // 写入D1000  uint值
melsec_net.Write( "D1000", new float[] { 123.456f, 35.3f, -675.2f } );                    // 写入D1000  float值
melsec_net.Write( "D1000", new double[] { 12343.542312d, 213123.123d, -231232.53432d } );                    // 写入D1000  double值
melsec_net.Write( "D1000", new long[] { 1231231242312,34312312323214,-1283862312631823 } );          // 写入D1000  long值
melsec_net.Write( "D1000", new ulong[] { 1231231242312, 34312312323214, 9731283862312631823 } );          // 写入D1000  ulong值

这里举例了三菱的PLC,实际上各种PLC的操作都是类似的。

 Redis实现


 

除了上述的基本的设备通信,还实现了redis数据库读写操作,分了两个类实现,下图为一般的通信功能

640?wx_fmt=png

同时demo中实现了一个浏览redis服务器的界面功能

640?wx_fmt=png

 

最后的总结


 本通信库实现了.net 3.5 和 .net 4.5的框架,还附带了一些简单的控件,此外还实现了.net standard版本,已在linux测试成功,由于官方在.net core2.2中还未实现串口类,所以暂时没有实现串口相关的。

未来的方向,希望继续优化代码,架构,集成实现更多设备通信,方便广大的网友直接开发测试。

 

开源地址:https://github.com/dathlin/HslCommunication

 更多详细的内容请查看源代码的readme文件。


原文地址:https://www.cnblogs.com/dathlin/p/10390311.html

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com
640?wx_fmt=jpeg


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/317187.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【地狱副本】数据结构之线段树Ⅲ——区间最值/赋值/修改/历史值操作(HDU5306,Tyvj 1518,【清华集训2015】V,HDU6315,HDU1828,POJ3162)

文章目录Gorgeous SequenceTyvj 1518 CPU监控【清华集训2015】VNaive OperationsPictureWalking RaceGorgeous Sequence HDU5306 操作 区间与xxx取min\rm minmin查询区间最大值查询区间和 比较暴力的线段树维护区间 Max : 区间最大值sub_max : 严格小于最大值的区间次大值…

Acwing 1089. 烽火传递

Acwing 1089. 烽火传递 题意: 有n个数,要保证每m个数中必须选一个,问所选数的最小总和是多少 题解: 我一开始设的状态为:dp[i]表示前i个数选完的最小值,第i个数可以选也可以不选,但是这样一个状态&…

IIS作为ASP.NET Core2.1 反向代理服务器未说的秘密

--以下内容针对 ASP.NET Core2.1,2.2出现IIS进程内寄宿 暂不展开讨论---相比ASP.NET,出现了3个新的组件:ASP.NET Core Module、Kestrel、dotnet.exe, 后面我们会理清楚这三个组件的作用和组件之间的交互原理。 ASP.NET Core 设计的初衷是开源…

程序员修神之路--分布式缓存的一条明路(附代码)

菜菜呀,由于公司业务不断扩大,线上分布式缓存服务器扛不住了呀程序员主力 Y总如果加硬件能解决的问题,那就不需要修改程序菜菜我是想加服务器来解决这个问题,但是有个问题呀程序员主力 Y总???菜…

长沙.NET技术社区正式成立

感谢大家的关注,请允许我冒昧的向大家汇报长沙.NET技术社区第一次交流会的会议进展情况。活动过程汇报2019年2月17日,继深圳,广州,西安,成都,苏州相继成立了.net社区之后,酝酿已久的长沙.net社区…

Asp.NetCore轻松学-部署到 IIS 进行托管

前言经过一段时间的学习,终于来到了部署服务这个环节,.NetCore 的部署方式非常的灵活多样,但是其万变不离其宗,所有的 Asp.NetCore 程序都基于端口的侦听,在部署的时候仅需要配置侦听地址、端口(一个或者多…

响应式编程知多少 | Rx.NET 了解下

1. 引言An API for asynchronous programming with observable streams. ReactiveX is a combination of the best ideas from the Observer pattern, the Iterator pattern, and functional programming.ReactiveX 使用可观察数据流进行异步编程的API。 ReactiveX结合了观察者…

.NET Core中的验证组件FluentValidation的实战分享

今天有人问我能不能出一篇FluentValidation的教程,刚好今天在实现我们的.NET Core实战项目之CMS的修改密码部分的功能中有用到FluentValidation,所以就以修改用户密码为实例来为大家进行一下ASP.NET Core中的验证组件FluentValidation的实战分享&#xf…

笛卡尔树详解带建树模板及例题运用(Largest Submatrix of All 1’s,洗车 Myjnie,Removing Blocks,SPOJ PERIODNI)

文章目录笛卡尔树介绍例题Largest Submatrix of All 1’s应用「POI2015」洗车 Myjnie[AGC028B] Removing BlocksSPOJ PERIODNI笛卡尔树 介绍 笛卡尔树是一种数据结构,每个点由两个值,键值key和权值val,组成 其键值满足二叉树性质 即点的左子…

如何为ASP.NET Core设置客户端IP白名单验证

本篇博文中展示了如何在ASP.NET Core应用程序中设置IP白名单验证的3种方式。你可以使用一下3种方式:使用中间件检查每个请求的远程IP地址使用Action过滤器为指定的Controller或action方法添加针对远程IP地址的检查使用IPageFilter为Razor Pages应用添加针对远程IP地…

让ASP.NET Core支持GraphQL之-GraphQL的实现原理

众所周知RESTful API是目前最流行的软件架构风格之一,它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。RESTful的优越性是毋庸置疑的,不过GraphQL也可以作为一种补充&am…

[2021-09-02 contest]CF1251C,可达性统计(bitset优化dp),Boomerang Tournament(状压dp),小蓝的好友(mrx)(treap平衡树)

文章目录CF1251C Minimize The Integeracwing164:可达性统计Facebook Hacker Cup 2016 Round 1 Boomerang Tournament[Zjoi2012]小蓝的好友(mrx)CF1251C Minimize The Integer ………………… 给你一个大整数aaa,它由nnn位数字,也可能有前导…

Entity Framework 的一些性能建议

点击上方蓝字关注“汪宇杰博客”这是一篇我在2012年写的老文章,至今适用(没错,我说的就是适用于EF Core)。因此使用微信重新推送,希望能帮到大家。自从我用了EF,每次都很关心是否有潜在的性能问题。所以每次…

微软内部全面拥抱开源流程Inner Source

微软过去几年一直是 GitHub 平台上开源贡献者最多的公司。不仅如此,微软还将继续拥抱开源,内部有一项名为 Inner Source 的计划,将开源开发流程引入到公司内部。事实上,Inner Source 已经存在于微软内部多年,包括更多代…

k8s使用helm打包chart并上传到腾讯云TencentHub

本文只涉及Helm的Chart操作,不会对其他知识进行过多描述。至于安装这块,麻烦自行百度吧,一大堆呢。在容器化的时代,我们很多应用都可以部署在docker,很方便,而再进一步,我们还有工具可以对docke…

数据结构之基环树——骑士,Island,旅行加强版,Number of Simple Paths,Traffic Network in Numazu,Card Game

文章目录[ZJOI2008]骑士[IOI2008] Island[NOIP2018 提高组] 旅行 加强版CF1454E Number of Simple PathsTraffic Network in NumazuCard Game基环树的常见解法若干个基环树互相独立断环为链(随便断一条)环外树和环外树之间的树形DP环变链后整体可以用数据…

如何在ASP.NET Core中自定义Azure Storage File Provider

主题:如何在ASP.NET Core中自定义Azure Storage File Provider作者: Lamond Lu地址: https://www.cnblogs.com/lwqlun/p/10406566.html项目源代码: https://github.com/lamondlu/AzureFileProvider背景ASP.NET Core是一个扩展性非常高的框架…

美好生活从撸好代码开始

楔子 昨天晚上做了个梦,梦到老板对我说了一番道理,他说对家庭要用爱心,做人对社会要有包容心,对工作要有责任心,对老板要有同理心。 我深以为然。现在的老板确实太不容易了,尤其是作为一家承载梦想&#xf…

Dotnet全平台下APM-Trace探索

随着支撑的内部业务系统越来越多,向着服务化架构进化,在整个迭代过程中,会逐渐暴露出以下问题。传统依赖于应用服务器日志等手段的排除故障原因的复杂度越来越高,传统的监控服务已经无法满足需求。终端--> Nginx --> IIS --…

部署Chart应用并使用.net core读取Kubernetes中的configMap

上一篇文章讲了 k8s使用helm打包chart并上传到腾讯云TencentHub,今天就讲一下使用Helm部署应用并使用configMap代替asp.net core 中的appsettings.json文件。把Chart上传到TencentHub之后,我们就可以通过腾讯云的容器服务,直接部署Helm应用了…