主頁 > .NET開發 > IoTClient開發3 - ModBusTcp協議客戶端實作

IoTClient開發3 - ModBusTcp協議客戶端實作

2020-09-21 15:50:46 .NET開發

前言

進過前面兩章的介紹,今天開始正式的實戰,

進制轉換

很多朋友對于進制轉換可能是在剛學計算機的時候有接觸,后來做高級語言開發可能就慢慢忘記了,我們做工控開發的時候需要經常進行進制轉換,這里和大家一起復習下,
一個位元組等8位(1byte = 8bit),可以存盤2^8(0-255)共計256個數字,所以我們要對8、256等數字要敏感,
int16(short), int32(int), int64(long) 分別是占用2個位元組、4個位元組、8個位元組,Single(float)也是占用4個位元組,

bool       System.Boolean (布爾型,其值為 true 或者 false)
byte       System.Byte    (位元組型,占 1 位元組,表示 8 位正整數,范圍 0 ~ 255)
sbyte      System.SByte   (帶符號位元組型,占 1 位元組,表示 8 位整數,范圍 -128 ~ 127)
char       System.Char    (字符型,占有 2 個位元組,表示 1 個 Unicode 字符)
short      System.Int16   (短整型,占 2 位元組,表示 16 位整數,范圍 -32,768 ~ 32,767)
ushort     System.UInt16  (無符號短整型,占 2 位元組,表示 16 位正整數,范圍 0 ~ 65,535)
uint       System.UInt32  (無符號整型,占 4 位元組,表示 32 位正整數,范圍 0 ~ 4,294,967,295)
int        System.Int32   (整型,占 4 位元組,表示 32 位整數,范圍 -2,147,483,648 到 2,147,483,647)
float      System.Single  (單精度浮點型,占 4 個位元組)
ulong      System.UInt64  (無符號長整型,占 8 位元組,表示 64 位正整數)
long       System.Int64   (長整型,占 8 位元組,表示 64 位整數)
double     System.Double  (雙精度浮點型,占8 個位元組)

接著我們來看其他進制轉十進制的計算

十進制轉十進制  
1263 = 1*10^3 + 2*10^2 + 6*10^1 + 3*10^0 = 1000 + 200 + 60 + 3 = 1263
二進制轉十進制 
1001 = 1*2^3 + 0*2^2 + 0*2^1 + 1*2^0 = 8 + 0 + 0 + 1 = 9 
十六進制轉十進制 
3245 = 3*16^3 + 2*16^2 + 4*16^1 + 5*16^0 = 3*4096 + 2*256 + 4*16 + 5 = 12869

十進制轉二進制

第八位 第七位 第六位 第五位 第四位 第三位 第二位 第一位
2^7     2^6    2^5    2^4     2^3    2^2    2^1   2^0
128     64     32     16      8      4      2      1

以上位二進制位能存盤最大十進制數,所以我們反過來也可以對照把十進制轉二進制,比如86,
86小于128多以第八位是0,86大于64所以第七位是1,86-64=22,22小于32所以第六位是0,22大于16所以第五位是1,,, 


所以最好轉成二進制是:0101 0110

二進制轉十六進制

我們用二進制 0101 0110來演示,也就是上面十進制的86,

當然,你最好用計算器驗證下

ModBusTcp協議介紹

我們在對進制轉換進行復習過后,接下來講ModBusTcp協議,
ModBus協議是現在工控里面用的比較多比較通用的一種協議,什么可靠啊、簡單啊等等一些優點就不說了,直接入正題,
ModBus分為RTU、ASCII、TCP三種方式進行通信,今天我們只講TCP,
在ModBus里面有站號、功能碼、暫存器地址等概念,

  • 站號:多設備的標識號
  • 功能碼:一些功能的標識號
    功能碼詳解:
01:讀線圈
02:讀離散量
03:讀保持暫存器(每個暫存器含有兩個位元組)
04:讀輸入暫存器
05:寫單個線圈
06:寫單個暫存器
15:用于寫多個線圈
16:寫多個暫存器

