Initial commit
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user