├── README.md
├── Program.cs
├── .gitattributes
├── MCCommand.cs
├── LICENSE
├── Properties
└── AssemblyInfo.cs
├── Test.Designer.cs
├── Test.cs
├── Utility.cs
├── MCProtocolDriver.csproj
├── Test.resx
└── MCProtocolDriver.cs
/README.md:
--------------------------------------------------------------------------------
1 | # MCProtocolDriver
2 | For mitsubishi PLC
3 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace MCProtocolDriver
5 | {
6 | class Program
7 | {
8 | [STAThread]
9 | static void Main()
10 | {
11 | Application.EnableVisualStyles();
12 | Application.SetCompatibleTextRenderingDefault(false);
13 | Application.Run(new Test());
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/MCCommand.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace SYN.BC.Driver.PLC.MCProtocolDriver
3 | {
4 | public class MCCommand
5 | {
6 | //Command
7 |
8 | ///
9 | /// 成批讀取
10 | ///
11 | public const string BatchRead = "0401";
12 |
13 | ///
14 | /// 成批寫入
15 | ///
16 | public const string BatchWrite = "1401";
17 |
18 | ///
19 | /// 隨機讀取
20 | ///
21 | public const string RandomRead = "0403";
22 |
23 | ///
24 | /// 隨機寫入
25 | ///
26 | public const string RandomWrite = "1402";
27 |
28 | //Sub Command
29 | public const string Word = "0000";
30 | public const string Bit = "0001";
31 |
32 | public enum CommunicationMode
33 | {
34 | Binary,
35 | ASCII,
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Luke
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // 組件的一般資訊是由下列的屬性集控制。
6 | // 變更這些屬性的值即可修改組件的相關
7 | // 資訊。
8 | [assembly: AssemblyTitle("MCProtocolDriver")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("MCProtocolDriver")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // 將 ComVisible 設定為 false 會使得這個組件中的類型
18 | // 對 COM 元件而言為不可見。如果您需要從 COM 存取這個組件中
19 | // 的類型,請在該類型上將 ComVisible 屬性設定為 true。
20 | [assembly: ComVisible(false)]
21 |
22 | // 下列 GUID 為專案公開 (Expose) 至 COM 時所要使用的 typelib ID
23 | [assembly: Guid("b865e61b-39cc-4a3f-a679-f8a74cba19f3")]
24 |
25 | // 組件的版本資訊由下列四個值所組成:
26 | //
27 | // 主要版本
28 | // 次要版本
29 | // 組建編號
30 | // 修訂編號
31 | //
32 | // 您可以指定所有的值,也可以依照以下的方式,使用 '*' 將組建和修訂編號
33 | // 指定為預設值:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Test.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace MCProtocolDriver
2 | {
3 | partial class Test
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.SuspendLayout();
32 | //
33 | // Test
34 | //
35 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
36 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
37 | this.ClientSize = new System.Drawing.Size(284, 262);
38 | this.Name = "Test";
39 | this.Text = "Test";
40 | this.Load += new System.EventHandler(this.Test_Load);
41 | this.ResumeLayout(false);
42 |
43 | }
44 |
45 | #endregion
46 | }
47 | }
--------------------------------------------------------------------------------
/Test.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Windows.Forms;
4 | using SYN.BC.Driver.PLC.MCProtocolDriver;
5 | using SYN.BC.Core.Common;
6 | using SYN.BC.Driver.PLC.Mitsubishi.MappingAnalysis;
7 |
8 | namespace MCProtocolDriver
9 | {
10 | public partial class Test : Form
11 | {
12 | private MCProtocol _MCP;
13 |
14 | public Test()
15 | {
16 | InitializeComponent();
17 | }
18 |
19 | private void Test_Load(object sender, EventArgs e)
20 | {
21 | try
22 | {
23 | var Parameter = new List();
24 | Parameter.Add(new ConstructorParameter() { Name = "IP", Value = "192.168.2.102" });
25 | Parameter.Add(new ConstructorParameter() { Name = "Port", Value = "2005" });
26 |
27 | _MCP = new MCProtocol(Parameter);
28 | _MCP.Ini();
29 | bool connect = _MCP.Connect();
30 | short[] read = new short[1];
31 | for (int i = 0; i < 10; i++)
32 | {
33 | int val = _MCP.ReadWordVal("0", 4000, ref read, "ZR");
34 | //_MCP.ReadBitVal("0", 5000, ref read, "B");
35 | }
36 |
37 |
38 | //short[] wriveval = MappingAnalysisUtility.HEXStringToShortArray("010203040506");
39 | //_MCP.WriteWordVal("0", wriveval.Length, wriveval, "W");
40 |
41 | //short[] wriveval = MappingAnalysisUtility.BinStringToShortArray("1011010");
42 | //_MCP.WriteBitVal("0", wriveval.Length,wriveval,"M");
43 | _MCP.DisConnect();
44 | }
45 | catch (Exception ex)
46 | {
47 | Console.WriteLine(ex.ToString());
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Utility.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Text;
4 |
5 | namespace SYN.BC.Driver.PLC.MCProtocolDriver
6 | {
7 | public static class Utility
8 | {
9 | ///
10 | /// 將byte[] 轉換成MC Protocol的string
11 | ///
12 | /// byte array
13 | /// string
14 | public static string ByteToMCString(byte[] byteAry)
15 | {
16 | StringBuilder sb = new StringBuilder();
17 | for(int i = 0;i<=byteAry.Length - 1; i++)
18 | {
19 | sb.Append((byteAry[i] / 16).ToString("X"));
20 | sb.Append((byteAry[i] % 16).ToString("X"));
21 | }
22 | return sb.ToString();
23 | }
24 |
25 | public static byte[] HexStringToByteArray(string HexStr)
26 | {
27 | return Enumerable.Range(0, HexStr.Length)
28 | .Where(x => x % 2 == 0)
29 | .Select(x => Convert.ToByte(HexStr.Substring(x, 2), 16))
30 | .ToArray();
31 | }
32 |
33 | /// Swaps two bytes in a byte array
34 | /// The array in which elements are to be swapped
35 | /// The index of the first element to be swapped
36 | /// The index of the second element to be swapped
37 | public static void SwapBytes(this byte[] buf, int i, int j)
38 | {
39 | byte temp = buf[i];
40 | buf[i] = buf[j];
41 | buf[j] = temp;
42 | }
43 |
44 | ///
45 | /// 取得MC Protocol回傳的Code
46 | ///
47 | /// MC Protocol回傳的字串
48 | /// Complete Code
49 | public static string GetCompleteCode(string Str)
50 | {
51 | return Str?.Substring(18, 4);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/MCProtocolDriver.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {B865E61B-39CC-4A3F-A679-F8A74CBA19F3}
8 | WinExe
9 | Properties
10 | MCProtocolDriver
11 | MCProtocolDriver
12 | v4.5.2
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | Form
54 |
55 |
56 | Test.cs
57 |
58 |
59 |
60 |
61 |
62 | {0d3e1e22-4788-4f80-9eed-851dfbe30e2a}
63 | Core
64 |
65 |
66 | {f66667b7-2d75-412e-92e5-7d86e3411aab}
67 | MappingAnalysis
68 |
69 |
70 | {d2bd1fba-7398-47c6-824a-5701b09c3605}
71 | PLC
72 |
73 |
74 |
75 |
76 | Test.cs
77 |
78 |
79 |
80 |
87 |
--------------------------------------------------------------------------------
/Test.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/MCProtocolDriver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using SYN.BC.Core.Common;
7 | using System.Net.Sockets;
8 | using System.Net;
9 | using SYN.BC.Core.Log;
10 | using System.Net.NetworkInformation;
11 | using static SYN.BC.Driver.PLC.Utility;
12 | using static SYN.BC.Core.Common.Constant;
13 | using System.Threading;
14 |
15 | namespace SYN.BC.Driver.PLC.MCProtocolDriver
16 | {
17 | public class MCProtocol : AbstractPLCDriver
18 | {
19 | private Socket _SocketTCP;
20 | private IPEndPoint _TargetIPPort;
21 | private Task _task;
22 | private string _IP;
23 | private string _Port;
24 | private List _PortList = new List();
25 | private Logger _Logger;
26 | private Logger _DMLog;
27 | private ushort _ReconnectTime = 1000;
28 |
29 | private string _BasicFormat = "5000"; //ASCII Header(3E)
30 | private string _SubBasicFormat = "03FF00"; //請求目標模組 I/O 編號[03FF] + 請求目標模組站號[00]
31 | private string _CPU_TimerStr = "0010";
32 | private string _NetNo="00";
33 | private string _PCNo = "FF";
34 |
35 | private const short _MAX_BIT_RW_POINT_ASCII = 3584; //ASCII模式Bit最大讀寫數量
36 | private const short _MAX_BIT_RW_POINT_BINARY = 7168; //Binary模式Bit最大讀寫數量
37 | private const short _MAX_WORD_RW_POINT = 960; //Word最大讀寫數量
38 |
39 | private MCCommand.CommunicationMode _Mode = MCCommand.CommunicationMode.ASCII;
40 |
41 | ///
42 | /// 建構MCProtocol
43 | ///
44 | /// 參數
45 | public MCProtocol(List para)
46 | {
47 | _IP = (from p in para
48 | where p.Name == "IP"
49 | select p.Value).FirstOrDefault();
50 | if (string.IsNullOrEmpty(_IP))
51 | throw new Exception("[IP can not be empty!]");
52 |
53 | _Port = (from p in para
54 | where p.Name == "Port"
55 | select p.Value).FirstOrDefault();
56 | if (string.IsNullOrEmpty(_Port))
57 | throw new Exception("[Port can not be empty!]");
58 |
59 | _PortList = _Port.Split(',').Select(x => Convert.ToInt32(x)).ToList();
60 | _Port = _PortList.First().ToString();
61 |
62 | string NetNo = (from p in para
63 | where p.Name == "NetNo"
64 | select p.Value).FirstOrDefault();
65 | if (!string.IsNullOrEmpty(NetNo)) { _NetNo = NetNo; }
66 |
67 | string PCNo = (from p in para
68 | where p.Name == "PCNo"
69 | select p.Value).FirstOrDefault();
70 | if (!string.IsNullOrEmpty(NetNo)) { _PCNo = PCNo; }
71 | }
72 |
73 | ~MCProtocol()
74 | {
75 | if (_SocketTCP != null && _SocketTCP.Connected) { Close(); }
76 | }
77 |
78 | ///
79 | ///
80 | ///
81 | public override bool IsConnected
82 | {
83 | get
84 | {
85 | if (_SocketTCP != null)
86 | return _SocketTCP.Connected;
87 | else
88 | return false;
89 | }
90 | }
91 |
92 | ///
93 | /// Connect
94 | ///
95 | /// Result
96 | public override bool Connect()
97 | {
98 | try
99 | {
100 | _SocketTCP = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
101 | _SocketTCP.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(false, 1));
102 | _SocketTCP.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 2500);
103 | _TargetIPPort = new IPEndPoint(IPAddress.Parse(_IP), int.Parse(_Port));
104 | //_SocketTCP?.Connect(_TargetIPPort);
105 |
106 | IAsyncResult result = _SocketTCP.BeginConnect(_TargetIPPort, null, null);
107 | result.AsyncWaitHandle.WaitOne(1000, true);//只等1秒
108 |
109 | if (!result.IsCompleted)
110 | {
111 | DisConnect();
112 | }
113 | //else if (_SocketTCP.Connected == true)
114 | //{
115 |
116 | //}
117 |
118 | //Start reconnect function
119 | if (_task.Status != TaskStatus.Running)
120 | _task.Start();
121 |
122 | return _SocketTCP.Connected;
123 | }
124 | catch (Exception ex)
125 | {
126 | _Logger.Error(ex);
127 | return false;
128 | }
129 | }
130 |
131 | ///
132 | /// DisConnect
133 | ///
134 | /// Result
135 | public override bool DisConnect()
136 | {
137 | try
138 | {
139 | //_SocketTCP?.Shutdown(SocketShutdown.Both);
140 | _SocketTCP?.Close();
141 |
142 | //_TcpClient?.Close();
143 | //_TcpClient = new TcpClient(AddressFamily.InterNetwork);
144 | return true;
145 | }
146 | catch(ObjectDisposedException)
147 | {
148 | return true;
149 | }
150 | catch (Exception ex)
151 | {
152 | throw ex;
153 | }
154 | }
155 |
156 | ///
157 | /// Initial
158 | ///
159 | public override void Ini()
160 | {
161 | try
162 | {
163 | _Logger = new Logger(nameof(MCProtocol));
164 | _DMLog = new Logger("DMLoger");
165 |
166 | //Reconnect Function
167 | _task = new Task(() =>
168 | {
169 | while (true)
170 | {
171 | try
172 | {
173 | if (_SocketTCP != null && (!_SocketTCP.Connected || !PingIP(_IP)))
174 | {
175 | _Port = Utility.getPortNo(_Port, _PortList).ToString();
176 | _Logger.Warn(new LogMessage("", nameof(MCProtocol)) { Message = $"Reconnecting..., Port:{_Port}" });
177 | DisConnect();
178 | Connect();
179 | }
180 | }
181 | catch (Exception ex)
182 | {
183 | _Logger.Error(ex);
184 | }
185 | System.Threading.SpinWait.SpinUntil(() => false, _ReconnectTime);
186 | }
187 | });
188 |
189 | }
190 | catch (Exception ex)
191 | {
192 | throw ex;
193 | }
194 | }
195 |
196 | ///
197 | /// Not Implemented
198 | ///
199 | ///
200 | ///
201 | public override long PutData(TransferMsg Msg)
202 | {
203 | throw new NotImplementedException();
204 | }
205 |
206 | ///
207 | /// 讀取Bit
208 | ///
209 | /// 起始位址
210 | /// 大小
211 | /// 讀到的值
212 | /// 記憶體名稱
213 | ///
214 | public override int ReadBitVal(string StartAddress, int Size, ref short[] Value, string Name = "")
215 | {
216 | byte[] SendDataByte;
217 | byte[] RecvDataByte = new byte[4999];
218 | try
219 | {
220 | if (string.IsNullOrEmpty(Name)) { throw new Exception("Device name can not be empty!"); }
221 | if (_SocketTCP == null)
222 | return -1;
223 |
224 | int Address = int.Parse(StartAddress);
225 | int ReadAddress = Address;
226 | string ReadString = "";
227 | string Command = "";
228 | int ByteRead = -1;
229 | do
230 | {
231 | Command = CreateCommandString(MCCommand.BatchRead, MCCommand.Bit, Name, ReadAddress.ToString(), _MAX_BIT_RW_POINT_ASCII);
232 | ReadAddress += _MAX_BIT_RW_POINT_ASCII;
233 | SendDataByte = Encoding.ASCII.GetBytes(Command);
234 | _SocketTCP.Send(SendDataByte, SendDataByte.Length, SocketFlags.None);
235 | ByteRead = _SocketTCP.Receive(RecvDataByte, RecvDataByte.Length, SocketFlags.None);
236 | if (ByteRead > 0)
237 | {
238 | string RecvStr = Encoding.ASCII.GetString(RecvDataByte, 0, ByteRead);
239 | string CompleteCode = Utility.GetCompleteCode(RecvStr);
240 | if (CompleteCode != "0000") { return Convert.ToInt32(CompleteCode, 16); }
241 | ReadString += RecvStr.Substring(22);
242 | }
243 | else
244 | {
245 | _Logger.Error(new LogMessage("", nameof(MCProtocol)) { Message = $"Read Bit fail, name={Name}, Address={StartAddress}, Size={Size}" });
246 | return -1;
247 | }
248 | } while (ReadAddress < (Address + Size));
249 | Value = Enumerable.Range(0, Size).Select(x => short.Parse(ReadString[x].ToString())).ToArray();
250 | return 0;
251 | }
252 | catch (Exception ex)
253 | {
254 | _Logger.Error(ex);
255 | throw ex;
256 | }
257 | }
258 |
259 | ///
260 | /// Not Implemented
261 | ///
262 | ///
263 | ///
264 | ///
265 | ///
266 | ///
267 | public override Task ReadBitValAsync(string StartAddress, int Size, ref short[] Value, string Name = "")
268 | {
269 | throw new NotImplementedException();
270 | }
271 |
272 | ///
273 | /// 讀取Word
274 | ///
275 | /// 起始位址
276 | /// 大小
277 | /// 讀到的值
278 | /// 記憶體名稱
279 | /// return complete code
280 | public override int ReadWordVal(string StartAddress, int Size, ref short[] Value, string Name = "")
281 | {
282 | byte[] SendDataByte;
283 | byte[] RecvDataByte= new byte[4999];
284 | try
285 | {
286 | if (string.IsNullOrEmpty(Name)) { throw new Exception("Device name can not be empty!"); }
287 |
288 | int Address = int.Parse(StartAddress);
289 | int ReadAddress= Address;
290 | string ReadString = "";
291 | string Command = "";
292 | int ByteRead = -1;
293 | //do
294 | //{
295 | Command = CreateCommandString(MCCommand.BatchRead, MCCommand.Word, Name, ReadAddress.ToString(), Size);
296 | SendDataByte = Encoding.ASCII.GetBytes(Command);
297 | _SocketTCP?.Send(SendDataByte, SendDataByte.Length, SocketFlags.None);
298 | Thread.Sleep(1);
299 | ByteRead = _SocketTCP.Receive(RecvDataByte, RecvDataByte.Length, SocketFlags.None);
300 | if (ByteRead>0)
301 | {
302 | string RecvStr = Encoding.ASCII.GetString(RecvDataByte, 0, ByteRead);
303 | string CompleteCode = Utility.GetCompleteCode(RecvStr);
304 | if (CompleteCode != "0000") { return Convert.ToInt32(CompleteCode, 16); }
305 | ReadString += RecvStr.Substring(22);
306 | }
307 | else
308 | {
309 | _Logger.Error(new LogMessage("", nameof(MCProtocol)) { Message = $"Read Word fail, name={Name}, Address={StartAddress}, Size={Size}" });
310 | return -1;
311 | }
312 | //ReadAddress += _MAX_WORD_RW_POINT;
313 | //} while (ReadAddress < (Address + Size));
314 |
315 | Value = HEXStringToShortArray(ReadString).Take(Size).ToArray();
316 | return 0;
317 | }
318 | catch(SocketException sex)
319 | {
320 | _Logger.Info(new LogMessage("", nameof(MCProtocol)) { Direct = emDirect.H2E, Message = sex.ToString() });
321 | return -1;
322 | }
323 | catch (Exception ex)
324 | {
325 | _Logger.Error(ex);
326 | throw ex;
327 | }
328 | }
329 |
330 | ///
331 | /// Not Implemented
332 | ///
333 | ///
334 | ///
335 | ///
336 | ///
337 | ///
338 | public override Task ReadWordValAsync(string StartAddress, int Size, ref short[] Value, string Name = "")
339 | {
340 | throw new NotImplementedException();
341 | }
342 |
343 | public override long SetData(TransferMsg Msg)
344 | {
345 | throw new NotImplementedException();
346 | }
347 |
348 | ///
349 | /// 寫入Bit
350 | ///
351 | /// 起始位址
352 | /// 寫入大小
353 | /// 寫入的值
354 | /// 記憶體名稱
355 | /// Return code
356 | public override int WriteBitVal(string StartAddress, int Size, short[] Value, string Name = "")
357 | {
358 | byte[] WriteByte;
359 | byte[] RecvDataByte = new byte[4999];
360 | try
361 | {
362 | if (string.IsNullOrEmpty(Name)) { throw new Exception("Device name can not be empty!"); }
363 | if (_SocketTCP == null)
364 | return -1;
365 |
366 | int Address = int.Parse(StartAddress);
367 | var WriteAry = Value.Split(_MAX_BIT_RW_POINT_ASCII);
368 | var QArray = WriteAry.Select((val, idx) => new { Index = idx, Value = val });//產生index
369 | foreach (var WAry in QArray)
370 | {
371 | string WriteAddress = (Address + (WAry.Index * _MAX_BIT_RW_POINT_ASCII)).ToString();
372 | string Command = CreateCommandString(MCCommand.BatchWrite, MCCommand.Bit, Name, WriteAddress, WAry.Value.Count(), WAry.Value.ToArray());
373 | WriteByte = Encoding.ASCII.GetBytes(Command);
374 | _SocketTCP.Send(WriteByte, WriteByte.Length, SocketFlags.None);
375 | int RtnByte = _SocketTCP.Receive(RecvDataByte, RecvDataByte.Length, SocketFlags.None);
376 | if (RtnByte > 0)
377 | {
378 | string RecvStr = Encoding.ASCII.GetString(RecvDataByte, 0, RtnByte);
379 | string CompleteCode = Utility.GetCompleteCode(RecvStr);
380 | if (CompleteCode != "0000") { return Convert.ToInt32(CompleteCode, 16); }
381 | }
382 | else
383 | {
384 | _Logger.Error(new LogMessage("", nameof(MCProtocol)) { Message = $"Write Bit fail, name={Name}, Address={StartAddress}, Size={Size}" });
385 | return -1;
386 | }
387 | }
388 | return 0;
389 | }
390 | catch (Exception ex)
391 | {
392 | _Logger.Error(ex);
393 | throw ex;
394 | }
395 | }
396 |
397 | ///
398 | /// Not Implemented
399 | ///
400 | ///
401 | ///
402 | ///
403 | ///
404 | ///
405 | public override Task WriteBitValAsync(string StartAddress, int Size, short[] Value, string Name = "")
406 | {
407 | throw new NotImplementedException();
408 | }
409 |
410 | ///
411 | /// 寫入Word
412 | ///
413 | /// 起始位址
414 | /// 寫入大小
415 | /// 寫入的值
416 | /// 記憶體名稱
417 | /// Return code
418 | public override int WriteWordVal(string StartAddress, int Size, short[] Value, string Name = "")
419 | {
420 | byte[] WriteByte;
421 | byte[] RecvDataByte = new byte[4999];
422 | try
423 | {
424 | if (string.IsNullOrEmpty(Name)) { throw new Exception("Device name can not be empty!"); }
425 |
426 | int Address = (int)new System.ComponentModel.Int32Converter().ConvertFromString(StartAddress);
427 | var WriteAry = Value.Split(_MAX_WORD_RW_POINT);
428 | var QArray = WriteAry.Select((val, idx) => new { Index = idx, Value = val });//產生index
429 | foreach (var WAry in QArray)
430 | {
431 | string WriteAddress = (Address + (WAry.Index * _MAX_WORD_RW_POINT)).ToString();
432 | string Command = CreateCommandString(MCCommand.BatchWrite, MCCommand.Word, Name, WriteAddress, WAry.Value.Count(), WAry.Value.ToArray());
433 | WriteByte = Encoding.ASCII.GetBytes(Command);
434 | _SocketTCP?.Send(WriteByte, WriteByte.Length, SocketFlags.None);
435 | Thread.Sleep(1);
436 | int RtnByte = _SocketTCP.Receive(RecvDataByte, RecvDataByte.Length, SocketFlags.None);
437 | if (RtnByte > 0)
438 | {
439 | string RecvStr = Encoding.ASCII.GetString(RecvDataByte, 0, RtnByte);
440 | string CompleteCode = Utility.GetCompleteCode(RecvStr);
441 | if (CompleteCode != "0000") { return Convert.ToInt32(CompleteCode, 16); }
442 | }
443 | else
444 | {
445 | _Logger.Error(new LogMessage("", nameof(MCProtocol)) { Message = $"Write Word fail, name={Name}, Address={StartAddress}, Size={Size}" });
446 | return -1;
447 | }
448 | }
449 | return 0;
450 | }
451 | catch (Exception ex)
452 | {
453 | _Logger.Error(ex);
454 | throw ex;
455 | }
456 | }
457 |
458 | ///
459 | /// Not Implemented
460 | ///
461 | ///
462 | ///
463 | ///
464 | ///
465 | ///
466 | public override Task WriteWordValAsync(string StartAddress, int Size, short[] Value, string Name = "")
467 | {
468 | throw new NotImplementedException();
469 | }
470 |
471 | ///
472 | /// ASCII Mode
473 | ///
474 | ///
475 | ///
476 | ///
477 | ///
478 | ///
479 | ///
480 | /// Command String
481 | private string CreateCommandString( string command, string subcommand, string DeviceName, string StartAddress, int Size, short[] WriteVal = null)
482 | {
483 | string Address;
484 | string Name = DeviceName.PadRight(2, '*');
485 |
486 | if (Name == "X*" || Name == "Y*" || Name == "B*" || Name == "W*" || Name == "SB" || Name == "SW" || Name == "DX" || Name == "DY" || Name == "ZR")
487 | Address = int.Parse(StartAddress).ToString("X").PadLeft(6, '0');
488 | else
489 | Address = int.Parse(StartAddress).ToString().PadLeft(6, '0');
490 |
491 | string Tmp = _CPU_TimerStr +
492 | command +
493 | subcommand +
494 | Name +
495 | Address +
496 | Size.ToString("X").PadLeft(4, '0');
497 |
498 | if (WriteVal != null) //Write Command
499 | {
500 | if(subcommand == MCCommand.Bit)
501 | {
502 | var TmpAry = WriteVal.Select(x => x == 48 ? (short)0 : (short)1).ToArray(); //轉換 48=0, 49=1
503 | Tmp += string.Join("", TmpAry);
504 | }
505 | else //Word
506 | {
507 | byte[] Writebyte = WriteVal.SelectMany(BitConverter.GetBytes).ToArray();
508 | for (int i = 0; i < Writebyte.Length; i += 2) // big/little endian轉換
509 | Writebyte.SwapBytes(i, i + 1);
510 | Tmp += Utility.ByteToMCString(Writebyte);
511 | }
512 | }
513 |
514 | string SendCMD = _BasicFormat +
515 | _NetNo +
516 | _PCNo +
517 | _SubBasicFormat +
518 | Tmp.Length.ToString("X").PadLeft(4, '0') +
519 | Tmp;
520 | return SendCMD;
521 | }
522 |
523 | ///
524 | /// Connection status
525 | ///
526 | ///
527 | public override bool CheckStatus()
528 | {
529 | if (_SocketTCP != null)
530 | return _SocketTCP.Connected;
531 | else
532 | return false;
533 | }
534 |
535 | ///
536 | /// Close connection
537 | ///
538 | public override void Close()
539 | {
540 | DisConnect();
541 | }
542 |
543 | ///
544 | /// Use Ping to check connection
545 | ///
546 | /// IP Address
547 | ///
548 | private bool PingIP(string IP)
549 | {
550 | IPAddress tIP = IPAddress.Parse(IP);
551 | Ping tPingControl = new Ping();
552 | PingReply tReply = tPingControl.Send(tIP, 100);
553 | tPingControl.Dispose();
554 |
555 | if (tReply.Status != IPStatus.Success)
556 | {
557 | _Logger.Info(new LogMessage("", nameof(MCProtocol)) { Message = $"Ping {IP} {tReply.Status.ToString()}" });
558 | return false;
559 | }
560 | else
561 | return true;
562 | }
563 |
564 | ///
565 | /// Write Data To PLC
566 | ///
567 | ///
568 | ///
569 | /// Error Code
570 | public override int WriteData(TransferMsg Data, List dataList)
571 | {
572 | int returncode = -1;
573 | foreach (TagMessagePLC taginfo in dataList)
574 | {
575 | if (taginfo.IsBit)
576 | returncode = WriteBitVal(taginfo.Offset, taginfo.Size, taginfo.RawData, taginfo.DeviceName);
577 | else
578 | returncode = WriteWordVal(taginfo.Offset, taginfo.Size, taginfo.RawData, taginfo.DeviceName);
579 |
580 | if (returncode == 0)
581 | {
582 | DMLogMessage _DM = new DMLogMessage(new List { taginfo }.ToArray(), Data, nameof(MCProtocol)) { Direct = emDirect.H2E };
583 | _DMLog.Info(_DM);
584 | }
585 | else
586 | {
587 | string[] arValueHex = Enumerable.Range(0, taginfo.RawData.Length).Select(x => taginfo.RawData[x].ToString("X4")).ToArray();
588 | LogMessage LM = new LogMessage(Data, nameof(MCProtocol)) { EqpID = Data.EqpID, Direct = emDirect.H2E };
589 | LM.Message = $"Write Data Error! code:0x{returncode.ToString("X")} Device={taginfo.DeviceName },Size={taginfo.Size},DeviceValue={string.Join(" ", arValueHex)}";
590 | _Logger.Error(LM);
591 | }
592 | }
593 |
594 | return returncode;
595 | }
596 | }
597 | }
598 |
--------------------------------------------------------------------------------