common/Hardware/Interfaces/Protocols/TransmitProtocol/UartTransmitProtocol.cs
2026-04-23 17:50:07 +02:00

170 lines
7.4 KiB
C#

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(String ident, List<Byte> uartRecord, Boolean hideDataInLog = false)
{
// check the record length and start of frame information
if (uartRecord.Count < uartRecord[UartLengthIndex] + UartSyncByteLength
|| UartSyncByte != uartRecord[UartSyncByteIndex])
{
var error = new ApplicationException($"{ident} 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($"{ident} 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(String ident, Byte requestProtocolCommand,
Byte[] requestProtocolPayload, Boolean hideDataInLog = false)
{
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;
}
/// <inheritdoc />
/// <remarks date="2022-Aug-24" author="R.Drabesch">
/// - Initial
/// </remarks>
public override List<Byte> DecodeDataForPhysicalLayerUI1236(String ident, Byte[] requestProtocolPayload,
Boolean hideDataInLog = false)
{
var uartHeader = new List<Byte>();
var uartPayload = requestProtocolPayload.ToList();
var uartRecord = new List<Byte>();
uartHeader.Add((Byte)(requestProtocolPayload.Length + 4 + 2));
uartHeader.Add(0);
uartHeader.Add(0);
var crcResult = CrcCalc(uartHeader, uartPayload);
uartRecord.Add(91);
uartRecord.AddRange(uartHeader);
uartRecord.Add((Byte)(crcResult & 0xFF));
uartRecord.Add((Byte)(crcResult >> 8));
uartRecord.AddRange(uartPayload);
return uartRecord;
}
}
}