ModBusTcp報文分析

協議的理解和實作主要就是要對協議報文理解,(注意:以下報文資料都是十六進制)

資料【讀取-請求報文】:19 B2 00 00 00 06 02 03 00 04 00 01

  • 19 B2 是客戶端發的檢驗資訊,隨意定義,
  • 00 00 代表是基于tcp/ip協議的modbus
  • 00 06 標識后面還有多長的位元組
  • 02 表示站號地址
  • 03 為功能碼(讀保持暫存器)
  • 00 04 為暫存器地址
  • 00 01 為暫存器的長度(暫存器個數)

資料【讀取-回應報文】(分兩次獲取)

第一次獲取前八個位元組(Map報文頭):19 B2 00 00 00 05 02 03 02 00 20

  • 19 B2 檢驗驗證資訊(復制的客戶端發的,配件檢驗)
  • 00 00 代表是基于tcp/ip協議的modbus(復制的客戶端發的)
  • 00 05 為當前位置到最后的長度
  • 02 表示站號地址(復制的客戶端發的)
  • 03 為功能碼(復制的客戶端發的)

第二次獲取的報文:02 00 20

  • 02 位元組個數
  • 00 20 回應的資料

資料【寫入-請求報文】:19 B2 00 00 00 09 02 10 00 04 00 01 02 00 20

  • 19 B2 是客戶端發的檢驗資訊,隨意定義,
  • 00 00 代表是基于tcp/ip協議的modbus
  • 00 09 從本位元組下一個到最后
  • 02 站號
  • 10 功能碼(轉十進制就是16)
  • 00 04 暫存器地址
  • 00 01 暫存器的長度(暫存器個數)
  • 02 寫位元組的個數
  • 00 20 要寫入的值(轉十進制為32)

資料【寫入-回應報文】:19 B2 00 00 00 06 02 10 00 04 00 01

和請求報文的區別

  • 沒有了請求報文的資料值
  • 00 09 變成了00 06 因為報文長度變了
  • 其他的報文意義和請求報文一致

ModBusTcp對暫存器的讀取

有了上面的三個報文做參考,我們就可以用Socket來實作ModBusTcp協議了,其實協議就是按照報文的規定來,也沒有想的那么復雜,和我們前面實作的聊天通訊軟體區別不大,
第一步,我們先實作資料讀取報文的組裝:

/// <summary>
/// 獲取讀取命令(此方法傳入引數后就可以得到類似19 B2 00 00 00 06 02 03 00 04 00 01這樣的請求報文)
/// </summary>
/// <param name="address">暫存器起始地址</param>
/// <param name="stationNumber">站號</param>
/// <param name="functionCode">功能碼</param>
/// <param name="length">讀取長度</param>
/// <returns></returns>
public static byte[] GetReadCommand(ushort address, byte stationNumber, byte functionCode, ushort length)
{
    byte[] buffer = new byte[12];
    buffer[0] = 0x19;
    buffer[1] = 0xB2;//Client發出的檢驗資訊
    buffer[2] = 0x00;
    buffer[3] = 0x00;//表示tcp/ip 的協議的modbus的協議
    buffer[4] = 0x00;
    buffer[5] = 0x06;//表示的是該位元組以后的位元組長度

    buffer[6] = stationNumber;  //站號
    buffer[7] = functionCode;   //功能碼
    buffer[8] = BitConverter.GetBytes(address)[1];
    buffer[9] = BitConverter.GetBytes(address)[0];//暫存器地址
    buffer[10] = BitConverter.GetBytes(length)[1];
    buffer[11] = BitConverter.GetBytes(length)[0];//表示request 暫存器的長度(暫存器個數)
    return buffer;
}

第二步,就是建立我們的Socket連接,并發送請求報文

//1 創建Socket
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

//2 建立連接
socket.Connect(new IPEndPoint(IPAddress.Parse(ip), 埠));

//3 獲取命令[組裝請求報文](暫存器起始地址:4、站號:2、功能碼:3、讀取暫存器長度:1)
byte[] command = GetReadCommand(4, 2, 3, 1);

