IoTClient開發6 - S7-200SmarTcp協議客戶端實現

来源:https://www.cnblogs.com/zhaopei/archive/2020/05/06/12834787.html
-Advertisement-
Play Games

環境和工具 服務端電腦IP:192.168.1.130 客戶端電腦IP:192.168.1.120 1、在服務端電腦運行 "IoTClientTool" 2、運行 "Wireshark" 3、在客戶端電腦運行 "IoTClientTool" 4、Wireshark得到如下報文 報文分析,plc的連接 ...


環境和工具

服務端電腦IP:192.168.1.130
客戶端電腦IP:192.168.1.120
1、在服務端電腦運行IoTClientTool

2、運行Wireshark

3、在客戶端電腦運行IoTClientTool

4、Wireshark得到如下報文

報文分析,plc的連接

我們看到上面連接西門子plc抓取到了八條報文。其中有tcp的三次握手、和對最後一次響應的回覆,然後就是西門子特有的兩次初始化指令的請求和響應。

兩次初始化指令

不同型號的西門子plc有不同的初始化指令,同型號的指令固定不變。

代碼實現對plc的連接

//直接是Wireshark抓取到的報文數據
var Command1 = new byte[22]
{
    0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,
    0x00,0x01,0x00,0xC1,0x02,0x10,0x00,0xC2,
    0x02,0x03,0x00,0xC0,0x01,0x0A
};

var Command2 = new byte[25]
{
    0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,
    0x01,0x00,0x00,0xCC,0xC1,0x00,0x08,0x00,
    0x00,0xF0,0x00,0x00,0x01,0x00,0x01,0x03,0xC0
};
        
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(new IPEndPoint(IPAddress.Parse(ip), port));

//第一次初始化指令交互
socket.Send(Command1);
var head1 = SocketRead(socket, SiemensConstant.InitHeadLength);
SocketRead(socket, GetContentLength(head1));

//第二次初始化指令交互
socket.Send(Command2);
var head2 = SocketRead(socket, SiemensConstant.InitHeadLength);
SocketRead(socket, GetContentLength(head2));

對寄存器的讀取

我們在客戶端電用IoTClientTool讀取地址V2634,抓取到包

我們可以看到其中很多都是固定的數據,如版本號、協議id等等。所以,我們可以以此規律獲取對應的指令格式

protected byte[] GetReadCommand(byte type, int beginAddress, ushort dbAddress, ushort length)
{
    byte[] command = new byte[31];
    command[0] = 0x03;
    command[1] = 0x00;//[0][1]固定報文頭
    command[2] = (byte)(command.Length / 256);
    command[3] = (byte)(command.Length % 256);//[2][3]整個讀取請求長度為0x1F= 31 
    command[4] = 0x02;
    command[5] = 0xF0;
    command[6] = 0x80;//COTP
    command[7] = 0x32;//協議ID
    command[8] = 0x01;//1  客戶端發送命令 3 伺服器回覆命令
    command[9] = 0x00;
    command[10] = 0x00;//[4]-[10]固定6個位元組
    command[11] = 0x00;
    command[12] = 0x01;//[11][12]兩個位元組,標識序列號,回覆報文相同位置和這個完全一樣;範圍是0~65535
    command[13] = 0x00;
    command[14] = 0x0E;//parameter length([17]-[30]都為parameter剛好14也就是0x0E)
    command[15] = 0x00;
    command[16] = 0x00;//data length
    command[17] = 0x04;//04讀 05寫
    command[18] = 0x01;//讀取數據塊個數
    command[19] = 0x12;//variable specification
    command[20] = 0x0A;//Length of following address specification
    command[21] = 0x10;//Syntax Id: S7ANY 
    command[22] = 0x02;//Transport size: BYTE 
    command[23] = (byte)(length / 256);
    command[24] = (byte)(length % 256);//[23][24]兩個位元組,訪問數據的個數,以byte為單位;
    command[25] = (byte)(dbAddress / 256);
    command[26] = (byte)(dbAddress % 256);//[25][26]DB塊的編號
    command[27] = type;//訪問數據塊的類型
    command[28] = (byte)(beginAddress / 256 / 256);
    command[29] = (byte)(beginAddress / 256);
    command[30] = (byte)(beginAddress % 256);//[28][29][30]訪問DB塊的偏移量
    return command;
}

