基於三菱Q系列PLC#MC協議上位機通訊(四)-C#通訊模塊開發


背景

  在做工控領域系統集成時,由於項目需要跟三菱PLC對接。主要是實現數據的下發及設備狀態數據的讀取。三菱自帶的MC協議具有讀寫速度塊,可集成性好,對電氣編程角度非常友好電氣工程師只需要分配D區地址塊長度就可以與上位機通過該地址塊進行交互。下面開始講訴我的三菱通訊模塊實現過程:

  • 1.MC通訊協議

寫入數據協議

 讀取數據協議

 讀取數據反饋

寫入數據反饋

   2.代碼實現

  • 協議

數據讀取

 1 private byte[] GetReadCommand(int startIndex, int len)
 2         {
 3             byte[] command = new byte[21];
 4             //發起指令
 5             command[0] = 0x50;
 6             command[1] = 0x00;
 7             //網路編號
 8             command[2] = 0x00;
 9             //PLC編號
10             command[3] = 0xFF;
11             //請求目標模塊IO編號
12             command[4] = 0xFF;
13             command[5] = 0x03;
14             //請求目標模塊站編號
15             command[6] = 0x00;
16             //應答數據物理長度
17             command[7] = 0x0C;
18             command[8] = 0x00;
19             //cpu監視定時器
20             command[9] = 0x11;
21             command[10] = 0x00;
22             //命令 
23             command[11] = 0x01;
24             command[12] = 0x04;
25             //子命令
26             command[13] = 0x00;
27             command[14] = 0x00;
28             //首地址,字節序反轉
29             command[17] = (byte)(startIndex / 2 / 256 / 256 % 256);
30             command[16] = (byte)(startIndex / 2 / 256 % 256);
31             command[15] = (byte)(startIndex / 2 % 256);
32             //軟元件
33             command[18] = 0xA8;
34             //讀取長度,字節序反轉
35             command[20] = (byte)(len / 2 / 256 % 256);
36             command[19] = (byte)(len / 2 % 256);
37 
38             return command;
39         }
View Code

數據寫入

 1 private byte[] GetWriteCommand(int startIndex,byte[] bytes)
 2         {
 3             byte[] command = new byte[21];
 4 
 5             //發起指令
 6             command[0] = 0x50;
 7             command[1] = 0x00;
 8             //網路編號
 9             command[2] = 0x00;
10             //PLC編號
11             command[3] = 0xFF;
12             //請求目標模塊IO編號
13             command[4] = 0xFF;
14             command[5] = 0x03;
15             //請求目標模塊站編號
16             command[6] = 0x00;
17             //應答數據物理長度,字節序反轉
18             command[8] = (byte)((bytes.Length + 12) / 256 % 256);
19             command[7] = (byte)((bytes.Length + 12) % 256);
20             //cpu監視定時器
21             command[9] = 0x11;
22             command[10] = 0x00;
23             //命令 
24             command[11] = 0x01;
25             command[12] = 0x14;
26             //子命令
27             command[13] = 0x00;
28             command[14] = 0x00;
29             //首地址,字節序反轉
30             command[17] = (byte)(startIndex/2 / 256 / 256 % 256);
31             command[16] = (byte)(startIndex/2 / 256 % 256);
32             command[15] = (byte)(startIndex/2 % 256);
33             //軟元件
34             command[18] = 0xA8;
35             //寫入長度,字節序反轉
36             command[20] = (byte)(bytes.Length / 2 / 256 % 256);
37             command[19] = (byte)(bytes.Length / 2 % 256);
38             //data
39             return command.Concat(bytes).ToArray();
40         }
View Code

