Initial commit

This commit is contained in:
Rowlander
2021-10-01 11:09:20 +02:00
commit fe54fceb1e
1301 changed files with 853973 additions and 0 deletions
@@ -0,0 +1,46 @@
using System;
using Xylem.Common.CommonCore.Consts;
namespace Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments
{
/// <inheritdoc cref="EventArgs" />
public abstract class BasePortDataEventArgs : EventArgs, IPortDataEventArgs
{
/// <summary>
/// Marker for incoming record at time of the PC
/// </summary>
private DateTimeOffset _receivedTime = default(DateTimeOffset);
/// <inheritdoc />
public abstract Object GetData();
/// <inheritdoc />
public abstract void SetData(Object data);
/// <inheritdoc />
public void SetReceivedTime(DateTimeOffset receivedTime)
{
_receivedTime = receivedTime;
}
/// <inheritdoc />
public DateTimeOffset GetReceivedTime()
{
return _receivedTime;
}
private SyncMarkRecord _syncMarkRecord;
/// <inheritdoc />
public void SetSyncMarkRecord(SyncMarkRecord syncMarkRecord)
{
_syncMarkRecord = syncMarkRecord;
}
/// <inheritdoc />
public SyncMarkRecord GetSyncMarkRecord()
{
return _syncMarkRecord;
}
}
}
@@ -0,0 +1,48 @@
using System;
using Xylem.Common.CommonCore.Consts;
namespace Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments
{
/// <summary>
/// Interface for port data event arguments
/// </summary>
public interface IPortDataEventArgs
{
/// <summary>
/// Read data
/// </summary>
/// <returns></returns>
Object GetData();
/// <summary>
/// Write data
/// </summary>
/// <param name="data"></param>
void SetData(Object data);
/// <summary>
/// Reading received time
/// </summary>
/// <returns></returns>
DateTimeOffset GetReceivedTime();
/// <summary>
/// Writing received time
/// </summary>
/// <param name="receivedTime"></param>
void SetReceivedTime(DateTimeOffset receivedTime);
/// <summary>
/// Setting the data marker
/// </summary>
/// <param name="syncMarkRecord"></param>
void SetSyncMarkRecord(SyncMarkRecord syncMarkRecord);
/// <summary>
/// Getting the data marker
/// </summary>
/// <returns></returns>
SyncMarkRecord GetSyncMarkRecord();
}
}
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using Xylem.Common.CommonCore.Consts;
namespace Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments
{
/// <inheritdoc />
public class ListBytePortDataEventArgs : BasePortDataEventArgs
{
private List<Byte> _data;
/// <inheritdoc />
public ListBytePortDataEventArgs(List<Byte> data, DateTimeOffset readTimeStampPc = default(DateTimeOffset),
SyncMarkRecord syncMarkRecord = SyncMarkRecord.SkipDecoding)
{
_data = data;
SetSyncMarkRecord(syncMarkRecord);
SetReceivedTime(readTimeStampPc);
}
/// <inheritdoc />
public override Object GetData()
{
return _data;
}
/// <inheritdoc />
public override void SetData(Object data)
{
_data = (List<Byte>)data;
}
}
}
@@ -0,0 +1,34 @@
using System;
using Xylem.Common.CommonCore.Consts;
namespace Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments
{
/// <inheritdoc />
public class StringPortDataEventArgs : BasePortDataEventArgs
{
private String _data;
/// <inheritdoc />
public StringPortDataEventArgs(String data, DateTimeOffset readTimeStampPc = default(DateTimeOffset),
SyncMarkRecord syncMarkRecord = SyncMarkRecord.SkipDecoding)
{
_data = data;
SetSyncMarkRecord(syncMarkRecord);
SetReceivedTime(readTimeStampPc);
}
/// <inheritdoc />
public override Object GetData()
{
return _data;
}
/// <inheritdoc />
public override void SetData(Object data)
{
_data = (String)data;
}
}
}
@@ -0,0 +1,63 @@
using System;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments;
namespace Xylem.Common.Hardware.Interfaces.Ports.PortCore
{
/// <summary>
/// Interface for Ports
/// </summary>
public interface IPort
{
/// <summary>
/// event for record received, either bytes or string
/// </summary>
event EventHandler<BasePortDataEventArgs> OnRawRecordReceived;
/// <summary>
/// event for record received, either bytes or string
/// </summary>
event EventHandler<BasePortDataEventArgs> OnRawRecordSendOut;
/// <summary>
/// set specific mark
/// and flush or delete all incoming byte from buffer when SyncMarkRecord is <see cref="SyncMarkRecord.SyncStart"/> or
/// <see cref="SyncMarkRecord.SyncEnd"/>
/// </summary>
/// <param name="syncMarkRecord"></param>
void SynchronizeReceiveBuffer(SyncMarkRecord syncMarkRecord);
/// <summary>
/// if the port needs stuff to open, always open for better logical handling
/// </summary>
void Open();
/// <summary>
/// close and dispose all connections
/// </summary>
void Dispose();
/// <summary>
/// indicates if the Port is open (also on Ports that did not have an open state)
/// </summary>
/// <returns></returns>
Boolean IsOpen();
/// <summary>
/// Write byte[] to the Stream/Port on Child Class
/// wrapped with base class error handling
/// </summary>
/// <param name="data">Array of bytes to write</param>
void PortWrite(Byte[] data);
/// <summary>
/// discard all buffers
/// </summary>
void Clear();
/// <summary>
/// Return the port name
/// </summary>
/// <returns>name of the port as string</returns>
String GetPortName();
}
}
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{2E139F63-B6FE-4EA9-A098-85364FE9B26C}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Hardware.Interfaces.Ports.PortCore</RootNamespace>
<AssemblyName>Xylem.Common.Hardware.Interfaces.Ports.PortCore</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>false</Deterministic>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>7.0</LangVersion>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.Interfaces.Ports.PortCore.xml</DocumentationFile>
<PlatformTarget>AnyCPU</PlatformTarget>
<RunCodeAnalysis>true</RunCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>CODE_ANALYSIS;DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.Interfaces.Ports.PortCore.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<RunCodeAnalysis>true</RunCodeAnalysis>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\..\..\.shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="EventArguments\BasePortDataEventArgs.cs" />
<Compile Include="EventArguments\IPortDataEventArgs.cs" />
<Compile Include="EventArguments\ListBytePortDataEventArgs.cs" />
<Compile Include="EventArguments\StringPortDataEventArgs.cs" />
<Compile Include="IPort.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TransmitPortSettings.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\CommonCore\CommonCore.csproj">
<Project>{1B02C79E-0B19-43E2-8F6B-71EF0C786C97}</Project>
<Name>CommonCore</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
@@ -0,0 +1,7 @@
using System.Runtime.InteropServices;
using System.Reflection;
[assembly: AssemblyTitle("Xylem.Common.Hardware.Interfaces.Ports.PortCore")]
[assembly: Guid("204B3ABA-E6A0-41EC-BC91-6B27344FD3CE")]
@@ -0,0 +1,81 @@
using System;
namespace Xylem.Common.Hardware.Interfaces.Ports.PortCore
{
/// <summary>
/// Store Port settings set most likely from transmit protocol
/// </summary>
public struct TransmitPortSettings
{
/// <summary>
/// The transmission protocol needs to inform the communication port how to setup,
/// this is the data container.
/// </summary>
/// <param name="protSyncByte"></param>
/// <param name="protLengthIndex"></param>
/// <param name="protAddLength"></param>
/// <param name="responseTimeoutMs"></param>
/// <param name="baudRate"></param>
/// <param name="receiveBufferFlushThreshold"></param>
/// <param name="doubleSyncByte"></param>
public TransmitPortSettings(Byte? protSyncByte, UInt16? protLengthIndex, UInt16 protAddLength, Int32 responseTimeoutMs, UInt32 baudRate,
UInt32? receiveBufferFlushThreshold, Boolean doubleSyncByte = false)
{
ProtSyncByte = protSyncByte;
ProtLengthIndex = protLengthIndex;
ProtAddLength = protAddLength;
ResponseTimeoutMs = responseTimeoutMs;
BaudRate = baudRate;
ReceiveBufferFlushThreshold = receiveBufferFlushThreshold;
DoubleSyncByte = doubleSyncByte;
}
/// <summary>
/// start of receiving synchronization byte at byte receive routine
/// syncByte == null: use the readLine routine (ASCII) and NOT the BYTE routine,
/// lengthPosition and additionalLength are not used
/// </summary>
public readonly Byte? ProtSyncByte;
/// <summary>
/// position of length information field in received BYTE record
/// lengthIndex == null: take the constant receive length of additionalLength
/// because the record doesn't contain length information
/// </summary>
public readonly UInt16? ProtLengthIndex;
/// <summary>
/// additional record length NOT covert by the record length information field
/// lengthIndex == null: constant length for received record
/// Being used for the BYTE records indicated by a valid syncByte,
/// not being used for ASCII records.
/// </summary>
public readonly UInt16 ProtAddLength;
/// <summary>
/// Response time out in milliseconds
/// </summary>
public readonly Int32 ResponseTimeoutMs;
/// <summary>
/// BaudRate for Port
/// </summary>
public readonly UInt32 BaudRate;
/// <summary>
/// lower threshold for buffer flushing if dataMarker != SkipDecoding
/// receiveBufferFlushThreshold == null: never flush the communication buffer
/// receiveBufferFlushThreshold == 0: flush always the communication buffer
/// receiveBufferFlushThreshold == x: flush communication buffer if it exceeds x Byte
/// waste old records if an up-to-date record is being needed for start/stop synchronization
/// of a measurement. This is the level at which the old data have to be flushed because the
/// data have been dammed up in the communication port input buffer which means they are to
/// old for synchronization purposes.
/// /// </summary>
public readonly UInt32? ReceiveBufferFlushThreshold;
/// <summary>
/// a special implementation may require the doubling of the sync byte to re-synchronize
/// </summary>
public Boolean DoubleSyncByte;
}
}
@@ -0,0 +1,664 @@
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;
private 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>
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>
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);
OnRawRecordSendOut?.Invoke(this, new ListBytePortDataEventArgs(txByteList, DateTimeOffset.UtcNow));
}
catch (Exception ex)
{
_byteDataLogger.Error($"{Ident} {ex.Message}");
}
}
#region Prop
#endregion
}
}
@@ -0,0 +1,39 @@
using System;
using System.Threading;
using System.IO.Ports;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
namespace Xylem.Common.Hardware.Interfaces.Ports.SerialPorts
{
/// <inheritdoc />
public class IrdaSerialPort : BaseSerialPort
{
/// <inheritdoc />
public IrdaSerialPort(String ident, SerialPort serialPort, TransmitPortSettings setting)
: base(ident, serialPort, setting)
{
}
/// <inheritdoc />
/// <remarks date="2018-Mar-15" author="T.Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2019-Aug-07" author="T.Wiedebusch">
/// - wakeup burst pattern changed from 2 times 0xF0 to 4 times 0x01,
/// - required delay from 500µs to 1ms between wakeup burst and data.
/// </remarks>
protected override void SpecificPortWrite(Byte[] tx)
{
//send wake up burst with two 0xF0 byte
//var wakeUpBurst = new Byte[] { 0xF0, 0xF0 };
var wakeUpBurst = new Byte[] { 0x01, 0x01, 0x01, 0x01 };
PhysicalWrite(wakeUpBurst);
//delay at least 1ms
Thread.Sleep(1);
//send the IrDA record
PhysicalWrite(tx);
}
}
}
@@ -0,0 +1,21 @@
using System;
using System.IO.Ports;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
namespace Xylem.Common.Hardware.Interfaces.Ports.SerialPorts
{
/// <inheritdoc />
public class LedSerialPort : BaseSerialPort
{
/// <inheritdoc />
public LedSerialPort(String ident, SerialPort serialPort, TransmitPortSettings setting) : base(ident, serialPort, setting)
{
}
/// <inheritdoc />
protected override void SpecificPortWrite(Byte[] tx)
{
throw new NotSupportedException("Led port does not support writing");
}
}
}
@@ -0,0 +1,7 @@
using System.Runtime.InteropServices;
using System.Reflection;
[assembly: AssemblyTitle("Xylem.Common.Hardware.Interfaces.Ports.SerialPorts")]
[assembly: Guid("55B0EC0E-21AB-442E-A1FA-26CFB78083CF")]
@@ -0,0 +1,602 @@
using System;
using System.IO.Ports;
using System.Linq;
using System.Threading;
using Xylem.Common.Utils.Crc16Ccitt;
using Xylem.Common.Utils.Logging;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments;
using NLog;
namespace Xylem.Common.Hardware.Interfaces.Ports.SerialPorts
{
/// <inheritdoc />
public class RfidSerialPort : BaseSerialPort
{
/// <inheritdoc />
public override event EventHandler<BasePortDataEventArgs> OnRawRecordReceived;
private const Byte RfidFrameStartId = 0x01;
private const Byte RfidFrameTxLength = 0x12;
private const Byte RfidFrameRxLength = 0x0C;
private const Byte RfidFrameExpectedRxLength = 0x0A;
private const Byte RfidFramePayloadDataMarker = 0x7D;
private const Byte RfidRxModeMarker = 0x7E;
private const Byte RfidFramePollingByte = 0x03;
private const Int32 RfidRxStartSyncPosition = 0;
private const Int32 RfidRxLengthPosition = 1;
private const Int32 RfidRxModePosition = 4;
private const Int32 RfidRxDataPosition = 5;
private const Int32 RfidRxDataMarkerPosition = 11;
private const Int32 RfidRxCrcLowPosition = 12;
private const Int32 RfidRxCrcHighPosition = 13;
private const Int32 RfidRxBccPosition = 14;
private const Int32 RfidCommRetries = 4;
private const Int32 RfidStartPatternLength = 10;
//additional frame length for start, length and BCC added to frame length
//being in the length itself
private const Int32 RfidProtAddLength = 3;
private const Int32 RfidRxLength = RfidFrameRxLength + RfidProtAddLength;
private const Int32 RfidTxLength = RfidFrameTxLength + RfidProtAddLength;
//first byte is fixed to 0x7D (data mode) at the Tx or the last byte behind
//the payload at Rx, the others are from the device protocol (the payload),
//this byte needs to be included into the CRC calculation
private const Int32 RfidPayLoadLength = 7;
private const Int32 RfidRawPayLoadLength = 6;
//the raw payload to add to RFID transmit buffer, it has a start Id, a length position
//and some additional length needed to add to the raw Rx length
private Byte[] _rawTxPayLoad;
private Int32 _rawRxLength;
private Byte[] _rawRxPayLoad;
//create RFID payload
private Byte[] _rfidTxPayLoad;
private Int32 _payLoadTxCounterPosition;
private Int32 _payLoadRxCounterPosition;
//remind send state
private Boolean _txIsActive;
//marker for first protocol containing the start id and the length
private Boolean _waitRawStartSyncPattern;
//this is the RFID raw buffer being sent to the serial port
private Byte[] _rfidTxBuffer = new Byte[RfidTxLength];
private Byte[] _rfidRxBuffer = new Byte[RfidRxLength];
private Int32 _rfidCommRetryCounter = RfidCommRetries;
private RfidRxState _rfidRxState;
private readonly ILogger _logger;
//communication counter for debug for application layer
private Int32 _commCounter;
/// <inheritdoc />
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
public RfidSerialPort(String ident, SerialPort serialPort, TransmitPortSettings setting)
: base(ident, serialPort, setting)
{
_logger = NLogHelper.CreateOrGetMultiLogger(ident, "", "RfidSerialPort", "COMBase", "BaseSerialPort");
}
/// <inheritdoc />
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
/// - Not needed and therefor deactivated by overwriting
/// </remarks>
public override void PhysicalDataReceived(Object sender, SerialDataReceivedEventArgs e)
{
//remove registration of delegate
GetBaseComport().DataReceived -= PhysicalDataReceived;
}
/// <inheritdoc />
/// <summary>
/// Overwritten routine, kicks off the first communication,
/// handles the communication state machine
/// </summary>
/// <param name="tx"></param>
/// <remarks date="2017-Dec-15" author="R.Drabesch">
/// - Not needed and therefor deactivated by overwriting
/// </remarks>
/// <remarks date="2017-Dec-17" author="T.Wiedebusch">
/// - communication scheduler removed, call directly the stats machine
/// </remarks>
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
/// - Hide data in logging for e.g. passwords
/// </remarks>
protected override void SpecificPortWrite(Byte[] tx)
{
//raw protocol before wrapped into RFID
SendDataViaRfid(tx);
RfidRxState rfidState;
//call state machine until ready
do
{
rfidState = RfidCommStateMachine();
} while (RfidRxState.CommunicationFinished != rfidState &&
RfidRxState.CommunicationFailed != rfidState);
}
/// <summary>
/// Send data via RFID
/// </summary>
/// <param name="data">data to be transferred via RFID</param>
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2017-Dec-15" author="T.Wiedebusch">
/// - Receive size removed, will be automatically extracted from underlay-protocol (raw)
/// </remarks>
/// <remarks date="2017-Dec-17" author="T.Wiedebusch">
/// - Raw buffer increased to communication retries * raw packet size.
/// </remarks>
public void SendDataViaRfid(Byte[] data)
{
if (data.Length < RfidRawPayLoadLength) return;
//length is unknown before receiving the first raw data, setup to x packets size for
//receive routine
_rawRxLength = RfidRawPayLoadLength * _rfidCommRetryCounter;
_rawRxPayLoad = new Byte[_rawRxLength];
_waitRawStartSyncPattern = true;
//start with sending of data
_txIsActive = true;
//allow retries during communication
_rfidCommRetryCounter = RfidCommRetries;
//reset running counters
_payLoadTxCounterPosition = 0;
_payLoadRxCounterPosition = 0;
_commCounter = 0;
//copy part of the huge payload buffer (containing the entire data) to small
//6 byte chunks being able to transmit within one RFID communication
_rfidTxPayLoad = null;
_rfidTxPayLoad = new Byte[RfidRawPayLoadLength];
_rawTxPayLoad = new Byte[data.Length];
_rawTxPayLoad = data;
for (var i = 0; i < RfidRawPayLoadLength; i++)
{
_rfidTxPayLoad[i] = _rawTxPayLoad[_payLoadTxCounterPosition];
_payLoadTxCounterPosition++;
}
//assemble the RFID transmit buffer
PrepareRfidTxBuffer(ref _rfidTxBuffer, _rfidTxPayLoad);
//kick off first communication
_rfidRxState = RfidComm(ref _rfidRxBuffer, _rfidTxBuffer);
}
/// <summary>
/// RFID Tx buffer content
/// </summary>
/// <returns>array of RFID Tx buffer content</returns>
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
public Byte[] GetRfidTxBuffer()
{
return _rfidTxBuffer;
}
/// <summary>
/// RFID Rx buffer content
/// </summary>
/// <returns>array of RFID Rx buffer content</returns>
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
public Byte[] GetRfidRxBuffer()
{
return _rfidRxBuffer;
}
/// <summary>
/// read out the received data from RFID
/// </summary>
/// <returns></returns>
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
public Byte[] GetDecodedDataFromRfid()
{
return _rawRxPayLoad;
}
/// <summary>
/// feedback of communication counter
/// </summary>
/// <returns>number of RFID communications</returns>
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
public Int32 GetRfidComCounter()
{
return _commCounter;
}
/// <summary>
/// communication state machine
/// </summary>
/// <returns></returns>
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2017-Dec-15" author="T.Wiedebusch">
/// - Extract length information from raw protocol
/// </remarks>
/// <remarks date="2017-Dec-17" author="T.Wiedebusch">
/// - Return of communication failed on missing start sync pattern of raw protocol
/// </remarks>
/// <remarks date="2017-Dec-19" author="T.Wiedebusch">
/// - New modes implemented to differ between sending / waiting for Rx start
/// and receiving
/// </remarks>
/// <remarks date="2018-Feb-21" author="T.Wiedebusch">
/// - Time stamp added
/// </remarks>
/// <remarks date="2019-Apr-12" author="T.Wiedebusch">
/// - Hide data in logging for e.g. passwords
/// </remarks>
public RfidRxState RfidCommStateMachine()
{
//check the receive status
switch (_rfidRxState)
{
case RfidRxState.EchoRxProtocolOk:
//clear payload buffer to force filling with polling pattern
_rfidTxPayLoad = null;
//send loop
if (_txIsActive)
{
if (_payLoadTxCounterPosition < _rawTxPayLoad.Length)
{
//assign new Tx buffer
Int32 arraySize;
if (_payLoadTxCounterPosition + RfidRawPayLoadLength <
_rawTxPayLoad.Length)
arraySize = RfidRawPayLoadLength;
else
arraySize = _rawTxPayLoad.Length - _payLoadTxCounterPosition;
_rfidTxPayLoad = new Byte[arraySize];
//fill payload buffer with remaining payload bytes
for (var i = 0; i < arraySize; i++)
{
if (_payLoadTxCounterPosition >= _rawTxPayLoad.Length) continue;
_rfidTxPayLoad[i] = _rawTxPayLoad[_payLoadTxCounterPosition];
_payLoadTxCounterPosition++;
}
}
else
{
//switch receive loop active
_txIsActive = false;
}
}
//reset retry counter
_rfidCommRetryCounter = RfidCommRetries;
//assemble new RFID transmit buffer
PrepareRfidTxBuffer(ref _rfidTxBuffer, _rfidTxPayLoad);
//start communication
_rfidRxState = RfidComm(ref _rfidRxBuffer, _rfidTxBuffer);
break;
case RfidRxState.DataRxProtocolOk:
//receive loop with polling pattern sending
for (var i = 0; i < RfidRawPayLoadLength; i++)
{
//avoid out of bounce access
if (_payLoadRxCounterPosition >= _rawRxLength) continue;
_rawRxPayLoad[_payLoadRxCounterPosition] =
_rfidRxBuffer[i + RfidRxDataPosition];
_payLoadRxCounterPosition++;
}
//check for entire message received
if (_payLoadRxCounterPosition >= _rawRxLength)
{
//assign time stamp of received record
var readDateTimePc = DateTimeOffset.UtcNow;
//communication finished, return data to caller
//RawRecordReceived(this, _rawRxPayLoad, readDateTimePc);
var byteList = _rawRxPayLoad.ToList();
OnRawRecordReceived?.Invoke(this, new ListBytePortDataEventArgs(byteList, readDateTimePc));
//log the RFID payload, RFID protocol is removed
//_logger.Trace($"{Ident} Read({BitConverter.ToString(_rawRxPayLoad)})");
return _waitRawStartSyncPattern ? RfidRxState.CommunicationFailed :
RfidRxState.CommunicationFinished;
}
//reset retry counter
_rfidCommRetryCounter = RfidCommRetries;
//start communication with polling pattern, has been assembled in last send run
_rfidRxState = RfidComm(ref _rfidRxBuffer, _rfidTxBuffer);
break;
case RfidRxState.Idle:
break;
default:
//RFID or device is not ready or protocol error, start retry
if (_rfidCommRetryCounter > 0)
{
_logger.Trace($"{Ident} Internal RFID retry");
_rfidCommRetryCounter--;
//send the same RFID message again
_rfidRxState = RfidComm(ref _rfidRxBuffer, _rfidTxBuffer);
}
else
{
//communication failed because of exceeded internal RFID retry counter
_rfidRxState = RfidRxState.CommunicationFailed;
_logger.Trace($"{Ident} Internal RFID communication failed, RFID retry counter exceeded");
}
break;
}
return _rfidRxState;
}
/// <summary>
/// send and receive
/// </summary>
/// <param name="rxBuffer"></param>
/// <param name="txBuffer"></param>
/// <returns>RfidRxState</returns>
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2017-Dec-18" author="T.Wiedebusch">
/// - Communication timeout activated.
/// </remarks>
/// <remarks date="2019-Apr-12" author="T.Wiedebusch">
/// - Hide data in logging for e.g. passwords
/// </remarks>
private RfidRxState RfidComm(ref Byte[] rxBuffer, Byte[] txBuffer)
{
var rfidRxState = RfidRxState.CommPortError;
if (!IsOpen()) return rfidRxState;
Clear();
//_logger.Trace($"{Ident} RFID raw sent({BitConverter.ToString(txBuffer)})");
PhysicalWrite(txBuffer);
_commCounter++;
//set timeout for receive exit, take 5 records of 6 byte chunks as base
var commTimeoutMs = PortSettingsForTransmitProtocol.ResponseTimeoutMs / 5;
while (commTimeoutMs > 0 && BytesToRead() < RfidRxLength)
{
Thread.Sleep(1);
commTimeoutMs--;
}
var i = 0;
//test if something is in input buffer
if (RfidRxLength > BytesToRead())
{
_logger.Trace($"{Ident} Internal RFID read timeout({PortSettingsForTransmitProtocol.ResponseTimeoutMs}ms)");
return RfidRxState.RxTimeout;
}
while (BytesToRead() > 0 && i < RfidRxLength)
{
rxBuffer[i] = (Byte)ReadByte();
i++;
}
if (i != RfidRxLength) return rfidRxState;
//all expected bytes received
//_logger.Trace($"{Ident} RFID raw read({BitConverter.ToString(rxBuffer)})");
rfidRxState = CheckRfidRxBuffer(rxBuffer);
return rfidRxState;
}
/// <summary>
/// check the received buffer CRC, BCC and
/// </summary>
/// <param name="rfidRxBuffer"></param>
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2017-Dec-19" author="T.Wiedebusch">
/// - New modes implemented to differ between sending / waiting for Rx start
/// and receiving
/// </remarks>
/// <returns>RfidRxState</returns>
private RfidRxState CheckRfidRxBuffer(Byte[] rfidRxBuffer)
{
//retry required by start sync error
if (RfidFrameStartId != rfidRxBuffer[RfidRxStartSyncPosition])
return RfidRxState.StartSyncError;
//retry required by length error
if (RfidFrameRxLength != rfidRxBuffer[RfidRxLengthPosition])
return RfidRxState.LengthError;
//data marked as useful data
if (RfidRxModeMarker != rfidRxBuffer[RfidRxModePosition] ||
RfidFramePayloadDataMarker != rfidRxBuffer[RfidRxDataMarkerPosition])
return RfidRxState.RetryRequired;
//BCC check transfer entire receive buffer, this will automatically handled
if (rfidRxBuffer[RfidRxBccPosition] != BuildRfidBcc(rfidRxBuffer))
return RfidRxState.BccError;
//extract the 6 data (payload) bytes and the data frame marker
var testBuffer = new Byte[RfidPayLoadLength];
for (var i = 0; i < RfidPayLoadLength; i++)
testBuffer[i] = rfidRxBuffer[i + RfidRxDataPosition];
//CRC with LSB first
var buildCrc = Crc16Ccitt.CalculateLsb0408(testBuffer);
UInt16 receivedCrc = rfidRxBuffer[RfidRxCrcHighPosition];
receivedCrc <<= 8;
receivedCrc &= 0xFF00;
receivedCrc += rfidRxBuffer[RfidRxCrcLowPosition];
if (receivedCrc != buildCrc) return RfidRxState.DataCrcError;
//send loop is active
if (_txIsActive) return RfidRxState.EchoRxProtocolOk;
//if the start sync byte has been detected the normal receive mode is active
if (!_waitRawStartSyncPattern) return RfidRxState.DataRxProtocolOk;
//search for raw protocol start sync Id to extract length information,
//if start Id hasn't been found the wait for Rx start mode is active
//forcing the retry loop being active (default switch)
if (PortSettingsForTransmitProtocol.ProtSyncByte != _rfidRxBuffer[RfidRxDataPosition])
return RfidRxState.WaitRxStartProtocolOk;
//here the Rx mode is going to be activated, first data received including
//start sync Id and length information
_waitRawStartSyncPattern = false;
_rawRxLength = _rfidRxBuffer[RfidRxDataPosition +
(PortSettingsForTransmitProtocol.ProtLengthIndex.HasValue ?
PortSettingsForTransmitProtocol.ProtLengthIndex.Value : 0)] +
PortSettingsForTransmitProtocol.ProtAddLength;
_rawRxPayLoad = new Byte[_rawRxLength];
return RfidRxState.DataRxProtocolOk;
}
/// <summary>
/// assemble the RFID transmit buffer as bytes
/// </summary>
/// <param name="rfidTxBuffer"></param>
/// <param name="payLoad"></param>
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
private static void PrepareRfidTxBuffer(ref Byte[] rfidTxBuffer, Byte[] payLoad)
{
RfidTxProtocol rfidTxStruct;
rfidTxStruct.StartPattern = new Byte[]
{
RfidFrameStartId, //start identifier 0x01
RfidFrameTxLength, //length, start behind length without BCC
0xE8, 0x90, 0x00, //CMD 1, 2, 3
0x00, 0x32, 0x00, 0x11, //Power 1 and 2
0x48 //TX bits
};
//the default data is the polling pattern 0x03
rfidTxStruct.PayLoad = new[] { RfidFramePayloadDataMarker,
RfidFramePollingByte, RfidFramePollingByte, RfidFramePollingByte,
RfidFramePollingByte, RfidFramePollingByte, RfidFramePollingByte};
//fill the payload with real data
var i = 0;
if (payLoad != null)
{
//put payload behind RFID frame payload data marker
for (; i < payLoad.Length; i++)
rfidTxStruct.PayLoad[i + 1] = payLoad[i];
}
rfidTxStruct.ExpectedRxBytes = RfidFrameExpectedRxLength;
//fill transmit buffer with constant start pattern for RFID communication
i = 0;
for (; i < RfidStartPatternLength; i++)
{
rfidTxBuffer[i] = rfidTxStruct.StartPattern[i];
}
//add payload to buffer, first byte is constant 0x7D (data mode)
var c = 0;
for (; i < RfidStartPatternLength + RfidPayLoadLength; i++)
{
rfidTxBuffer[i] = rfidTxStruct.PayLoad[c];
c++;
}
//add data CRC, LSB first
rfidTxStruct.DataCrc = Crc16Ccitt.CalculateLsb0408(rfidTxStruct.PayLoad);
rfidTxBuffer[i] = (Byte)(rfidTxStruct.DataCrc & 0xFF);
i++;
rfidTxBuffer[i] = (Byte)((rfidTxStruct.DataCrc & 0xFF00) >> 8);
//add expected receive bytes
i++;
rfidTxBuffer[i] = rfidTxStruct.ExpectedRxBytes;
//add BCC
rfidTxStruct.Bcc = BuildRfidBcc(rfidTxBuffer);
i++;
rfidTxBuffer[i] = rfidTxStruct.Bcc;
}
/// <summary>
/// build simple byte by byte XOR'ed checksum called BCC for RFID
/// </summary>
/// <param name="rfidRawBuffer"></param>
/// <returns>BCC code</returns>
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
private static Byte BuildRfidBcc(Byte[] rfidRawBuffer)
{
//remove the "start byte" and the BCC itself
var maxCounts = rfidRawBuffer.Length - 1;
//start behind the "start byte"
var i = 1;
Byte bcc = rfidRawBuffer[i];
//take the second value
i++;
for (; i < maxCounts; i++)
{
bcc ^= rfidRawBuffer[i];
}
return bcc;
}
private struct RfidTxProtocol
{
public Byte[] StartPattern;
public Byte[] PayLoad;
public UInt16 DataCrc;
public Byte ExpectedRxBytes;
public Byte Bcc;
}
}
public enum RfidRxState
{
/// <summary>
/// No communication is ongoing
/// </summary>
Idle,
/// <summary>
/// Echo of sent protocol is received successfully back
/// </summary>
EchoRxProtocolOk,
/// <summary>
/// Waiting for switch from transmitting to receiving
/// </summary>
WaitRxStartProtocolOk,
/// <summary>
/// Received data message is valid
/// </summary>
DataRxProtocolOk,
/// <summary>
/// Communication port assignment error
/// </summary>
CommPortError,
/// <summary>
/// Start of synchronization error
/// </summary>
StartSyncError,
/// <summary>
/// Length error
/// </summary>
LengthError,
/// <summary>
/// Retry required
/// </summary>
RetryRequired,
/// <summary>
/// CRC error of data in payload field
/// </summary>
DataCrcError,
/// <summary>
/// BCC (special checksum) error of RFID
/// </summary>
BccError,
/// <summary>
/// Receive timeout
/// </summary>
RxTimeout,
/// <summary>
/// Communication to RFID failed, record cannot be assembled
/// </summary>
CommunicationFailed,
/// <summary>
/// Communication to RFID successfully executed
/// </summary>
CommunicationFinished
}
}
@@ -0,0 +1,257 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{72037E89-4F63-4B3F-BEFF-4138E578CE99}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Hardware.Interfaces.Ports.SerialPorts</RootNamespace>
<AssemblyName>Xylem.Common.Hardware.Interfaces.Ports.SerialPorts</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<RegisterForComInterop>false</RegisterForComInterop>
<RunCodeAnalysis>true</RunCodeAnalysis>
<LangVersion>7.0</LangVersion>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.xml</DocumentationFile>
<LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\XP32bit\</OutputPath>
<DefineConstants>CODE_ANALYSIS;DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<RunCodeAnalysis>true</RunCodeAnalysis>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.Interface.Com.SerialComPort.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>CODE_ANALYSIS;DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<RunCodeAnalysis>true</RunCodeAnalysis>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\XP32bit\</OutputPath>
<DefineConstants>CODE_ANALYSIS;DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<RunCodeAnalysis>true</RunCodeAnalysis>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\PerformTest\</OutputPath>
<DefineConstants>CODE_ANALYSIS;DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<RunCodeAnalysis>true</RunCodeAnalysis>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.Interface.Com.SerialComPort.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\PerformTest\</OutputPath>
<DefineConstants>CODE_ANALYSIS;CODE_ANALYSIS;DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<RunCodeAnalysis>true</RunCodeAnalysis>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>CODE_ANALYSIS;TRACE;DEBUG</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<RunCodeAnalysis>true</RunCodeAnalysis>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<LangVersion>6</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>CODE_ANALYSIS;CODE_ANALYSIS;DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<RunCodeAnalysis>true</RunCodeAnalysis>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\..\..\.shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="BaseSerialPort.cs" />
<Compile Include="IrdaSerialPort.cs" />
<Compile Include="LedSerialPort.cs" />
<Compile Include="RfidSerialPort.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UartSerialPort.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\NLog.4.6.7\lib\net40-client\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Net" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\CommonCore\CommonCore.csproj">
<Project>{1B02C79E-0B19-43E2-8F6B-71EF0C786C97}</Project>
<Name>CommonCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\CommonCore\ThreadWatcher\ThreadWatcher.csproj">
<Project>{94ca71f3-c5d7-4ec6-ba47-fa8eea3c9406}</Project>
<Name>ThreadWatcher</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Utils\Crc16Ccitt\Crc16Ccitt.csproj">
<Project>{91def0c0-aa4f-40ae-8f18-cb717fde7e7f}</Project>
<Name>Crc16Ccitt</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Utils\Logging\Logging.csproj">
<Project>{4B88B0D3-E791-4774-9521-EABEACDC420E}</Project>
<Name>Logging</Name>
</ProjectReference>
<ProjectReference Include="..\PortCore\PortCore.csproj">
<Project>{2e139f63-b6fe-4ea9-a098-85364fe9b26c}</Project>
<Name>PortCore</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -0,0 +1,17 @@
using System;
using System.IO.Ports;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
namespace Xylem.Common.Hardware.Interfaces.Ports.SerialPorts
{
/// <inheritdoc />
public class UartSerialPort : BaseSerialPort
{
/// <inheritdoc />
public UartSerialPort(String ident, SerialPort serialPort, TransmitPortSettings setting) : base(ident, serialPort, setting)
{
}
}
}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="4.6.7" targetFramework="net40" />
</packages>