You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
573 lines
18 KiB
573 lines
18 KiB
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
|
|
}
|
|
}
|