- Flush buffer (DiscardInBuffer) removed as some bytes are missing from time to time. The IrDA sniffer showed these on the communication line, but they are incompletely received. Assuming the DiscardInBuffer may be delayed, so the incoming data will be scrapped. IrdaTransmitProtocol: - WakeUpMessage marked and debug output
679 lines
28 KiB
C#
679 lines
28 KiB
C#
using NLog;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO.Ports;
|
|
using System.Threading;
|
|
using Xylem.Common.CommonCore.Consts;
|
|
using Xylem.Common.CommonCore.ThreadWatcher;
|
|
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
|
|
using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments;
|
|
using Xylem.Common.Utils.Logging;
|
|
|
|
namespace Xylem.Common.Hardware.Interfaces.Ports.SerialPorts
|
|
{
|
|
/// <summary>
|
|
/// All Ports must use BasePort as an base class
|
|
/// its support some wrapping event/function handling
|
|
/// </summary>
|
|
public abstract class BaseSerialPort : IPort, IDisposable
|
|
{
|
|
|
|
private ILogger _byteDataLogger;
|
|
internal ILogger AsciiDataLogger;
|
|
|
|
/// <inheritdoc />
|
|
public virtual event EventHandler<BasePortDataEventArgs> OnRawRecordReceived;
|
|
|
|
/// <inheritdoc />
|
|
public virtual event EventHandler<BasePortDataEventArgs> OnRawRecordSendOut;
|
|
|
|
//initially do not signal event
|
|
private readonly AutoResetEvent _onSyncReceiveThread = new AutoResetEvent(false);
|
|
private readonly Thread _receiveThread;
|
|
private readonly CancellationTokenSource _receiveToken = new CancellationTokenSource();
|
|
|
|
/// <summary>
|
|
/// Delay between bytes if receiving has started in milliseconds
|
|
/// </summary>
|
|
private const Int32 InterByteReadDelayMs = 50;
|
|
|
|
/// <summary>
|
|
/// internal SerialPort class
|
|
/// </summary>
|
|
private readonly SerialPort _serialPort;
|
|
|
|
|
|
/// <summary>
|
|
/// Activate raw record recording
|
|
/// </summary>
|
|
public Boolean RecordStreamingRawData = false;
|
|
//public Boolean RecordStreamingRawData
|
|
//{
|
|
// get => _recordStreamingRawData;
|
|
// set => _recordStreamingRawData = value;
|
|
//}
|
|
|
|
/// <summary>
|
|
/// date and time of incoming first date interrupt on serial IO to set the
|
|
/// PC time-stamp to the record
|
|
/// </summary>
|
|
private DateTimeOffset _receiveTimeStampPc;
|
|
|
|
/// <summary>
|
|
/// Individual port settings depending on transmit protocol
|
|
/// </summary>
|
|
protected TransmitPortSettings PortSettingsForTransmitProtocol;
|
|
|
|
/// <inheritdoc />
|
|
public String GetPortName()
|
|
{
|
|
return _serialPort.PortName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// store a identification of a port, this contains the Slot, Port, Protocol and Type
|
|
/// e.g. "Slot:1, Port:COM4, Protocol:Request, Type:IrDA -"
|
|
/// </summary>
|
|
public String Ident;
|
|
|
|
/// <summary>
|
|
/// ctor for SerialComPort with and full constructed <see cref="T:System.IO.Ports.SerialPort" /> properties
|
|
/// </summary>
|
|
/// <param name="ident">set port as unique</param>
|
|
/// <param name="serialPort">Class for communication, all parameters for serial communication needs to be set</param>
|
|
/// <param name="setting">Class for store all parameters for serial communication needs to be set came from transmit protocol</param>
|
|
protected BaseSerialPort(String ident, SerialPort serialPort, TransmitPortSettings setting)
|
|
{
|
|
Ident = ident;
|
|
|
|
//communication port settings
|
|
_serialPort = serialPort;
|
|
_serialPort.BaudRate = (Int32)setting.BaudRate;
|
|
|
|
PortSettingsForTransmitProtocol = setting;
|
|
|
|
//assign thread to loop
|
|
_receiveThread = new Thread(ReadingThreadLoop) { Name = $"{Ident} Reading thread" };
|
|
|
|
AsciiDataLogger = NLogHelper.CreateOrGetMultiLogger(Ident, "", "LedRawData", "TargetLedRawDataBase", "LedRawDataBase");
|
|
_byteDataLogger = NLogHelper.CreateOrGetMultiLogger(Ident, "", "UartSerialPort", "COMBase", "BaseSerialPort");
|
|
}
|
|
|
|
|
|
public void RefreshLogger()
|
|
{
|
|
|
|
AsciiDataLogger = NLogHelper.CreateOrGetMultiLogger(Ident, "", "LedRawData", "TargetLedRawDataBase", "LedRawDataBase");
|
|
_byteDataLogger = NLogHelper.CreateOrGetMultiLogger(Ident, "", "UartSerialPort", "COMBase", "BaseSerialPort");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// received bytes in buffer of SerialComPort, calls base function
|
|
/// </summary>
|
|
/// <returns>number of bytes in Rx buffer</returns>
|
|
protected Int32 BytesToRead()
|
|
{
|
|
try
|
|
{
|
|
return _serialPort.BytesToRead;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_byteDataLogger.Error(ex);
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// read single byte from Rx buffer of SerialComPort, calls base function
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected Int32 ReadByte()
|
|
{
|
|
|
|
try
|
|
{
|
|
return _serialPort.ReadByte();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_byteDataLogger.Error(ex);
|
|
return 0;
|
|
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void PortWrite(Byte[] record)
|
|
{
|
|
try
|
|
{
|
|
SpecificPortWrite(record);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new SystemException($"{Ident} Communication error while sending data to port", ex);
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
/// <summary>
|
|
/// flush Rx and Tx buffer of SerialComPort, calls base functions
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
try
|
|
{
|
|
if (!_serialPort.IsOpen)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_serialPort.DiscardInBuffer();
|
|
_serialPort.DiscardOutBuffer();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_byteDataLogger.Error(ex);
|
|
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dispose serial port
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
try
|
|
{
|
|
|
|
//remove registration for receive delegate
|
|
_serialPort.DataReceived -= PhysicalDataReceived;
|
|
|
|
//Cancel send and receive tokens
|
|
_receiveToken.Cancel();
|
|
|
|
//Run thread again to notice CancellationToken has changed
|
|
_onSyncReceiveThread.Set();
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_byteDataLogger.Error(ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
public void Open()
|
|
{
|
|
try
|
|
{
|
|
|
|
//set receive interrupt threshold for immediate execution
|
|
_serialPort.ReceivedBytesThreshold = 1;
|
|
_serialPort.DataReceived += PhysicalDataReceived;
|
|
|
|
//start reading thread, will be immediately put to waitSleepJoin in ReadingThreadLoop
|
|
//to avoid side effects with RFID communication
|
|
|
|
ThreadWatcher.Instance.Start(_receiveThread);
|
|
|
|
if (!_serialPort.IsOpen)
|
|
{
|
|
_serialPort.Open();
|
|
if (!_serialPort.IsOpen)
|
|
{
|
|
_byteDataLogger.Error($"{Ident} Port cannot be opened");
|
|
throw new ApplicationException($"{Ident} Port cannot be opened");
|
|
}
|
|
_byteDataLogger.Info($"{Ident} Port is opened");
|
|
}
|
|
else
|
|
{
|
|
_byteDataLogger.Warn($"{Ident} Port was already opened");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_byteDataLogger.Error($"{Ident} {ex.Message}");
|
|
throw new ApplicationException($"{Ident} {ex.Message}");
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Getting the base port
|
|
/// </summary>
|
|
protected SerialPort GetBaseComport()
|
|
{
|
|
return _serialPort;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fill FIFO with received data
|
|
/// </summary>
|
|
/// <remarks date="2018-Feb-16" author="T.Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2018-Feb-21" author="T.Wiedebusch">
|
|
/// - Time stamp added
|
|
/// </remarks>
|
|
/// <remarks date="2018-Dec-14" author="T.Wiedebusch">
|
|
/// - Timeout moved from <see cref="ReadingThreadLoop"/> to PhysicalDataReceived to avoid
|
|
/// <see cref="TimeoutException"/> of serial port while waiting on first incoming
|
|
/// data.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Dec-12" author="T.Wiedebusch">
|
|
/// - Inter byte read delay used instead of response delay!!!
|
|
/// </remarks>
|
|
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
public virtual void PhysicalDataReceived(Object sender, SerialDataReceivedEventArgs e)
|
|
{
|
|
if (!_receiveToken.IsCancellationRequested)
|
|
{
|
|
//remove event delegate to avoid repeated execution during one record
|
|
_serialPort.DataReceived -= PhysicalDataReceived;
|
|
|
|
//put the actual time stamp to this record being able to assign it correctly
|
|
//even if the decoding is delayed. This time stamp will be used to do the first
|
|
//synchronization at start and stop of the measurement. Therefor the serial buffer
|
|
//has to be flushed in advance to avoid wrong time stamp to "old" records
|
|
_receiveTimeStampPc = DateTimeOffset.UtcNow;
|
|
|
|
//start timeout for hanging read communication called inter byte delay
|
|
_serialPort.ReadTimeout = InterByteReadDelayMs;
|
|
// _serialPort.ReadTimeout = PortSettingsForTransmitProtocol.ResponseTimeoutMs;
|
|
|
|
//wake up the reading threat from JoinWaitSleep
|
|
_onSyncReceiveThread.Set();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// <see cref="SyncMarkRecord" />
|
|
/// </summary>
|
|
private SyncMarkRecord _syncMarkRecord;
|
|
|
|
/// <inheritdoc />
|
|
public void SynchronizeReceiveBuffer(SyncMarkRecord syncMarkRecord)
|
|
{
|
|
try
|
|
{
|
|
|
|
//not in test bench situation only for temp logging
|
|
if (syncMarkRecord == SyncMarkRecord.FlushBuffer)
|
|
{
|
|
FlushBuffer();
|
|
return;
|
|
}
|
|
//DecodeEveryPackage is ongoing and SkipDecoding is requested = do nothing
|
|
if (_syncMarkRecord == SyncMarkRecord.DecodeEveryPackage &&
|
|
syncMarkRecord == SyncMarkRecord.SkipDecoding)
|
|
{
|
|
return;
|
|
}
|
|
//deny intermediate record when a start or end record is requested
|
|
if (syncMarkRecord == SyncMarkRecord.DecodeIntermediate &&
|
|
(_syncMarkRecord == SyncMarkRecord.SyncEnd ||
|
|
_syncMarkRecord == SyncMarkRecord.SyncStart))
|
|
{
|
|
return;
|
|
}
|
|
//being able to detect the first incoming record after a sync is requested, the buffer is going to be flushed
|
|
//if the measurement will be started or stopped
|
|
if (syncMarkRecord == SyncMarkRecord.SyncEnd ||
|
|
syncMarkRecord == SyncMarkRecord.SyncStart ||
|
|
syncMarkRecord == SyncMarkRecord.DecodeIntermediate)
|
|
{
|
|
FlushBuffer();
|
|
}
|
|
|
|
_byteDataLogger.Info($"{Ident} Mark next incoming record as {syncMarkRecord} (was {_syncMarkRecord})");
|
|
_syncMarkRecord = syncMarkRecord;
|
|
|
|
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_byteDataLogger.Error($"{Ident} {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private void FlushBuffer()
|
|
{
|
|
//check if flushing is allowed
|
|
if (null != PortSettingsForTransmitProtocol.ReceiveBufferFlushThreshold)
|
|
{
|
|
//flush buffer above threshold to get an accurate actual record with next record coming in
|
|
if (_serialPort.IsOpen && _serialPort.BytesToRead > PortSettingsForTransmitProtocol.ReceiveBufferFlushThreshold)
|
|
{
|
|
_byteDataLogger.Warn($"{Ident} Flushed receive buffer({_serialPort.BytesToRead}Byte)");
|
|
_serialPort.DiscardInBuffer();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reading thread loop getting data from serial port
|
|
/// </summary>
|
|
/// <remarks date="2018-Feb-16" author="T.Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2018-Mar-08" author="T.Wiedebusch">
|
|
/// - Reading will be executed and repeated until buffer is empty or timeout
|
|
/// </remarks>
|
|
/// <remarks date="2018-Mar-09" author="T.Wiedebusch">
|
|
/// - Receive byte protocol more dynamically on position of length information in record
|
|
/// and additional length,
|
|
/// - decoding of data controlled by dataSyncMarker!= SyncMarkRecord.SkipDecoding to speed up recording
|
|
/// </remarks>
|
|
/// <remarks date="2018-Oct-23" author="T.Wiedebusch">
|
|
/// - Directly invoked onRawRecord Received event,
|
|
/// - Doubling of sync byte implemented
|
|
/// </remarks>
|
|
/// <remarks date="2018-Dec-14" author="T.Wiedebusch">
|
|
/// - Timeout moved from ReadingThreadLoop to <see cref="PhysicalDataReceived"/> to avoid
|
|
/// <see cref="TimeoutException"/> of serial port while waiting on first incoming
|
|
/// data and flush receive buffer at timeout to force task to enter JoinWaitSleep state.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
|
|
/// - Hide data in logging for e.g. passwords
|
|
/// </remarks>
|
|
/// <remarks date="2019-Dec-03" author="T.Wiedebusch">
|
|
/// - Raw record recording for missing sync-byte issues.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Dec-04" author="T.Wiedebusch">
|
|
/// - Try to recover raw record on missing SYNC byte by adding the SYNC upfront and sending
|
|
/// the record to the decoding thread which will detect if just the SYNC byte had been
|
|
/// missed, then the CRC will match and the record can be decoded.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Dec-05" author="T.Wiedebusch">
|
|
/// - Improved recovering of data sets at byte records with preceding SYNC byte:
|
|
/// - used payload length == 0 to skip decoding, it makes no sense to decode something where
|
|
/// the payload is empty,
|
|
/// - wait a certain time to complete incoming data.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Dec-12" author="T.Wiedebusch">
|
|
/// - Inter byte read delay used instead of response delay.
|
|
/// </remarks>
|
|
/// <remarks date="2019-Dec-14" author="T.Wiedebusch">
|
|
/// - Flush buffer if received length is 0.
|
|
/// </remarks>
|
|
/// <remarks date="2022-Jul-15" author="T.Wiedebusch">
|
|
/// - Flush buffer (DiscardInBuffer) removed as some bytes are missing from time to time.
|
|
/// The IrDA sniffer showed these on the communication line, but they are incompletely received.
|
|
/// Assuming the DiscardInBuffer may be delayed, so the incoming data will be scrapped.
|
|
/// </remarks>
|
|
private void ReadingThreadLoop()
|
|
{
|
|
//put the receive thread to JoinWaitSleep to avoid reading and logger output for timeout on
|
|
//RFID communication, because RFID will handle the physical receive by itself
|
|
//here the assignment of the _synReadingThreadEvent to the reading thread is being done
|
|
_onSyncReceiveThread.WaitOne();
|
|
|
|
try
|
|
{
|
|
while (!_receiveToken.IsCancellationRequested)
|
|
{
|
|
try
|
|
{
|
|
//remind data marker for this Thread execution time slice
|
|
var dataSyncMarkThisRun = _syncMarkRecord;
|
|
|
|
//read line of ASCII indicated by syncByte == null
|
|
if (null == PortSettingsForTransmitProtocol.ProtSyncByte)
|
|
{
|
|
//exit is timeout or received line
|
|
var rxStringRecord = _serialPort.ReadLine();
|
|
|
|
if (!string.IsNullOrEmpty(rxStringRecord))
|
|
{
|
|
//decode optional at synchronized data SyncStart, SyncEnd or DecodeIntermediate
|
|
if (SyncMarkRecord.SkipDecoding != dataSyncMarkThisRun)
|
|
{
|
|
OnRawRecordReceived?.Invoke(this, new StringPortDataEventArgs(rxStringRecord,
|
|
_receiveTimeStampPc, dataSyncMarkThisRun));
|
|
}
|
|
if (RecordStreamingRawData)
|
|
{
|
|
AsciiDataLogger.Trace($"{rxStringRecord}");
|
|
}
|
|
}
|
|
}
|
|
//byte record with preceding SYNC byte
|
|
else
|
|
{
|
|
//normally decoding is required
|
|
dataSyncMarkThisRun = SyncMarkRecord.DecodeEveryPackage;
|
|
|
|
//this is the record for decoding
|
|
var rxByteRecord = new List<Byte>();
|
|
|
|
//this is the informational record for logging on undetected SYNC byte
|
|
var rxRawRecord = new List<Byte>();
|
|
|
|
//each byte protocol has to start with a syncByte, this has to be detected first
|
|
Byte rxByte;
|
|
do
|
|
{
|
|
rxByte = (Byte)_serialPort.ReadByte();
|
|
//record raw data stream for output on missing sync-byte to investigate this issue
|
|
rxRawRecord.Add(rxByte);
|
|
} while (_serialPort.BytesToRead > 0 && PortSettingsForTransmitProtocol.ProtSyncByte != rxByte);
|
|
|
|
// if syncByte has not been detected skip read loop and wait for next incoming record
|
|
if (PortSettingsForTransmitProtocol.ProtSyncByte == rxByte)
|
|
{
|
|
//save received SYNC byte
|
|
rxByteRecord.Add(rxByte);
|
|
|
|
//to assemble the length it has to be extracted first from the record at given index,
|
|
//the payload length index cannot be 0 because at this position is always the SYNC byte,
|
|
//the overall length includes the SYNC byte!
|
|
var length = PortSettingsForTransmitProtocol.ProtLengthIndex + 1;
|
|
|
|
//if the protLengthIndex is null the protAddLength equals the entire record length
|
|
if (null == PortSettingsForTransmitProtocol.ProtLengthIndex)
|
|
{
|
|
length = PortSettingsForTransmitProtocol.ProtAddLength;
|
|
}
|
|
|
|
//read until length index to extract the length from the record
|
|
//the length index cannot be at position 0, because this is always the syncByte
|
|
do
|
|
{
|
|
rxByte = (Byte)_serialPort.ReadByte();
|
|
//read the next byte if doubled sync byte detected and required by transmit protocol settings
|
|
if (PortSettingsForTransmitProtocol.DoubleSyncByte)
|
|
{
|
|
if (rxByte.Equals(PortSettingsForTransmitProtocol.ProtSyncByte) &&
|
|
rxByteRecord[rxByteRecord.Count - 1].Equals(PortSettingsForTransmitProtocol.ProtSyncByte))
|
|
{
|
|
//skips this byte and read the next one
|
|
rxByte = (Byte)_serialPort.ReadByte();
|
|
}
|
|
}
|
|
//add byte to receive result buffer
|
|
rxByteRecord.Add(rxByte);
|
|
|
|
//capture the length at given index and readjust the record length
|
|
if (null != PortSettingsForTransmitProtocol.ProtLengthIndex
|
|
&& rxByteRecord.Count - 1 == PortSettingsForTransmitProtocol.ProtLengthIndex)
|
|
{
|
|
//if the received length indicates empty payload, then nothing is to decode
|
|
if (rxByte == 0)
|
|
{
|
|
//exit this loop
|
|
dataSyncMarkThisRun = SyncMarkRecord.SkipDecoding;
|
|
FlushBuffer();
|
|
}
|
|
else
|
|
{
|
|
//build the new length to continue this receive loop
|
|
length = (UInt16)(PortSettingsForTransmitProtocol.ProtAddLength + rxByte);
|
|
}
|
|
}
|
|
|
|
//wait a certain time to let the data stream coming in
|
|
if (length - rxByteRecord.Count - 1 > _serialPort.BytesToRead)
|
|
{
|
|
Thread.Sleep(2);
|
|
}
|
|
|
|
} while (rxByteRecord.Count < length && dataSyncMarkThisRun != SyncMarkRecord.SkipDecoding);
|
|
|
|
//all data received or skip decoding marked, skip decoding will clear the response timeout
|
|
//these protocols have always to be decoded because the base is a request protocol
|
|
OnRawRecordReceived?.Invoke(this, new ListBytePortDataEventArgs(rxByteRecord,
|
|
_receiveTimeStampPc, dataSyncMarkThisRun));
|
|
}
|
|
else
|
|
{
|
|
//output actual byte and recorded raw data stream to investigate missed sync-byte
|
|
_byteDataLogger.Warn($"{Ident} Response SYNC Byte missing. Raw Byte Received: " +
|
|
$"{BitConverter.ToString(rxRawRecord.ToArray())}");
|
|
}
|
|
}
|
|
}
|
|
catch (ThreadAbortException)
|
|
{ throw; }
|
|
catch (TimeoutException)
|
|
{
|
|
//flush receive buffer at timeout to force task to enter JoinWaitSleep state in finally
|
|
//if (_serialPort.IsOpen)
|
|
//{
|
|
// _serialPort.DiscardInBuffer();
|
|
//}
|
|
|
|
_byteDataLogger.Trace($"{Ident} Read timeout({InterByteReadDelayMs}ms)");
|
|
//_byteDataLogger.Trace($"{Ident} Read timeout({PortSettingsForTransmitProtocol.ResponseTimeoutMs}ms)");
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (_receiveThread.ThreadState == ThreadState.Aborted
|
|
|| _receiveThread.ThreadState == ThreadState.AbortRequested)
|
|
{
|
|
_byteDataLogger.Warn(ex, $"{ Ident} ThreadState is Aborted or AbortRequested but an " +
|
|
"error occurred while reading records from serial port");
|
|
}
|
|
else
|
|
{
|
|
_byteDataLogger.Error(ex, $"{ Ident} Error while reading records from serial port");
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (_serialPort.IsOpen && !_receiveToken.IsCancellationRequested)
|
|
{
|
|
//it makes no sense to put the thread to sleep if there is something to read
|
|
if (0 == _serialPort.BytesToRead)
|
|
{
|
|
//restore event delegate to activate handle for incoming records
|
|
_serialPort.DataReceived += PhysicalDataReceived;
|
|
|
|
//put this thread (receive thread) to JainWaitSleep
|
|
_onSyncReceiveThread.WaitOne();
|
|
}
|
|
}
|
|
}
|
|
}//while (!_tokenReadData.IsCancellationRequested)
|
|
}// over hole reading thread loop
|
|
catch (ThreadAbortException)
|
|
{ }
|
|
catch (Exception ex)
|
|
{
|
|
_byteDataLogger.Error($"{Ident} {ex.Message}");
|
|
}
|
|
finally
|
|
{
|
|
if (_serialPort.IsOpen)
|
|
{
|
|
_serialPort.Close();
|
|
_serialPort.Dispose();
|
|
_byteDataLogger.Info($"{Ident} Port is closed");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Boolean IsOpen()
|
|
{
|
|
return _serialPort != null && _serialPort.IsOpen;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serial port specific write routine
|
|
/// </summary>
|
|
/// <param name="txBuffer"></param>
|
|
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
|
|
/// - Hide data in logging for e.g. passwords
|
|
/// </remarks>
|
|
protected virtual void SpecificPortWrite(Byte[] txBuffer)
|
|
{
|
|
PhysicalWrite(txBuffer);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Physically sending to the serial port
|
|
/// </summary>
|
|
/// <remarks date="2017" author="R.Drabesch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
/// <remarks date="2018-Oct-23" author="T.Wiedebusch">
|
|
/// - Doubling of sync byte in protocol behind sync byte itself implemented
|
|
/// </remarks>
|
|
/// <remarks date="2022-Feb-25" author="T.Wiedebusch, R.Drabesch">
|
|
/// - Flush buffer as MOXA sometimes takes two messages and combines these to one!
|
|
/// As the Genesis needs a separated wake-up message with a following delay before the
|
|
/// real payload message, this causes a lot of trouble.
|
|
/// </remarks>
|
|
protected void PhysicalWrite(Byte[] txBuffer)
|
|
{
|
|
try
|
|
{
|
|
//don't double the sync byte itself
|
|
var txByteList = new List<Byte> { txBuffer[0] };
|
|
for (var byteCtr = 1; byteCtr < txBuffer.Length; byteCtr++)
|
|
{
|
|
txByteList.Add(txBuffer[byteCtr]);
|
|
|
|
//write the doubled sync byte again if required by transmit protocol settings
|
|
if (PortSettingsForTransmitProtocol.DoubleSyncByte)
|
|
{
|
|
if (txBuffer[byteCtr].Equals(PortSettingsForTransmitProtocol.ProtSyncByte))
|
|
{
|
|
txByteList.Add(txBuffer[byteCtr]);
|
|
}
|
|
}
|
|
}
|
|
|
|
_serialPort.Write(txByteList.ToArray(), 0, txByteList.Count);
|
|
|
|
//TODO THW flush before sending NOT after
|
|
// flush the buffer for MOXA, to avoid two subsequent communications assembled to one communication!
|
|
_serialPort.BaseStream.Flush();
|
|
|
|
OnRawRecordSendOut?.Invoke(this, new ListBytePortDataEventArgs(txByteList, DateTimeOffset.UtcNow));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_byteDataLogger.Error($"{Ident} {ex.Message}");
|
|
}
|
|
}
|
|
|
|
#region Prop
|
|
#endregion
|
|
}
|
|
}
|