數據反饋

 1 private byte[] GetResponse()
 2         {
 3             byte[] response = new byte[7];
 4             //發起指令
 5             response[0] = 0xD0;
 6             response[1] = 0x00;
 7             //網路編號
 8             response[2] = 0x00;
 9             //PLC編號
10             response[3] = 0xFF;
11             //請求目標模塊IO編號
12             response[4] = 0xFF;
13             response[5] = 0x03;
14             //請求目標模塊站編號
15             response[6] = 0x00;
16             return response;
17         }
View Code
  • 連接
 1 public bool Open(out string msg)
 2         {
 3             msg = string.Empty;
 4             try
 5             {
 6                 if (!SocketHelper.PingCheck(Ip, ConnectTimeout))
 7                 {
 8                     msg = "網絡故障!";
 9                     return false;
10                 }
11                 System.Diagnostics.Stopwatch sp = new System.Diagnostics.Stopwatch();
12                 sp.Start();
13                 tcpClient = new TcpClient();
14                 tcpClient.ReceiveTimeout = ReceiveTimeout;
15                 tcpClient.SendTimeout = SendTimeout;
16                 tcpClient.Connect(Ip, Port);
17                 Thread.Sleep(10);
18                 if (!tcpClient.Connected)
19                 {
20                     throw new ApplicationException($"未連接到{Ip}");
21                 }
22                
23                 msg = $"連接[{Ip}]成功,耗時{sp.Elapsed.TotalMilliseconds.ToString()}ms";
24                 return true;
25 
26             }
27             catch (Exception ex)
28             {
29                 msg = $"連接失敗:{ex.Message}";
30                 return false;
31             }
32         }
View Code
  • 斷開連接
 1  public bool Close(out string msg)
 2         {
 3             msg = string.Empty;
 4             try
 5             {
 6                 tcpClient?.Close();
 7                 tcpClient = null;
 8                 return true;
 9             }
10             catch (Exception ex)
11             {
12                 msg = $"關閉失敗:{ex.Message}";
13                 tcpClient = null;
14                 return false;
15             }
16         }
View Code
  • 讀取操作
 1 public bool ReadDataBlock(out string msg, int startIndex, int len, out byte[] reData)
 2         {
 3             msg = string.Empty;reData = new byte[0];
 4             try
 5             {
 6                 System.Diagnostics.Stopwatch sp = new System.Diagnostics.Stopwatch();
 7                 sp.Start();
 8 
 9                 #region 連接狀態
10                 if (tcpClient == null || !tcpClient.Connected)
11                 {
12                     if (!Open(out msg))
13                     {
14                         Thread.Sleep(40);
15                         if (!Open(out msg)) return false;
16                     }
17                 }
18                 #endregion
19 
20                 int i = 0;
21                 for (int index = startIndex; index < startIndex + len; index += MelsecConsts.MAXREADDATE)
22                 {
23                     int _newLen = len + startIndex - index;
24 
25                     if (_newLen > MelsecConsts.MAXREADDATE) _newLen = MelsecConsts.MAXREADDATE;
26                     i++;
27 
28                     byte[] command = GetReadCommand(index, _newLen);
29 
30                     if (!SocketHelper.SendData(out msg, tcpClient, command))
31                     {
32                         msg = $"發送讀取指令失敗:{msg}!";
33                         return false;
34                     }
35                     byte[] r = GetResponse();
36 
37                     byte[] head = new byte[r.Length+2+2];//head+Length+err->7+2+2
38 
39                     if (!SocketHelper.ReceiveData(out msg, tcpClient, head))
40                     {
41                         msg = $"讀取數據接收失敗:{msg}!";
42                         return false;
43                     }
44                     #region 頭部校驗
45 
46                     if (!head.EqualsBytes(r))
47                     {
48                         msg = $"反饋頭部校驗失敗";
49                         return false;
50                     };
51                     #endregion
52 
53                     int le = (int)BitConverter.ToUInt16(head, 7);
54 
55                     //錯誤碼
56                     if (!head.EqualsBytes(new byte[2],9))
57                     {
58                         byte[] e2 = new byte[le-2];//剩余錯誤碼
59 
60                         if (!SocketHelper.ReceiveData(out msg, tcpClient, e2))
61                         {
62                             msg = $"二次接收錯誤碼失敗:{msg}!";
63                             return false;
64                         }
65                         byte[] e1 = new byte[2];
66                         Array.Copy(head, 9, e1, 0, 2);
67                         msg = $"{string.Join(" ", string.Join(" ", (e1.Concat(e2)).Select(x => x.ToString("x2"))))}";
68                         return false;
69                     };
70 
71                     if (_newLen != (le - 2))
72                     {
73                         msg = $"接收數據不正確";
74                         return false;
75                     }
76 
77                     byte[] nData = new byte[_newLen];
78 
79                     if (!SocketHelper.ReceiveData(out msg, tcpClient, nData))
80                     {
81                         msg = $"讀取數據接收失敗:{msg}!";
82                         return false;
83                     }
84 
85                     reData = reData.Concat(nData).ToArray();
86                 }
87 
88                 msg = $"讀取({reData.Length})字節數據成功,耗時{sp.Elapsed.TotalMilliseconds.ToString()}ms,{i}次讀取";
89                 return true;
90             }
91             catch (Exception ex)
92             {
93                 Close(out string _msg);
94                 msg = $"讀取失敗:{ex.Message},{_msg}";
95                 return false;
96             }
97         }
View Code
  • 寫入操作
 1 public bool WriteDataBlock(out string msg, int startIndex, byte[] inData)
 2         {
 3             msg = string.Empty;
 4             try
 5             {
 6                 System.Diagnostics.Stopwatch sp = new System.Diagnostics.Stopwatch();
 7                 sp.Start();
 8 
 9                 #region 連接狀態
10                 if (tcpClient == null || !tcpClient.Connected)
11                 {
12                     if (!Open(out msg))
13                     {
14                         Thread.Sleep(40);
15                         if (!Open(out msg)) return false;
16                     }
17                 }
18                 #endregion
19                 //奇數補0
20                 if (inData.Length % 2 > 0) inData = inData.Concat(new byte[1] { 0 }).ToArray();
21                 int i = 0; int len = inData.Length;
22                 for (int index = startIndex; index < startIndex + len; index += MelsecConsts.MAXWRIDATE)
23                 {
24                     int _newLen = len + startIndex - index;
25                     if (_newLen > MelsecConsts.MAXWRIDATE) _newLen = MelsecConsts.MAXWRIDATE;
26                     i++;
27                     byte[] nData = new byte[_newLen];
28 
29                     Array.Copy(inData, index - startIndex, nData, 0, _newLen);
30 
31                     byte[] command = GetWriteCommand(index, nData);
32 
33                     if (!SocketHelper.SendData(out msg, tcpClient, command))
34                     {
35                         msg = $"發送寫入指令失敗:{msg}!";
36                         return false;
37                     }
38                     byte[] r = GetResponse();
39 
40                     byte[] head = new byte[r.Length + 4];
41 
42                     if (!SocketHelper.ReceiveData(out msg, tcpClient, head))
43                     {
44                         msg = $"寫入數據接收失敗:{msg}!";
45                         return false;
46                     }
47                     #region 頭部校驗
48 
49                     if (!head.EqualsBytes(r))
50                     {
51                         msg = $"反饋頭部校驗失敗";
52                         return false;
53                     };
54                     #endregion
55 
56                     #region 長度校驗
57 
58                     int le = (int)BitConverter.ToUInt16(head, 7);
59 
60                     if (le != 2|| !head.EqualsBytes(new byte[2], 9))
61                     {
62                         byte[] e2 = new byte[le-2];
63                         if (!SocketHelper.ReceiveData(out msg, tcpClient, e2))
64                         {
65                             msg = $"寫入數據失敗,錯誤碼數據接收失敗:{msg}!";
66                             return false;
67                         }
68                         byte[] e1 = new byte[2];
69                         Array.Copy(head,9, e1,0,2);
70                         msg = $"寫入數據失敗,錯誤碼:{string.Join(" ", (e1.Concat(e2)).Select(x => x.ToString("x2")))}";
71                         return false;
72                     }
73                     #endregion
74                 }
75                 msg = $"寫入({len})字節數據成功,耗時{sp.Elapsed.TotalMilliseconds.ToString()}ms,{i}次寫入";
76                 return true;
77             }
78             catch (Exception ex)
79             {
80                 Close(out string _msg);
81                 msg = $"寫入失敗:{ex.Message},{_msg}";
82                 return false;
83             }
84         }
View Code

測試結果:

 

 

 

 

完畢!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM