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>
@@ -0,0 +1,29 @@
using NLog;
using System;
using System.Collections.Generic;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
using Xylem.Common.Utils.Logging;
namespace Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol
{
public abstract class BaseTransmitProtocol : ITransmitProtocol
{
public readonly ILogger Logger;
protected BaseTransmitProtocol(String port)
{
Logger = NLogHelper.CreateOrGetMultiLogger(port, "COM", "TransmitProtocol", "COMBase", "BaseTransmitProtocol");
}
public abstract TransmitPortSettings GetTransmitPortSettings();
public abstract void SetResponseTimeout(Int32 responseTimeoutMs);
public abstract void SetDefaultResponseTimeout();
public abstract List<Byte> DecodeDataForPhysicalLayer(Byte command, Byte[] payload);
public abstract List<Byte> DecodeDataForLogicLayer(List<Byte> rawData);
}
}
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
namespace Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol
{
public interface ITransmitProtocol
{
/// <summary>
/// Struct of settings being needed for transmit port
/// </summary>
/// <returns></returns>
TransmitPortSettings GetTransmitPortSettings();
/// <summary>
/// Overwrite the default response timeout
/// </summary>
void SetResponseTimeout(Int32 responseTimeoutMs);
/// <summary>
/// Set timeout to the default response time
/// </summary>
void SetDefaultResponseTimeout();
/// <summary>
/// Send out data (including transport protocol specific data like CRC) to physical port (like UART or IrDA)
/// </summary>
List<Byte> DecodeDataForPhysicalLayer(Byte command , Byte[] payload);
/// <summary>
/// Received data have to be checked and converted to request protocol
/// </summary>
List<Byte> DecodeDataForLogicLayer(List<Byte> rawData);
}
}
@@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
using Xylem.Common.Utils.Crc16Ccitt;
namespace Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol
{
/// <summary>
/// IrDA transmission protocol
/// </summary>
public class IrdaTransmitProtocol : BaseTransmitProtocol
{
// Format of IrDA transmit protocol:
// IrdaSyncByte|IrdaSend/ReceiveHeader|IrdaPayLoadLength|IrdaCommand/Message|
// IrdaPayLoad|IrdaCrc LSB|IrdaCrc MSB
private const Byte IrdaSyncByte = 0x9B;
// Encoding of IrdaSendHeader (LAT: listen after talk, LAT 10b: 500ms)
// Bits: 0| 0| 10| 0| 000
// standard frame|adapter to register|LAT|reserved|optical command
private const Byte IrdaSendHeader = 0x20;
// Encoding of IrdaReceiveHeader (LAT: listen after talk, LAT 10b: 500ms)
// Bits: 0| 1| 10| 0| 001
// standard frame|register to adapter|LAT|reserved|optical message
private const Byte IrdaReceiveHeader = 0x61;
// Mask out the LAT and the reserved bit for the IrdaReceiveHeader
private const Byte IrdaReceiveHeaderMask = 0xC7;
// data from adapter (software) to register (water meter) referred as optical command
private const Byte IrdaCommand = 0x02;
// data from register (water meter) to adapter (software) referred as optical message
private const Byte IrdaMessage = 0x03;
// IrdaSyncByte|IrdaReceiveHeader|IrdaPayLoadLength|IrdaMessage|CRC LSB|CRC MSB
private const Int32 IrdaProtocolFrameLength = 6;
// Indexes of IrDA optical protocol
private const Int32 IrdaSyncByteIndex = 0;
private const Int32 IrdaHeaderIndex = 1;
private const Int32 IrdaPayLoadLengthIndex = 2;
private const Int32 IrdaMessageIndex = 3;
private const Int32 IrdaPayLoadIndex = 4;
//the IrDA length covers the payload length only
private const UInt16 ProtAddLength = IrdaProtocolFrameLength;
//private const Int32 DefaultResponseTimeoutMs = 50;
private const Int32 DefaultResponseTimeoutMs = 150;
private Int32 _responseTimeoutMs = DefaultResponseTimeoutMs;
private const UInt32 BaudRate = 115200;
private readonly UInt32? _receiveBufferFlushThreshold = 1;
/// <inheritdoc />
public override void SetResponseTimeout(Int32 responseTimeoutMs)
{
_responseTimeoutMs = responseTimeoutMs;
}
/// <inheritdoc />
public override void SetDefaultResponseTimeout()
{
_responseTimeoutMs = DefaultResponseTimeoutMs;
}
/// <inheritdoc />
public IrdaTransmitProtocol(String portName) : base (portName)
{
}
/// <inheritdoc />
public override TransmitPortSettings GetTransmitPortSettings()
{
return new TransmitPortSettings(IrdaSyncByte, IrdaPayLoadLengthIndex, ProtAddLength,
_responseTimeoutMs, BaudRate, _receiveBufferFlushThreshold);
}
/// <inheritdoc />
/// <remarks date="2018-Mar-15" author="T.Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2020-Dec-19" author="T.Wiedebusch">
/// - Removed IrdaMessage identifier check as it was observed receiving a 0x03 or a 0x04 in this field.
/// </remarks>
public override List<Byte> DecodeDataForLogicLayer(List<Byte> irdaRecord)
{
Logger.Info($"DecodeDataForPhysicalLayer Cordonel->PC({BitConverter.ToString(irdaRecord.ToArray())})");
// check the record length and start of frame information
if (irdaRecord.Count < IrdaProtocolFrameLength + irdaRecord[IrdaPayLoadLengthIndex]
|| irdaRecord[IrdaSyncByteIndex] != IrdaSyncByte
|| (irdaRecord[IrdaHeaderIndex] & IrdaReceiveHeaderMask) != (IrdaReceiveHeader & IrdaReceiveHeaderMask)
|| irdaRecord[IrdaMessageIndex] != IrdaMessage )
{
var error = new ApplicationException("Reply format from IrDA is invalid. Decoding of transmit layer failed.");
Logger.Error(error.Message, error);
return new List<Byte>();
}
// extract the received CRC first the MSB at last position
UInt16 receivedCrc = irdaRecord[irdaRecord.Count - 1];
receivedCrc <<= 8;
// add the LSB from before last position, the LSB of the CRC will be sent first
receivedCrc += irdaRecord[irdaRecord.Count - 2];
// the CRC is being built from IrdaReceiveHeader to end of IrdaPayload,
// subtract irdaSyncByteLength and irdaCrcLength
var irdaCrcInputBuffer = new List<Byte>();
irdaCrcInputBuffer.AddRange(irdaRecord.GetRange(IrdaHeaderIndex, irdaRecord.Count - 3));
var calculatedCrc = Crc16Ccitt.CalculateReversedLsb8408(irdaCrcInputBuffer.ToArray());
if (receivedCrc != calculatedCrc)
{
var error = new ApplicationException("Reply CRC failure. Decoding of transmit layer failed.");
Logger.Error(error.Message, error);
return new List<Byte>();
}
// build the return value witch equals the IrDA payload
var irdaPayLoad = new List<Byte>();
irdaPayLoad.AddRange(irdaRecord.GetRange(IrdaPayLoadIndex, irdaRecord.Count - IrdaProtocolFrameLength));
return irdaPayLoad;
}
/// <inheritdoc />
/// <remarks date="2018-Mar-13" author="T.Wiedebusch">
/// - Initial
/// </remarks>
public override List<Byte> DecodeDataForPhysicalLayer(Byte requestProtocolCommand, Byte[] requestProtocolPayload)
{
var irdaCrcInputBuffer = new List<Byte>
{
// build CRC calculation buffer without sync byte
// IrdaSendHeader|IrdaPayLoadLength|IrdaCommand|requestProtocolCommand|requestProtocolPayload
IrdaSendHeader,
// IrdaPayloadLength is the requestProtocolCommandLength + requestProtocolPayloadLength
(Byte)(requestProtocolPayload.Length + 1),
IrdaCommand,
// add the IrdaPayload witch is the requestProtocolCommand + requestProtocolPayload
requestProtocolCommand
};
irdaCrcInputBuffer.AddRange(requestProtocolPayload);
var crcResult = Crc16Ccitt.CalculateReversedLsb8408(irdaCrcInputBuffer.ToArray());
// assemble result buffer
// IrdaSyncByte|crcInputBuffer|IrdaCrc LSB|IrdaCrc MSB
var irdaRecord = new List<Byte> { IrdaSyncByte };
irdaRecord.AddRange(irdaCrcInputBuffer);
irdaRecord.Add((Byte)(crcResult & 0xFF));
irdaRecord.Add((Byte)(crcResult >> 8));
Logger.Info($"DecodeDataForPhysicalLayer PC->Cordonel({BitConverter.ToString(irdaRecord.ToArray())})");
return irdaRecord;
}
}
}
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
namespace Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol
{
public class LedTransmitProtocol : BaseTransmitProtocol
{
private readonly Byte? _protSyncByte = null;
private const UInt16 ProtAddLength = 0;
private readonly UInt16? _protLengthIndex = null;
private const Int32 DefaultResponseTimeoutMs = 50;
private Int32 _responseTimeoutMs = DefaultResponseTimeoutMs;
private const UInt32 BaudRate = 115200;
private readonly UInt32? _receiveBufferFlushThreshold = 1000;
/// <inheritdoc />
public override void SetResponseTimeout(Int32 responseTimeoutMs)
{
_responseTimeoutMs = responseTimeoutMs;
}
/// <inheritdoc />
public override void SetDefaultResponseTimeout()
{
_responseTimeoutMs = DefaultResponseTimeoutMs;
}
public LedTransmitProtocol(String portName) : base (portName)
{
}
public override TransmitPortSettings GetTransmitPortSettings()
{
return new TransmitPortSettings(_protSyncByte, _protLengthIndex, ProtAddLength, _responseTimeoutMs, BaudRate,
_receiveBufferFlushThreshold);
}
/// <inheritdoc />
public override List<Byte> DecodeDataForPhysicalLayer(Byte command, Byte[] payload)
{
throw new ApplicationException("Streaming port cannot send data");
}
/// <inheritdoc />
public override List<Byte> DecodeDataForLogicLayer(List<Byte> rawData)
{
return rawData;
}
}
}
@@ -0,0 +1,9 @@
using System.Runtime.InteropServices;
using System.Reflection;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol")]
[assembly: Guid("D93862ED-329B-49E2-8113-D6754D34BE6B")]
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
namespace Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol
{
public class RfidTransmitProtocol : UartTransmitProtocol
{
//use the UART transmit protocol sync byte wrapped to RFID protocol
private const Byte ProtSyncByte = 0x5B;
private const UInt16 ProtAddLength = 1;
private readonly UInt16? _protLengthIndex = 1;
//this will be used if RFID protocol has been extracted from RfidComPort
//private const Byte ProtSyncByte = 0x01;
//private const UInt16 ProtAddLength = 1;
//private readonly UInt16? _protLengthIndex = 3;
private const Int32 DefaultResponseTimeoutMs = 1550;
private Int32 _responseTimeoutMs = DefaultResponseTimeoutMs;
/// <inheritdoc />
public override void SetResponseTimeout(Int32 responseTimeoutMs)
{
_responseTimeoutMs = responseTimeoutMs;
}
/// <inheritdoc />
public override void SetDefaultResponseTimeout()
{
_responseTimeoutMs = DefaultResponseTimeoutMs;
}
private const UInt32 BaudRate = 9600;
private readonly UInt32? _receiveBufferFlushThreshold = null;
public RfidTransmitProtocol(String portName) : base (portName)
{
}
public override TransmitPortSettings GetTransmitPortSettings()
{
return new TransmitPortSettings(ProtSyncByte, _protLengthIndex, ProtAddLength,
_responseTimeoutMs, BaudRate, _receiveBufferFlushThreshold);
}
public override List<Byte> SpecificCrcCalc()
{
//returns an empty list of bytes to avoid filling of CRC in advance to the CRC calculation,
//because the RFID does not use the CRC filled up with 0x00, 0x00 to calculate itself
return new List<Byte>();
}
}
}
@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.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>{63E0AC5E-B7C8-4363-A2FE-BE489E648FF0}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol</RootNamespace>
<AssemblyName>Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<SccProjectName>%24/WorkBenchV2/Main/Common/Hardware/Interfaces/Protocols/TransmitProtocol</SccProjectName>
<SccLocalPath>.</SccLocalPath>
<SccAuxPath>http://10.1.0.164:8080/tfs/sensusprojectcollection</SccAuxPath>
<SccProvider>{4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>7.0</LangVersion>
</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>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\PerformTest\</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)' == '_MANUAL_CONTROL|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<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.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\..\.shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="BaseTransmitProtocol.cs" />
<Compile Include="ITransmitProtocol.cs" />
<Compile Include="LedTransmitProtocol.cs" />
<Compile Include="IrdaTransmitProtocol.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RfidTransmitProtocol.cs" />
<Compile Include="UartTransmitProtocol.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\Utils\Logging\Logging.csproj">
<Project>{4B88B0D3-E791-4774-9521-EABEACDC420E}</Project>
<Name>Logging</Name>
</ProjectReference>
<ProjectReference Include="..\..\Ports\PortCore\PortCore.csproj">
<Project>{2E139F63-B6FE-4EA9-A098-85364FE9B26C}</Project>
<Name>PortCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Utils\Crc16Ccitt\Crc16Ccitt.csproj">
<Project>{91def0c0-aa4f-40ae-8f18-cb717fde7e7f}</Project>
<Name>Crc16Ccitt</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,150 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
using Xylem.Common.Utils.Crc16Ccitt;
namespace Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol
{
public class UartTransmitProtocol : BaseTransmitProtocol
{
// Format of UART transmit protocol:
// UartSyncByte|UartLength LSB|UartLength MSB|requestProtocolCommand|
// NextRequestProtocolCommand|UartCrc LSB|UartCrc MSB|requestProtocolPayload
// UartLength MSB will NOT be used, it is always 0x00
// NextRequestProtocolCommand will NOT be used, it is always 0x00
//Indexes of UART transmit protocol
private const Int32 UartSyncByteIndex = 0;
private const Int32 UartLengthIndex = 1;
private const Int32 UartCommandIndex = 3;
private const Int32 UartCrcIndex = 5;
private const Int32 UartPayloadIndex = 7;
//length of UART transmit protocol fields
private const Int32 UartSyncByteLength = 1; //length of syncByte
private const Int32 UartHeaderLength = 4; //length excluding syncByte and CRC
private const Int32 UartCrcLength = 2;
private const Int32 UartProtocolFrameLength = UartSyncByteLength + UartHeaderLength
+ UartCrcLength;
private const Byte UartSyncByte = 0x5B;
//The UART length covers all bytes excluding the syncByte length
private const Int32 DefaultResponseTimeoutMs = 50;
private Int32 _responseTimeoutMs = DefaultResponseTimeoutMs;
private const UInt16 ProtAddLength = UartSyncByteLength;
private const UInt32 BaudRate = 9600;
private readonly UInt32? _receiveBufferFlushThreshold = null;
private const Boolean UartDoubleSyncByte = true;
/// <inheritdoc />
public override void SetResponseTimeout(Int32 responseTimeoutMs)
{
_responseTimeoutMs = responseTimeoutMs;
}
/// <inheritdoc />
public override void SetDefaultResponseTimeout()
{
_responseTimeoutMs = DefaultResponseTimeoutMs;
}
public UartTransmitProtocol(String portName) : base (portName)
{
}
public override TransmitPortSettings GetTransmitPortSettings()
{
return new TransmitPortSettings(UartSyncByte, UartLengthIndex, ProtAddLength,
_responseTimeoutMs, BaudRate, _receiveBufferFlushThreshold, UartDoubleSyncByte);
}
public override List<Byte> DecodeDataForLogicLayer(List<Byte> uartRecord)
{
// check the record length and start of frame information
if (uartRecord.Count < uartRecord[UartLengthIndex] + UartSyncByteLength
|| UartSyncByte != uartRecord[UartSyncByteIndex])
{
var error = new ApplicationException("Reply from UART is invalid. Decoding of transmit layer failed.");
Logger.Error(error.Message, error);
return new List<Byte>();
}
//extract the header without syncByte and the payload, call CRC calculation
var calculatedCrc = CrcCalc(uartRecord.GetRange(UartLengthIndex, UartHeaderLength),
uartRecord.GetRange(UartPayloadIndex, uartRecord.Count - UartProtocolFrameLength));
//extract the received CRC, first the MSB
UInt16 receivedCrc = uartRecord[UartCrcIndex + 1];
receivedCrc <<= 8;
receivedCrc += uartRecord[UartCrcIndex];
if(receivedCrc != calculatedCrc)
{
var error = new ApplicationException("Reply CRC failure. Decoding of transmit layer failed.");
Logger.Error(error.Message, error);
return new List<Byte>();
}
//the request protocol needs the requestProtocolCommand and the requestProtocolPayload
var ret = new List<Byte> {uartRecord[UartCommandIndex]};
//add the requestProtocolCommand
//add the request protocol payload
ret.AddRange(uartRecord.GetRange(UartPayloadIndex, uartRecord.Count - UartProtocolFrameLength));
return ret;
}
public virtual List<Byte> SpecificCrcCalc()
{
//for the pure UART transmit protocol the CRC fields will be set
//to 0x00 and used for the CRC calculation
return new List<Byte>() { 0x00, 0x00 };
}
public UInt16 CrcCalc(List<Byte> header, List<Byte> payload)
{
//the CRC calculation excludes the syncByte and optional includes the
//CRC fields filled up with 0x00
var tmpArr = new List<Byte>();
tmpArr.AddRange(header);
//this adds the optional CRC fields
tmpArr.AddRange(SpecificCrcCalc());
tmpArr.AddRange(payload);
return Crc16Ccitt.CalculateMsb1021(tmpArr.ToArray());
}
/// <inheritdoc />
public override List<Byte> DecodeDataForPhysicalLayer(Byte requestProtocolCommand, Byte[] requestProtocolPayload)
{
var uartHeader = new List<Byte>();
var uartPayload = requestProtocolPayload.ToList();
var uartRecord = new List<Byte>();
//first assemble the list being used for CRC calculation,
//this list does NOT contain the syncByte and optional
//the CRC LSB and MSB if set in SpecificCrcCalc()
//the length of length LSB and MSB: 2
//add the length of the requestProtocolCommand: 1
//the nextRequestProtocolCommand length: 1
//the length of the CRC MSB and LSB: 2
uartHeader.Add((Byte)(requestProtocolPayload.Length + UartHeaderLength + UartCrcLength));
//add the length MSB, always 0x00
uartHeader.Add(0x00);
uartHeader.Add(requestProtocolCommand);
//add the nextRequestProtocolCommand, always 0x00
uartHeader.Add(0x00);
var crcResult = CrcCalc(uartHeader, uartPayload);
//assemble the entire record for sending via the communication port
uartRecord.Add(UartSyncByte);
uartRecord.AddRange(uartHeader);
uartRecord.Add((Byte)(crcResult & 0xFF));
uartRecord.Add((Byte)(crcResult >> 8));
uartRecord.AddRange(uartPayload);
return uartRecord;
}
}
}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="4.6.7" targetFramework="net40" />
</packages>