166 lines
7.1 KiB
C#
166 lines
7.1 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|