Modbus TCP通信报文解析

一、实现了读取线圈状态和写入多个线圈的功能。代码中包含了详细的注释说明,可以清晰地了解每个方法的功能和使用方式。

对于读取线圈状态的方法,使用时需要传入从站地址、起始地址和线圈数量,最后会返回一个 bool 数组,其中每个元素表示一个线圈的状态。

对于写入多个线圈的方法,使用时需要传入从站地址、起始地址和要写入的 bool 数组,表示每个线圈的状态。该方法内部会根据数量计算出需要传输的字节数,并将 bool 数组转换为字节数组,最后将整个请求报文发送出去。如果写入成功,该方法会返回 true。

二、实现了读取保持寄存器和写入多个保持寄存器的功能。代码中包含了详细的注释说明,可以清晰地了解每个方法的功能和使用方式。

对于读取保持寄存器的方法,使用时需要传入从站地址、起始地址和寄存器数量,最后会返回一个 ushort 数组,其中每个元素表示一个寄存器的值。

对于写入多个保持寄存器的方法,使用时需要传入从站地址、起始地址和要写入的 ushort 数组,表示每个寄存器的值。该方法内部会根据数量计算出需要传输的字节数,并将 ushort 数组转换为字节数组,最后将整个请求报文发送出去。如果写入成功,该方法会返回 true。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net.Sockets;namespace Authorization.WebApi
{/// <summary>/// /// </summary>public static class ModbusTcpClient{private static byte[] buffer = new byte[1024];/// <summary>/// 读线圈 00 01 00 00 00 06 01 01 00 00 00 08/// 读取线圈状态的请求报文,从站地址是 0x01,起始地址是 0x0000,线圈数量是 0x0008/// 使用 client.ReadCoil(0x01, 0x0000, 0x0008, out bool[] values);/// </summary>/// <param name="socket"></param>/// <param name="slaveAddress"></param>/// <param name="startAddress"></param>/// <param name="quantity"></param>/// <param name="values"></param>/// <returns></returns>public static bool ReadCoil(this Socket socket, int slaveAddress, int startAddress, int quantity, out bool[] values){bool success = false;values = null;// 组装 Modbus TCP 请求报文byte[] request = new byte[12];byte[] addressBytes = BitConverter.GetBytes((ushort)startAddress);byte[] quantityBytes = BitConverter.GetBytes((ushort)quantity);request[0] = (byte)slaveAddress; // 从站地址request[1] = 0x01; // 功能码request[2] = addressBytes[1]; // 起始地址高字节request[3] = addressBytes[0]; // 起始地址低字节request[4] = quantityBytes[1]; // 线圈数量高字节request[5] = quantityBytes[0]; // 线圈数量低字节byte[] crcBytes = CalculateCrc(request, 6); // 计算 CRC 校验码request[6] = crcBytes[0]; // CRC 校验码低字节request[7] = crcBytes[1]; // CRC 校验码高字节// 发送 Modbus TCP 请求报文socket.Send(request, 0, 8, SocketFlags.None);// 接收 Modbus TCP 响应报文int count = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);if (count >= 5 && buffer[0] == slaveAddress && buffer[1] == 0x01 && buffer[2] == quantity / 8 + (quantity % 8 == 0 ? 0 : 1)){byte[] data = new byte[quantity / 8 + (quantity % 8 == 0 ? 0 : 1)];Array.Copy(buffer, 3, data, 0, data.Length);values = new bool[quantity];for (int i = 0; i < quantity; i++){int byteIndex = i / 8;int bitIndex = i % 8;values[i] = ((data[byteIndex] >> bitIndex) & 0x01) == 0x01;}success = true;}return success;}public static bool WriteCoil(this Socket socket, int slaveAddress, int address, bool value){bool success = false;// 组装 Modbus TCP 请求报文byte[] request = new byte[12];byte[] addressBytes = BitConverter.GetBytes((ushort)address);request[0] = (byte)slaveAddress; // 从站地址request[1] = 0x05; // 功能码request[2] = addressBytes[1]; // 输出地址高字节request[3] = addressBytes[0]; // 输出地址低字节request[4] = (byte)(value ? 0xFF : 0x00); // 输出值byte[] crcBytes = CalculateCrc(request, 6); // 计算 CRC 校验码request[6] = crcBytes[0]; // CRC 校验码低字节request[7] = crcBytes[1]; // CRC 校验码高字节// 发送 Modbus TCP 请求报文socket.Send(request, 0, 8, SocketFlags.None);// 接收 Modbus TCP 响应报文int count = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);if (count == 8 && buffer[0] == slaveAddress && buffer[1] == 0x05 && BitConverter.ToUInt16(buffer, 2) == address && BitConverter.ToUInt16(buffer, 4) == (value ? 0xFF00 : 0x0000)){success = true;}return success;}/// <summary>/// 写多个线圈 00 01 00 00 00 08 01 0f 00 00 00 08 01 ff/// bool[] coils = new bool[] { true, true, true, true, true, true, true, true };///  client.WriteMultipleCoils(0x01, 0x0000, coils);/// </summary>/// <param name="socket"></param>/// <param name="slaveAddress"></param>/// <param name="address"></param>/// <param name="values"></param>/// <returns></returns>public static bool WriteMultipleCoils(this Socket socket, int slaveAddress, int address, bool[] values){bool success = false;// 组装 Modbus TCP 请求报文int quantity = values.Length;int byteCount = (quantity % 8 == 0) ? quantity / 8 : quantity / 8 + 1;byte[] request = new byte[7 + byteCount];byte[] addressBytes = BitConverter.GetBytes((ushort)address);byte[] quantityBytes = BitConverter.GetBytes((ushort)quantity);request[0] = (byte)slaveAddress; // 从站地址request[1] = 0x0F; // 功能码request[2] = addressBytes[1]; // 起始地址高字节request[3] = addressBytes[0]; // 起始地址低字节request[4] = quantityBytes[1]; // 线圈数量高字节request[5] = quantityBytes[0]; // 线圈数量低字节request[6] = (byte)byteCount; // 字节数for (int i = 0; i < byteCount; i++){byte coilByte = 0;for (int j = 0; j < 8 && i * 8 + j < quantity; j++){if (values[i * 8 + j]){coilByte |= (byte)(1 << j);}}request[7 + i] = coilByte; // 线圈值}byte[] crcBytes = CalculateCrc(request, request.Length - 2); // 计算 CRC 校验码request[request.Length - 2] = crcBytes[0]; // CRC 校验码低字节request[request.Length - 1] = crcBytes[1]; // CRC 校验码高字节// 发送 Modbus TCP 请求报文socket.Send(request, 0, request.Length, SocketFlags.None);// 接收 Modbus TCP 响应报文int count = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);if (count == 8 && buffer[0] == slaveAddress && buffer[1] == 0x0F && BitConverter.ToUInt16(buffer, 2) == address && BitConverter.ToUInt16(buffer, 4) == quantity){success = true;}return success;}/// <summary>/// 读保持寄存器 00 01 00 00 00 06 01 03 00 00 00 02 /// 读取多个寄存器的请求报文,从站地址是 0x01,起始地址是 0x0000,寄存器数量是 0x0002/// client.ReadHoldingRegisters(0x01, 0x0000, 2);/// </summary>/// <param name="socket"></param>/// <param name="slaveAddress"></param>/// <param name="startAddress"></param>/// <param name="quantity"></param>/// <param name="values"></param>/// <returns></returns>public static bool ReadHoldingRegister(this Socket socket, int slaveAddress, int startAddress, int quantity, out ushort[] values){bool success = false;values = null;// 组装 Modbus TCP 请求报文byte[] request = new byte[12];byte[] addressBytes = BitConverter.GetBytes((ushort)startAddress);byte[] quantityBytes = BitConverter.GetBytes((ushort)quantity);request[0] = (byte)slaveAddress; // 从站地址request[1] = 0x03; // 功能码request[2] = addressBytes[1]; // 起始地址高字节request[3] = addressBytes[0]; // 起始地址低字节request[4] = quantityBytes[1]; // 寄存器数量高字节request[5] = quantityBytes[0]; // 寄存器数量低字节byte[] crcBytes = CalculateCrc(request, 6); // 计算 CRC 校验码request[6] = crcBytes[0]; // CRC 校验码低字节request[7] = crcBytes[1]; // CRC 校验码高字节// 发送 Modbus TCP 请求报文socket.Send(request, 0, 8, SocketFlags.None);// 接收 Modbus TCP 响应报文int count = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);if (count == 3 + quantity * 2 && buffer[0] == slaveAddress && buffer[1] == 0x03){values = new ushort[quantity];for (int i = 0; i < quantity; i++){values[i] = BitConverter.ToUInt16(buffer, 3 + i * 2);}success = true;}return success;}/// <summary>/// 写多个保持寄存器/// 00 01 00 00 00 06 01 10 00 00 00 02 04 00 64 00 32/// 表示写入第一个保持寄存器的值为00 64,转换成+进制为100/// 表示写入第二个保持寄存器的值为00 32,转换成十进制为50/// socket.WriteMultipleRegisters(1, 0, new ushort[] { 100, 50 });/// </summary>/// <param name="socket"></param>/// <param name="slaveAddress"></param>/// <param name="startAddress"></param>/// <param name="values"></param>/// <returns></returns>public static bool WriteMultipleRegisters(this Socket socket, int slaveAddress, int startAddress, ushort[] values){bool success = false;// 组装 Modbus TCP 请求报文byte[] request = new byte[13 + values.Length * 2];byte[] addressBytes = BitConverter.GetBytes((ushort)startAddress);byte[] quantityBytes = BitConverter.GetBytes((ushort)values.Length);request[0] = (byte)slaveAddress; // 从站地址request[1] = 0x10; // 功能码request[2] = addressBytes[1]; // 起始地址高字节request[3] = addressBytes[0]; // 起始地址低字节request[4] = quantityBytes[1]; // 寄存器数量高字节request[5] = quantityBytes[0]; // 寄存器数量低字节request[6] = (byte)(values.Length * 2); // 字节数for (int i = 0; i < values.Length; i++){byte[] valueBytes = BitConverter.GetBytes(values[i]);request[7 + i * 2] = valueBytes[1]; // 寄存器值高字节request[8 + i * 2] = valueBytes[0]; // 寄存器值低字节}byte[] crcBytes = CalculateCrc(request, request.Length - 2); // 计算 CRC 校验码request[request.Length - 2] = crcBytes[0]; // CRC 校验码低字节request[request.Length - 1] = crcBytes[1]; // CRC 校验码高字节// 发送 Modbus TCP 请求报文socket.Send(request, 0, request.Length, SocketFlags.None);// 接收 Modbus TCP 响应报文int count = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);if (count == 8 && buffer[0] == slaveAddress && buffer[1] == 0x10 && BitConverter.ToUInt16(buffer, 2) == startAddress && BitConverter.ToUInt16(buffer, 4) == values.Length){success = true;}return success;}public static bool WriteHoldingRegister(this Socket socket, int slaveAddress, int address, ushort value){bool success = false;// 组装 Modbus TCP 请求报文byte[] request = new byte[12];byte[] addressBytes = BitConverter.GetBytes((ushort)address);byte[] valueBytes = BitConverter.GetBytes(value);request[0] = (byte)slaveAddress; // 从站地址request[1] = 0x06; // 功能码request[2] = addressBytes[1]; // 寄存器地址高字节request[3] = addressBytes[0]; // 寄存器地址低字节request[4] = valueBytes[1]; // 寄存器值高字节request[5] = valueBytes[0]; // 寄存器值低字节byte[] crcBytes = CalculateCrc(request, 6); // 计算 CRC 校验码request[6] = crcBytes[0]; // CRC 校验码低字节request[7] = crcBytes[1]; // CRC 校验码高字节// 发送 Modbus TCP 请求报文socket.Send(request, 0, 8, SocketFlags.None);// 接收 Modbus TCP 响应报文int count = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);if (count == 8 && buffer[0] == slaveAddress && buffer[1] == 0x06 && BitConverter.ToUInt16(buffer, 2) == address && BitConverter.ToUInt16(buffer, 4) == value){success = true;}return success;}private static byte[] CalculateCrc(byte[] data, int length){ushort crc = 0xFFFF;for (int i = 0; i < length; i++){crc ^= data[i];for (int j = 0; j < 8; j++){if ((crc & 0x0001) == 0x0001){crc >>= 1;crc ^= 0xA001;}else{crc >>= 1;}}}byte[] crcBytes = BitConverter.GetBytes(crc);Array.Reverse(crcBytes);return crcBytes;}}
}

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

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