讀取數據

public Result<byte[]> ReadString(string address, ushort length)
{
    Connect();
    var result = new Result<byte[]>(); 
    //發送讀取信息
    var arg = ConvertArg(address);
    byte[] command = GetReadCommand(arg.TypeCode, arg.BeginAddress, arg.DbBlock, length);
    result.Requst = string.Join(" ", command.Select(t => t.ToString("X2")));
    var dataPackage = SendPackage(command);
    byte[] requst = new byte[length];
    Array.Copy(dataPackage, 25, requst, 0, length);
    //Array.Copy(dataPackage, dataPackage.Length - length, requst, 0, length);
    result.Response = string.Join(" ", dataPackage.Select(t => t.ToString("X2")));
    result.Value = requst; 
    return result;
}

對寄存器的寫入

我們在客戶端電用IoTClientTool對地址V2634寫入值666,抓取到包

我們可以以此規律獲取對應的指令格式

protected byte[] GetWriteCommand(byte type, int beginAddress, ushort dbAddress, byte[] data)
{
    byte[] command = new byte[35 + data.Length];
    command[0] = 0x03;
    command[1] = 0x00;//[0][1]固定報文頭
    command[2] = (byte)((35 + data.Length) / 256);
    command[3] = (byte)((35 + data.Length) % 256);//[2][3]整個讀取請求長度
    command[4] = 0x02;
    command[5] = 0xF0;
    command[6] = 0x80;
    command[7] = 0x32;//[4]-[7]固定數據
    command[8] = 0x01;//1  客戶端發送命令 3 伺服器回覆命令
    command[9] = 0x00;
    command[10] = 0x00;
    command[11] = 0x00;
    command[12] = 0x01;//[9]-[12]標識序列號
    command[13] = 0x00;
    command[14] = 0x0E;
    command[15] = (byte)((4 + data.Length) / 256);
    command[16] = (byte)((4 + data.Length) % 256);//[15][16]寫入長度+4
    command[17] = 0x05;//04讀 05寫
    command[18] = 0x01;//寫入數據塊個數
    command[19] = 0x12;
    command[20] = 0x0A;
    command[21] = 0x10;//[19]-[21]固定
    command[22] = 0x02;//寫入方式,1是按位,2是按字
    command[23] = (byte)(data.Length / 256);
    command[24] = (byte)(data.Length % 256);//寫入數據個數
    command[25] = (byte)(dbAddress / 256);
    command[26] = (byte)(dbAddress % 256);//DB塊的編號
    command[27] = type;
    command[28] = (byte)(beginAddress / 256 / 256 % 256); ;
    command[29] = (byte)(beginAddress / 256 % 256);
    command[30] = (byte)(beginAddress % 256);//[28][29][30]訪問DB塊的偏移量
    command[31] = 0x00;
    command[32] = 0x04;//04 byte(位元組) 03bit(位)
    command[33] = (byte)(data.Length * 8 / 256);
    command[34] = (byte)(data.Length * 8 % 256);//按位計算出的長度
    data.CopyTo(command, 35);
    return command;
}

寫入數據

public Result Write(string address, byte[] data)
{
    if (!socket?.Connected ?? true) Connect();
    Result result = new Result(); 
    Array.Reverse(data);
    //發送寫入信息
    var arg = ConvertArg(address);
    byte[] command = GetWriteCommand(arg.TypeCode, arg.BeginAddress, arg.DbBlock, data);
    result.Requst = string.Join(" ", command.Select(t => t.ToString("X2")));
    var dataPackage = SendPackage(command);
    result.Response = string.Join(" ", dataPackage.Select(t => t.ToString("X2")));
    return result;
}

