|
|
using Mirle.Component.MPLC.Interfaces; using NetMQ; using NetMQ.Sockets; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Reflection; using System.Threading;
namespace Mirle.Component.MPLC.MCProtocol { /// <summary>
///
/// </summary>
public class ReadWriteAdapter : IDisposable, IMPLCProvider, IConnectable { /// <summary>
///
/// </summary>
/// <param name="hostId"></param>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
public ReadWriteAdapter(string hostId, string ipAddress, int port) { _hostId = hostId; _plcPort = port; _plcIPAddress = IPAddress.Parse(ipAddress); _frame3E = new Frame3E(); } /// <summary>
///
/// </summary>
private int _timeout = 600; /// <summary>
///
/// </summary>
private StreamSocket _socket; /// <summary>
///
/// </summary>
private readonly string _hostId; /// <summary>
///
/// </summary>
private readonly int _plcPort; /// <summary>
///
/// </summary>
private readonly IPAddress _plcIPAddress = IPAddress.Parse("127.0.0.1"); /// <summary>
///
/// </summary>
private readonly Frame3E _frame3E; /// <summary>
///
/// </summary>
private readonly object _plcLock = new object(); /// <summary>
///
/// </summary>
private readonly object _socketLock = new object(); /// <summary>
///
/// </summary>
public bool IsConnected { get; private set; } = false; /// <summary>
///
/// </summary>
public int Timeout { get => _timeout; set { if (value < 600) { _timeout = 600; } else if (value > 60_000) { _timeout = 60_000; } else { _timeout = value; } } } /// <summary>
///
/// </summary>
/// <returns></returns>
public bool TestConnection() { return TestConnection(_plcIPAddress.ToString(), _plcPort); } /// <summary>
///
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <returns></returns>
public bool TestConnection(string ipAddress, int port) { try { var client = new TcpClient(); var ip = IPAddress.Parse(ipAddress); var asyncResult = client.BeginConnect(ip, port, null, null); bool workIsCompleted = asyncResult.AsyncWaitHandle.WaitOne(_timeout, true); if (workIsCompleted && client.Connected) { client.Close(); return true; } else { return false; } } catch (Exception ex) { TraceLog($"TestConnection: ({ipAddress}:{port}) {ex}"); return false; } } /// <summary>
///
/// </summary>
internal void TestConnectionByPing() { using Ping ping = new Ping(); var pingReply = ping.Send(_plcIPAddress); TraceLog($"TestConnectionByPing: ({_plcIPAddress}) {pingReply.Status}"); } /// <summary>
///
/// </summary>
public void Close() { if (_socket != null) { _socket.Close(); _socket.Dispose(); _socket = null; } IsConnected = false; } /// <summary>
///
/// </summary>
/// <returns></returns>
public bool ReConnect() { if (IsConnected) { TraceLog($"ReConnect : Connect Is Alive"); return true; } else { TraceLog($"ReConnect : Wait NetMQ Reconnect..."); Thread.Sleep(1000); if (Connect()) { TraceLog($"ReConnect : Success!"); return true; } else { TraceLog($"ReConnect : Fail!"); return false; } } } /// <summary>
///
/// </summary>
/// <returns></returns>
public bool Connect() { try { using Ping ping = new Ping(); var pingReply = ping.Send(_plcIPAddress); if (pingReply.Status == IPStatus.Success) { if (_socket != null) Close();
_socket = new StreamSocket(); _socket.Connect($"tcp://{_plcIPAddress}:{_plcPort}"); IsConnected = true; return true; } } catch (Exception ex) { TraceLog($"IP: {_plcIPAddress}:{_plcPort}: {ex}"); } return false; } /// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public bool GetBit(string address) { lock (_plcLock) { if (IsConnected == false) { return false; } var device = MCDevice.Parse(address); if (device == null) { return false; } if (device.IsWord) { int[] data = RequestReadWords(device, 1); if (data.Length > 0) { var bitArray = new BitArray(BitConverter.GetBytes(data[0])); return bitArray.Get(device.BitIndex); } } else if (device.IsBit) { return RequestReadBit(device); } return false; } } /// <summary>
///
/// </summary>
/// <param name="address"></param>
public void SetBitOn(string address) { lock (_plcLock) { SetBit(address, true); } } /// <summary>
///
/// </summary>
/// <param name="address"></param>
public void SetBitOff(string address) { lock (_plcLock) { SetBit(address, false); } } /// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public int ReadWord(string address) { lock (_plcLock) { if (IsConnected == false) return 0;
MCDevice device = MCDevice.Parse(address); if (device == null) return 0;
int[] data = RequestReadWords(device, 1); return data?.Length > 0 ? data[0] : 0; } } /// <summary>
///
/// </summary>
/// <param name="startAddress"></param>
/// <param name="length"></param>
/// <returns></returns>
public int[] ReadWords(string startAddress, int length) { lock (_plcLock) { if (IsConnected == false) return Array.Empty<int>();
List<int> data = new List<int>(); int offset = 0; do { MCDevice device = MCDevice.Parse(startAddress, offset); if (device == null) return null; int numberOfWords = Math.Min(_frame3E.MaximumWords, length - offset); data.AddRange(RequestReadWords(device, numberOfWords)); offset += _frame3E.MaximumWords; } while (offset < length); return data.ToArray(); } } /// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <param name="data"></param>
public void WriteWord(string address, int data) { lock (_plcLock) { if (IsConnected == false) return; MCDevice device = MCDevice.Parse(address); if (device == null) return; RequestWriteWords(device, new[] { data }); } } /// <summary>
///
/// </summary>
/// <param name="startAddress"></param>
/// <param name="data"></param>
public void WriteWords(string startAddress, int[] data) { lock (_plcLock) { if (IsConnected == false) return; int offset = 0; do { MCDevice device = MCDevice.Parse(startAddress, offset); if (device == null) return;
int numberOfWords = Math.Min(_frame3E.MaximumWords, data.Length - offset); int[] readyToWriteData = new int[numberOfWords]; Array.Copy(data, offset, readyToWriteData, 0, numberOfWords); RequestWriteWords(device, readyToWriteData); offset += _frame3E.MaximumWords; } while (offset < data.Length); } } /// <summary>
///
/// </summary>
/// <param name="device"></param>
/// <param name="length"></param>
/// <returns></returns>
private int[] RequestReadWords(MCDevice device, int length) { try { byte[] receiveData = SendData(_frame3E.CreateReadWordsFrame(device, length)); int completeCode = _frame3E.ResolveReadWordsReturnFrame(receiveData, out int[] data); if (completeCode != 0) throw new InvalidOperationException($"{MethodBase.GetCurrentMethod().Name}: MC ErrorCode: 0x{completeCode:X8}"); else return data; } catch (Exception ex) { TraceLog($"MCDevice: {device.AsciiDeviceCode + device.AsciiAddress}.{device.BitIndex}: {ex}"); return new int[length]; } } /// <summary>
///
/// </summary>
/// <param name="device"></param>
/// <returns></returns>
private bool RequestReadBit(MCDevice device) { try { byte[] receiveData = SendData(_frame3E.CreateReadBitFrame(device)); int completeCode = _frame3E.ResolveReadBitReturnFrame(receiveData, out bool data); if (completeCode != 0) throw new InvalidOperationException($"{MethodBase.GetCurrentMethod().Name}: MC ErrorCode: 0x{completeCode:X8}"); else return data; } catch (Exception ex) { TraceLog($"MCDevice: {device.AsciiDeviceCode + device.AsciiAddress}.{device.BitIndex}: {ex}"); return false; } } /// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <param name="isOn"></param>
private void SetBit(string address, bool isOn) { if (IsConnected == false) return;
MCDevice device = MCDevice.Parse(address); if (device == null) return;
if (device.IsWord) { int[] data = RequestReadWords(device, 1); if (data.Length > 0) { BitArray bitArray = new BitArray(BitConverter.GetBytes(data[0])); bitArray.Set(device.BitIndex, isOn); int[] newData = new int[1]; bitArray.CopyTo(newData, 0); RequestWriteWords(device, newData); } } else if (device.IsBit) { RequestWriteBit(device, isOn); } } /// <summary>
///
/// </summary>
/// <param name="device"></param>
/// <param name="isOn"></param>
/// <returns></returns>
private bool RequestWriteBit(MCDevice device, bool isOn) { try { byte[] receiveData = SendData(_frame3E.CreateWriteBitFrame(device, isOn)); int completeCode = _frame3E.ResolveWriteBitReturnFrame(receiveData); if (completeCode != 0) throw new InvalidOperationException($"{MethodBase.GetCurrentMethod().Name}: MC ErrorCode: 0x{completeCode:X8}"); else return true; } catch (Exception ex) { TraceLog($"MCDevice: {device.AsciiDeviceCode + device.AsciiAddress}.{device.BitIndex}: {ex}"); return false; } } /// <summary>
///
/// </summary>
/// <param name="device"></param>
/// <param name="data"></param>
/// <returns></returns>
private bool RequestWriteWords(MCDevice device, int[] data) { try { byte[] receiveData = SendData(_frame3E.CreateWriteWordsFrame(device, data)); int completeCode = _frame3E.ResolveWriteWordsReturnFrame(receiveData); if (completeCode != 0) throw new InvalidOperationException($"{MethodBase.GetCurrentMethod().Name}: MC ErrorCode: 0x{completeCode:X4}"); else return true; } catch (Exception ex) { TraceLog($"MCDevice: {device.AsciiDeviceCode + device.AsciiAddress}.{device.BitIndex}: {ex}"); return false; } } /// <summary>
///
/// </summary>
/// <param name="message"></param>
private void TraceLog(string message) { string logPath = $@"{AppDomain.CurrentDomain.BaseDirectory}LOG\{DateTime.Now:yyyy-MM-dd}\{_hostId}\"; if (!Directory.Exists(logPath)) Directory.CreateDirectory(logPath);
string logFilename = $@"{logPath}MC_{_plcIPAddress.ToString().Replace('.', '_')}_{_plcPort}_Trace.log"; using var file = new StreamWriter(File.Open(logFilename, FileMode.Append)); file.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] {message}"); } /// <summary>
/// 傳送資料
/// </summary>
/// <param name="frame"></param>
/// <returns></returns>
private byte[] SendData(byte[] frame) { lock (_socketLock) { if (_socket == null || _socket.IsDisposed) { IsConnected = false; TraceLog($"SendData: Fail, NoConnect!"); } try { for (int i = 0; i < 5; i++) { if (_socket == null) continue;
TimeSpan timeout = TimeSpan.FromMilliseconds(_timeout); byte[] id = _socket.Options.Identity; if (_socket.TrySendFrame(timeout, id, true) && _socket.TrySendFrame(timeout, frame)) { if (_socket.TryReceiveFrameBytes(timeout, out byte[] receiveId) && _socket.TryReceiveFrameBytes(timeout, out byte[] dataFrame)) { if (receiveId.SequenceEqual(id)) return dataFrame; else TraceLog($"SendData: SequenceEqual Id Fail, Retrt Count{i}"); } else TraceLog($"SendData: TryReceiveFrameBytes Fail, Retrt Count{i}"); } } } catch (Exception ex) { TraceLog($"SendData: {ex}"); } IsConnected = false; return null; } }
#region IDisposable Support
private bool _disposedValue = false; /// <summary>
/// 釋放資源
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing) { if (!_disposedValue) { if (disposing) { _socket?.Close(); _socket?.Dispose(); } _disposedValue = true; } } /// <summary>
/// 解構是
/// </summary>
~ReadWriteAdapter() { Dispose(false); } /// <summary>
/// 釋放資源
/// </summary>
public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion
} }
|