相关文章

StAX解析

StAX解析 StAX解析介绍 StAX解析与SAX解析类似&#xff0c;也是基于事件驱动的&#xff0c;不同之处在于StAX采用的是拉模式&#xff0c;应用程序通过调用解析器推进解析的进程&#xff0c;可以调用next()方法来获取下一个解析事件(开始文档&#xff0c;结束文档&#xff0c;开…

建立TCP连接的各个系统调用

TCP 连接的过程图 服务器 socket() 函数 socket() 返回的 sockfd 是一个描述符。socket()对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字&#xff0c;而socket()用于创建一个socket描述符&#xff08;socket descriptor&#xff09;&#xff0c;它唯一标识…

ks webdid 滑块注册

web和app其实都一样&#xff0c;主要是针对于设备进行风控&#xff0c;web设备叫webdid; webdid注册出来&#xff0c;过了ks滑块激活&#xff0c;测试了主页&#xff0c;评论等接口都可以跑&#xff0c;平均也就2s注册一个&#xff0c;如果开并发那就更快了&#xff1b; 不过一…

hdvp.

hdvp:外部函数文件&#xff0c;函数定义在hdvp中可以传输给任何hdev使用&#xff0c;即可以发给别人使用。同时允许对hdvp进行加密

OpenCV笔记