IoTClient中S7-200SmarTcp協議的使用

安裝

Nuget安裝 Install-Package IoTClient
或圖形化安裝

使用

//1、實例化客戶端 - 輸入正確的IP和埠
SiemensClient client = new SiemensClient(SiemensVersion.S7_200Smart, "127.0.0.1",102);

//2、寫操作
client.Write("Q1.3", true);
client.Write("V2205", (short)11);
client.Write("V2209", 33);

//3、讀操作
var value1 = client.ReadBoolean("Q1.3").Value;
var value2 = client.ReadInt16("V2205").Value;
var value3 = client.ReadInt32("V2209").Value;

//4、如果沒有主動Open,則會每次讀寫操作的時候自動打開自動和關閉連接,這樣會使讀寫效率大大減低。所以建議手動Open和Close。
client.Open();

//5、讀寫操作都會返回操作結果對象Result
var result = client.ReadInt16("V2205");
//5.1 讀取是否成功(true或false)
var isSucceed = result.IsSucceed;
//5.2 讀取失敗的異常信息
var errMsg = result.Err;
//5.3 讀取操作實際發送的請求報文
var requst  = result.Requst;
//5.4 讀取操作服務端響應的報文
var response = result.Response;
//5.5 讀取到的值
var value3 = result.Value;

結束


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 網上很多寫使用WindowsMediaPlayer WMP控制項的文章. 大多數都是從工具欄或COM導入. 最近正在做的CEF整合Asp.Net Core Blazor server side的過程中, 因為CEF編譯支持mp4的dll是涉及了版權的問題, 不能隨便乾的. 要播放mp4, 可以用Win ...
  • 以前一直想著有沒有一個方法能夠把字元串直接轉化成函數的,剛好有需求就找了下,還真有。 微軟地址:https://docs.microsoft.com/en-us/previous-versions/bb894665(v=msdn.10)?redirectedfrom=MSDN 一、安裝 NuGet程 ...
  • 綜述 .NET CORE 3.0開始,桌面端支持WPF了。很多.NET FRAMEWORK的項目已經跑了一陣子了,不是很有必要支持.NET CORE,不過最近用一個程式,為了貫徹一些C 8的特性,需要升級項目到.NET CORE 3.1。 方法 參考 "官方指導" ,需要這麼幾步: 1. 瞭解並更新 ...
  • vue+element部署 一.環境準備 1.因為Nginx依賴於gcc的編譯環境,所以,需要安裝編譯環境來使Nginx能夠編譯起來。 命令:yum install gcc-c++ 2.Nginx的http模塊需要使用pcre來解析正則表達式,需要安裝pcre。 命令:yum install -y ...
  • EFCore數據操作 ...
  • 0. 前言 這是C 基礎知識系列的最後一個內容講解篇,下一篇是基礎知識 實戰篇。這一篇主要講解一下C 程式的結構和主要編程工具。 1. 工具 工欲善其事必先利其器,在實際動手之前我們先來看看想要編寫一套C 程式需要做哪些準備吧。 1.1 選擇 C 的sdk在之前的某個時間點分為了三個方向: 1. . ...
  • 關於數據類型 c#的類型一般分為值類型和引用類型兩種, 值類型的變數包含類型的實例,值類型存放棧中 引用類型的變數包含對類型實例的引用,引用類型放在堆中,在棧中設置一個指針指向堆 值類型: 1.整數型數值:所有的整型數值類型均為值類型,可以用文本進行初始化,每個整型類型的預設值都是0,c#支持以下預 ...
  • 今年年初進了一家新公司,進入之後一邊維護老項目一邊瞭解項目流程,為了接下來的項目重做積累點經驗。 先說下老項目吧,.net fx 3.5+oracle...... 在實際維護中逐漸發現,老項目有標準版、定製版兩種,標準版就是一套代碼,粗略計算了下,全部版本加起來有20+個版本,如果項目重做後還是依照 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...