├── 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 | --------------------------------------------------------------------------------