opencv读取视频操作 import cv2video cv2.VideoCapture("./1.mp4")if video.isOpened():# video.read() 一帧一帧地读取# open 得到的是一个布尔值&#xff0c;就是 True 或者 False# frame 得到当前这一帧的图像open, frame video.read() else:open Falsewhile …

Mac 四大常用清理软件推荐,软件特色下载教程横向评测

Mac 一般来说基本是不会中毒的&#xff0c;而且像 现在的 windows 也是很少中毒&#xff0c;但我们可能还是需要一款杀毒清理软件&#xff0c;主要是为了清理垃圾&#xff0c;统一查看并管理软件开机自启、权限信息等&#xff0c;统一卸载清理等功能&#xff0c;另外我们可能还…

【python】在matlab中调用python

参考 Matlab调用Python - 知乎 (zhihu.com) 说一下我犯的错误&#xff1a; 1、电脑上有没有python都可以&#xff0c;我以为anaconda里的python不行&#xff0c;又重新下了一个python3.8 实际上导入的时候可以用 pyversion(D:\myDownloads\anaconda\envs\pytorch38\pytho…

SpringCloud学习路线(10)——分布式搜索ElasticSeach基础

一、初识ES &#xff08;一&#xff09;概念&#xff1a; ES是一款开源搜索引擎&#xff0c;结合数据可视化【Kibana】、数据抓取【Logstash、Beats】共同集成为ELK&#xff08;Elastic Stack&#xff09;&#xff0c;ELK被广泛应用于日志数据分析和实时监控等领域&#xff0…

