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

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
}
}