Sic.Framework/MECF.Framework.Common/MECF/Framework/Common/Communications/SerialPortConnectionBase.cs

285 lines
6.3 KiB
C#
Raw Normal View History

2023-04-13 11:51:03 +08:00
using System;
using System.Collections.Generic;
using System.IO.Ports;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
namespace MECF.Framework.Common.Communications
{
public abstract class SerialPortConnectionBase : IConnection
{
private AsyncSerialPort _port;
protected HandlerBase _activeHandler;
private object _lockerActiveHandler = new object();
private string _address;
private bool _isAsciiMode;
public int retryTime = 0;
private PeriodicJob _thread;
private object _locker = new object();
private LinkedList<string> _lstAsciiMsgs = new LinkedList<string>();
private LinkedList<byte[]> _lstBinsMsgs = new LinkedList<byte[]>();
private string _newLine;
public string Address => _address;
public bool IsConnected => _port.IsOpen();
public bool IsBusy => _activeHandler != null;
public bool IsCommunicationError { get; private set; }
public string LastCommunicationError { get; private set; }
public bool Connect()
{
return _port.Open();
}
public bool Disconnect()
{
_port.Close();
return true;
}
public void TerminateCom()
{
_port.Dispose();
}
public SerialPortConnectionBase(string port, int baudRate = 9600, int dataBits = 8, Parity parity = Parity.None, StopBits stopBits = StopBits.One, string newline = "\r", bool isAsciiMode = true, int interval = 2)
{
_address = port;
_isAsciiMode = isAsciiMode;
_newLine = newline;
_port = new AsyncSerialPort(port, baudRate, dataBits, parity, stopBits, newline, isAsciiMode);
_port.OnDataChanged += _port_OnAsciiDataReceived;
_port.OnBinaryDataChanged += _port_OnBinaryDataChanged;
_port.OnErrorHappened += _port_OnErrorHappened;
_thread = new PeriodicJob(interval, OnTimer, port + ".MonitorHandler", isStartNow: true);
}
private bool OnTimer()
{
lock (_locker)
{
if (_isAsciiMode)
{
while (_lstAsciiMsgs.Count > 0)
{
string value = _lstAsciiMsgs.First.Value;
_port_HandleAsciiData(value);
_lstAsciiMsgs.RemoveFirst();
}
}
else
{
while (_lstBinsMsgs.Count > 0)
{
byte[] value2 = _lstBinsMsgs.First.Value;
_port_HandleBinarayData(value2);
_lstBinsMsgs.RemoveFirst();
}
}
}
return true;
}
public void SetPortAddress(string portName)
{
_port.PortName = portName;
}
private void _port_OnErrorHappened(string obj)
{
LOG.Error(obj);
}
public virtual bool SendMessage(string message)
{
if (_port != null && _port.IsOpen())
{
return _port.Write(message);
}
LOG.Error("No connection writing message " + message);
return false;
}
public virtual bool SendMessage(byte[] message)
{
if (_port != null && _port.IsOpen())
{
return _port.Write(message);
}
LOG.Error("No connection writing message " + string.Join(" ", Array.ConvertAll(message, (byte x) => x.ToString("X2"))));
return false;
}
public void ForceClear()
{
lock (_lockerActiveHandler)
{
IsCommunicationError = false;
_activeHandler = null;
}
}
public void Execute(HandlerBase handler)
{
if (_activeHandler != null || handler == null || !_port.IsOpen())
{
return;
}
lock (_lockerActiveHandler)
{
retryTime = 0;
_activeHandler = handler;
_activeHandler.SetState(EnumHandlerState.Sent);
}
if ((!_isAsciiMode && handler.SendBinary == null) || (_isAsciiMode ? SendMessage(handler.SendText) : SendMessage(handler.SendBinary)))
{
return;
}
lock (_lockerActiveHandler)
{
_activeHandler = null;
}
}
protected virtual MessageBase ParseResponse(string rawMessage)
{
return null;
}
protected virtual MessageBase ParseResponse(byte[] rawMessage)
{
return null;
}
protected virtual void OnEventArrived(MessageBase msg)
{
}
protected virtual void ActiveHandlerProceedMessage(MessageBase msg)
{
lock (_lockerActiveHandler)
{
if (_activeHandler != null && (msg.IsFormatError || (_activeHandler.HandleMessage(msg, out var transactionComplete) && transactionComplete)))
{
if (_isAsciiMode || (!_isAsciiMode && _activeHandler.SendBinary != null))
{
_activeHandler = null;
}
else
{
_activeHandler.SetState(EnumHandlerState.Sent);
}
}
}
}
public void EnableLog(bool enable)
{
_port.EnableLog = enable;
}
private void ProceedTransactionMessage(MessageBase msg)
{
if (msg == null || msg.IsFormatError)
{
SetCommunicationError(isError: true, "received invalid response message.");
return;
}
if (msg.IsEvent)
{
OnEventArrived(msg);
}
ActiveHandlerProceedMessage(msg);
}
private void _port_OnBinaryDataChanged(byte[] binaryData)
{
lock (_locker)
{
_lstBinsMsgs.AddLast(binaryData);
}
}
private void _port_HandleBinarayData(byte[] binaryData)
{
MessageBase msg = ParseResponse(binaryData);
ProceedTransactionMessage(msg);
}
private void _port_OnAsciiDataReceived(string oneLineMessage)
{
lock (_locker)
{
if (string.IsNullOrEmpty(_newLine))
{
_lstAsciiMsgs.AddLast(oneLineMessage);
return;
}
string[] array = oneLineMessage.Split(_newLine.ToCharArray());
foreach (string text in array)
{
if (!string.IsNullOrEmpty(text))
{
_lstAsciiMsgs.AddLast(text + _newLine);
}
}
}
}
private void _port_HandleAsciiData(string oneLineMessage)
{
MessageBase msg = ParseResponse(oneLineMessage);
ProceedTransactionMessage(msg);
}
public HandlerBase MonitorTimeout()
{
HandlerBase result = null;
lock (_lockerActiveHandler)
{
if (_activeHandler != null && _activeHandler.CheckTimeout())
{
EV.PostWarningLog("System", Address + " receive " + _activeHandler.Name + " timeout");
result = _activeHandler;
_activeHandler = null;
SetCommunicationError(isError: true, "receive response timeout");
}
}
return result;
}
public void Retry()
{
if (_activeHandler != null && _port.IsOpen())
{
_activeHandler.SetState(EnumHandlerState.Sent);
if (!(_isAsciiMode ? SendMessage(_activeHandler.SendText) : SendMessage(_activeHandler.SendBinary)))
{
_activeHandler = null;
}
}
}
public void SetCommunicationError(bool isError, string reason)
{
IsCommunicationError = isError;
LastCommunicationError = reason;
}
}
}