【LangChain】检索器之上下文压缩

LangChain学习文档 【LangChain】检索器(Retrievers)【LangChain】检索器之MultiQueryRetriever【LangChain】检索器之上下文压缩 上下文压缩 LangChain学习文档 概要内容使用普通向量存储检索器使用 LLMChainExtractor 添加上下文压缩(Adding contextual compression with an…

AI视频监控综合管理平台EasyCVR多分屏默认播放协议的配置优化

智能视频监控平台EasyCVR可拓展性强、开放度高&#xff0c;既能作为业务平台使用&#xff0c;也能作为视频能力层被调用和集成。视频监控综合管理平台兼容度高&#xff0c;支持自由调用、支持与第三方集成。在AI能力的接入上&#xff0c;TSINGSEE青犀视频平台可支持AI智能分析网…

奇舞周刊第500期:TQL,巧用 CSS 实现动态线条 Loading 动画

记得点击文章末尾的“ 阅读原文 ”查看哟~ 下面先一起看下本期周刊 摘要 吧~ 奇舞推荐 ■ ■ ■ TQL&#xff0c;巧用 CSS 实现动态线条 Loading 动画 最近&#xff0c;群里有个很有意思的问题&#xff0c;使用 CSS 如何实现如下 Loading 效果&#xff1a; leaferjs&#xff0c…

支付宝小程序页面跳转——openURL、navigateToAlipayPage

基础库 2.7.20 或更高版本&#xff0c;建议使用 my.ap.openURL 进行跳转&#xff0c;案例如下&#xff1a; my.ap.openURL({url: https://render.alipay.com/p/404,success: (res) > {my.showToast({type: success,content: 跳转成功,duration: 3000,});},fail: (err) >…

STM32MP157驱动开发——LED 驱动( GPIO 子系统)

文章目录 编写思路GPIO子系统的LED驱动程序(stm32mp157)如何找到引脚功能和配置信息在设备树中添加 Pinctrl 信息leddrv.cledtest.cMakefile编译测试 编写思路 阅读&#xff1a;STM32MP157驱动开发——GPIO 和 和 Pinctrl 子系统的概念可知利用GPIO子系统去编写LED驱动&#x…

机器学习深度学习——softmax回归从零开始实现

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——向量求导问题 &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习 希望文章对你们有所帮助 …

SPEC CPU 2017 x86_64 Ubuntu 22.04 LTS LLVM 16.0.6 编译 intrate intspeed

源码编译llvm 下载源码 yeqiangyeqiang-MS-7B23:~/Downloads/src$ git clone --depth1 -b 7cbf1a2 https://github.com/llvm/llvm-project 正克隆到 llvm-project... warning: 不能发现要克隆的远程分支 7cbf1a2。 fatal: 远程分支 7cbf1a2 在上游 origin 未发现 yeqiangyeqi…

全网最牛,Jmeter接口自动化-读取用例执行并结果回写(详细整理)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、环境准备 下载…

网络安全(零基础)自学

一、网络安全基础知识 1.计算机基础知识 了解了计算机的硬件、软件、操作系统和网络结构等基础知识&#xff0c;可以帮助您更好地理解网络安全的概念和技术。 2.网络基础知识 了解了网络的结构、协议、服务和安全问题&#xff0c;可以帮助您更好地解决网络安全的原理和技术…

windows下安装composer

安装Php 教程 下载composer 官网 中文网站 exe下载地址 下载好exe 双击运行 找到php.ini注释一行代码 测试 composer -v说明安装成功 修改源 执行以下命令即可修改 composer config -g repo.packagist composer https://packagist.phpcomposer.com # 查看配置…

【js Set()】

Set() 是 JavaScript 中的数据结构之一&#xff0c;它类似于数组&#xff0c;但是每个值都是唯一的&#xff08;没有重复的值&#xff09;。它可以存储任何类型的值&#xff0c;包括原始类型和对象引用。 创建一个 Set 对象的语法如下&#xff1a; const mySet new Set();或…

SAFe工具,SAFe规模化敏捷工具,SAFe实施流程,SAFe框架管理工具

​Leangoo领歌敏捷工具覆盖了敏捷项目研发全流程&#xff0c;包括小型团队敏捷开发&#xff0c;Scrum of Scrums大规模敏捷。 随着SAFe的越来越普及&#xff0c;Leangoo本次上线提供了完整的SAFe框架功能&#xff0c;包括&#xff1a;Program Backlog&#xff0c;PI规划&#…