//4 發送命令
socket.Send(command);

第三步,決議回應報文,得到資料值

//5 讀取回應
byte[] buffer1 = new byte[8];//先讀取前面八個位元組(Map報文頭)
socket.Receive(buffer1, 0, buffer1.Length, SocketFlags.None);

//5.1 獲取將要讀取的資料長度
int length = buffer1[4] * 256 + buffer1[5] - 2;//減2是因為這個長度資料包括了單元識別符號和功能碼,占兩個位元組

//5.2 讀取資料
byte[] buffer2 = new byte[length];
var readLength2 = socket.Receive(buffer2, 0, buffer2.Length, SocketFlags.None);

byte[] buffer3 = new byte[readLength2 - 1];
//5.3  過濾第一個位元組(第一個位元組代表資料的位元組個數)
Array.Copy(buffer2, 1, buffer3, 0, buffer3.Length);
var buffer3Reverse = buffer3.Reverse().ToArray();
var value = https://www.cnblogs.com/zhaopei/p/BitConverter.ToInt16(buffer3Reverse, 0);

//6 關閉連接
socket.Shutdown(SocketShutdown.Both);
socket.Close();

ModBusTcp對暫存器的寫入

對于資料寫入就更簡單了,
第一步,組裝請求報文

/// <summary>
/// 獲取寫入命令
/// </summary>
/// <param name="address">暫存器地址</param>
/// <param name="values"></param>
/// <param name="stationNumber">站號</param>
/// <param name="functionCode">功能碼</param>
/// <returns></returns>
public static byte[] GetWriteCommand(ushort address, byte[] values, byte stationNumber, byte functionCode)
{
    byte[] buffer = new byte[13 + values.Length];
    buffer[0] = 0x19;
    buffer[1] = 0xB2;//檢驗資訊,用來驗證response是否串資料了           
    buffer[4] = BitConverter.GetBytes(7 + values.Length)[1];
    buffer[5] = BitConverter.GetBytes(7 + values.Length)[0];//表示的是header handle后面還有多長的位元組

    buffer[6] = stationNumber; //站號
    buffer[7] = functionCode;  //功能碼
    buffer[8] = BitConverter.GetBytes(address)[1];
    buffer[9] = BitConverter.GetBytes(address)[0];//暫存器地址
    buffer[10] = (byte)(values.Length / 2 / 256);
    buffer[11] = (byte)(values.Length / 2 % 256);//寫暫存器數量(除2是一個暫存器兩個位元組,暫存器16位,除以256是byte最大存盤255,)              
    buffer[12] = (byte)(values.Length);          //寫位元組的個數
    values.CopyTo(buffer, 13);                   //把目標值附加到陣列后面
    return buffer;
}

第二步,建立Socket連接,并發送報文

//1 創建Socket
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

//2 建立連接
socket.Connect(new IPEndPoint(IPAddress.Parse(ip), 埠));

//值的轉換
short value = https://www.cnblogs.com/zhaopei/p/32;
var values = BitConverter.GetBytes(value).Reverse().ToArray();

//3 獲取并發送命令(暫存器起始地址、站號、功能碼)
var command = GetWriteCommand(4, values, 2, 16);
socket.Send(command);

//4 關閉連接
socket.Shutdown(SocketShutdown.Both);
socket.Close();

結束

  • 同步至索引目錄:《物聯網基礎組件IoTClient開發系列》
  • 參考1:https://www.cnblogs.com/any91/p/3530540.html
  • 參考2:https://www.cnblogs.com/DreamRecorder/p/9081134.html
  • demo:https://github.com/zhaopeiym/BlogDemoCode/tree/master/IoTClient/ModbusTCP
  • 完整實作:https://github.com/zhaopeiym/IoTClient

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/98047.html

標籤:.NET Core

上一篇:.Net Core實作健康檢查

下一篇:C#/.NET/.NET Core定時任務調度的方法或者組件有哪些--Timer,FluentScheduler還是...

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more