2015-04-15 18:32:56 +00:00
|
|
|
|
///
|
2022-01-23 18:56:15 +00:00
|
|
|
|
/// Copyright (c) 2013-2021 Sensus Metering Systems
|
2015-04-15 18:32:56 +00:00
|
|
|
|
///
|
|
|
|
|
|
using System;
|
2015-02-18 17:05:06 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
2022-01-23 18:56:15 +00:00
|
|
|
|
namespace Common
|
2015-02-18 17:05:06 +00:00
|
|
|
|
{
|
2022-01-23 18:56:15 +00:00
|
|
|
|
public static class Telegram
|
2015-02-18 17:05:06 +00:00
|
|
|
|
{
|
|
|
|
|
|
const UInt16 crcSeed = 0xFFFF; // Initial crc value
|
|
|
|
|
|
const UInt16 crcGP = 0xA001; // Generating polynomial
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// For standard CRC16 (remainder of division)
|
|
|
|
|
|
/// to start a new crc, set CRC16 = SEED
|
|
|
|
|
|
/// then for each byte call UpdateCRC(byte, &CRC16);
|
|
|
|
|
|
/// CRC16 will contain the result
|
|
|
|
|
|
/// (if you calculate all of the incoming data
|
|
|
|
|
|
/// INCLUDING the crc, the result should be 0x0000
|
|
|
|
|
|
/// and if you are sending the crc be sure to
|
|
|
|
|
|
/// send the bytes in the correct order)
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
static void UpdateCRC(byte b, ref UInt16 crc)
|
|
|
|
|
|
{
|
|
|
|
|
|
crc ^= (UInt16)b;
|
|
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool carry = (crc & 0x0001) != 0;
|
|
|
|
|
|
crc >>= 1;
|
|
|
|
|
|
if (carry) crc ^= crcGP;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Calculates the CRC from a data telegram (an array of bytes).
|
|
|
|
|
|
/// A complete telegram including two bytes reserved for the CRC
|
|
|
|
|
|
/// must be supplied as an argument.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="data">Data telegram</param>
|
|
|
|
|
|
/// <returns>Calculated CRC</returns>
|
2022-01-23 18:56:15 +00:00
|
|
|
|
public static UInt16 CalculateTelegramCRC(byte[] data, int enforcedLen = 0)
|
2015-02-18 17:05:06 +00:00
|
|
|
|
{
|
2022-01-23 18:56:15 +00:00
|
|
|
|
int len = (enforcedLen != 0) ? Math.Min(enforcedLen, data.Length) : data.Length;
|
2015-02-18 17:05:06 +00:00
|
|
|
|
UInt16 crc = crcSeed;
|
2022-01-23 18:56:15 +00:00
|
|
|
|
for (int i = 0; i < len - 2; i++) UpdateCRC(data[i], ref crc);
|
2015-02-18 17:05:06 +00:00
|
|
|
|
return crc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-01-23 18:56:15 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Calculates the CRC from a data telegram (an array of bytes).
|
|
|
|
|
|
/// A complete telegram including two bytes reserved for the CRC
|
|
|
|
|
|
/// must be supplied as an argument.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="data">Data telegram</param>
|
|
|
|
|
|
/// <returns>Calculated CRC</returns>
|
|
|
|
|
|
public static UInt16 CalculateTelegramCRC(int offset, byte[] data, int enforcedLen = 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
int len = (enforcedLen != 0) ? Math.Min(enforcedLen, data.Length - offset) : data.Length - offset;
|
|
|
|
|
|
UInt16 crc = crcSeed;
|
|
|
|
|
|
for (int i = 0; i < len - 2; i++) UpdateCRC(data[offset + i], ref crc);
|
|
|
|
|
|
return crc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-02-18 17:05:06 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This function modifies the last 2 bytes in the message buffer
|
|
|
|
|
|
/// by the CRC calculated from the remaining first (Lenght - 2) bytes.
|
|
|
|
|
|
/// A complete telegram including two bytes reserved for the CRC
|
|
|
|
|
|
/// must be supplied as an argument.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="data">Data telegram</param>
|
|
|
|
|
|
public static void UpdateTelegramCRC(byte[] data)
|
|
|
|
|
|
{
|
|
|
|
|
|
int len = data.Length;
|
|
|
|
|
|
if (len < 2) return;
|
|
|
|
|
|
|
2022-01-23 18:56:15 +00:00
|
|
|
|
UInt16 crc = CalculateTelegramCRC(data);
|
2015-02-18 17:05:06 +00:00
|
|
|
|
|
|
|
|
|
|
data[len - 2] = (byte)(crc & 0xFF);
|
|
|
|
|
|
data[len - 1] = (byte)(crc >> 8);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This function compares the last 2 bytes in the message buffer
|
|
|
|
|
|
/// with the CRC calculated from the remaining first (Lenght - 2) bytes.
|
|
|
|
|
|
/// A complete telegram including two bytes reserved for the CRC
|
|
|
|
|
|
/// must be supplied as an argument.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="data">Data telegram</param>
|
|
|
|
|
|
/// <returns>true if the CRC in the telegram is correct</returns>
|
2022-01-23 18:56:15 +00:00
|
|
|
|
public static bool VerifyTelegramCRC(byte[] data, int enforcedLen = 0)
|
2015-02-18 17:05:06 +00:00
|
|
|
|
{
|
2022-01-23 18:56:15 +00:00
|
|
|
|
if (data == null || data.Length == 0 || enforcedLen > data.Length) return false;
|
|
|
|
|
|
|
|
|
|
|
|
int len = (enforcedLen != 0) ? enforcedLen : data.Length;
|
2015-02-18 17:05:06 +00:00
|
|
|
|
if (len < 2) return false;
|
|
|
|
|
|
|
2022-01-23 18:56:15 +00:00
|
|
|
|
UInt16 crc = CalculateTelegramCRC(data, len);
|
2015-02-18 17:05:06 +00:00
|
|
|
|
|
|
|
|
|
|
return (data[len - 2] == (byte)(crc & 0xFF)) && (data[len - 1] == (byte)(crc >> 8));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-01-23 18:56:15 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This function compares the last 2 bytes in the message buffer
|
|
|
|
|
|
/// with the CRC calculated from the remaining first (Lenght - 2) bytes.
|
|
|
|
|
|
/// A complete telegram including two bytes reserved for the CRC
|
|
|
|
|
|
/// must be supplied as an argument.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="data">Data telegram</param>
|
|
|
|
|
|
/// <returns>true if the CRC in the telegram is correct</returns>
|
|
|
|
|
|
public static bool VerifyTelegramCRC(int offset, byte[] data, int enforcedLen = 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (data == null || data.Length == 0 || enforcedLen > data.Length - offset) return false;
|
|
|
|
|
|
|
|
|
|
|
|
int len = (enforcedLen != 0) ? enforcedLen : data.Length - offset;
|
|
|
|
|
|
if (len < 2) return false;
|
|
|
|
|
|
|
|
|
|
|
|
UInt16 crc = CalculateTelegramCRC(offset, data, len);
|
|
|
|
|
|
|
|
|
|
|
|
return (data[offset + len - 2] == (byte)(crc & 0xFF)) && (data[offset + len - 1] == (byte)(crc >> 8));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-02-18 17:05:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Calculates the checksum from a data telegram (an array of bytes).
|
2019-01-16 13:52:20 +00:00
|
|
|
|
/// A complete telegram including one byte reserved for the checksum
|
2015-02-18 17:05:06 +00:00
|
|
|
|
/// must be supplied as an argument.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="data">Data telegram</param>
|
2018-10-24 14:28:56 +00:00
|
|
|
|
/// <returns>Calculated checksum</returns>
|
2022-01-23 18:56:15 +00:00
|
|
|
|
public static byte CalculateTelegramChecksum(byte[] data)
|
2015-02-18 17:05:06 +00:00
|
|
|
|
{
|
|
|
|
|
|
byte checksum = 0;
|
|
|
|
|
|
for (int i = 0; i < data.Length - 1; i++) checksum = (byte)(checksum ^ data[i]);
|
|
|
|
|
|
return checksum;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-24 14:28:56 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Calculates the checksum from a data telegram (an array of bytes).
|
|
|
|
|
|
/// A complete telegram including one bytes reserved for the checksum
|
|
|
|
|
|
/// must be supplied as an argument.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hartPreambLen">Length of HART telegram preamble (FF bytes)</param>
|
|
|
|
|
|
/// <param name="data">Data telegram</param>
|
|
|
|
|
|
/// <returns>Calculated checksum</returns>
|
2022-01-23 18:56:15 +00:00
|
|
|
|
public static byte CalculateTelegramChecksum(int hartPreambLen, byte[] data)
|
2018-10-24 14:28:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
byte checksum = 0;
|
|
|
|
|
|
for (int i = hartPreambLen; i < data.Length - 1; i++) checksum = (byte)(checksum ^ data[i]);
|
|
|
|
|
|
return checksum;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-02-18 17:05:06 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This function modifies the last byte in the message buffer
|
|
|
|
|
|
/// by the checksum calculated from the remaining first (Lenght - 1) bytes.
|
|
|
|
|
|
/// A complete telegram including one bytes reserved for the checksum
|
|
|
|
|
|
/// must be supplied as an argument.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="data">Data telegram</param>
|
|
|
|
|
|
public static void UpdateTelegramChecksum(byte[] data)
|
|
|
|
|
|
{
|
|
|
|
|
|
int len = data.Length;
|
|
|
|
|
|
if (len < 1) return;
|
2022-01-23 18:56:15 +00:00
|
|
|
|
data[len - 1] = CalculateTelegramChecksum(data);
|
2015-02-18 17:05:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-24 14:28:56 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This function modifies the last byte in the message buffer
|
|
|
|
|
|
/// by the checksum calculated from the preceeding (Lenght - hartPreambLen - 1) bytes.
|
|
|
|
|
|
/// First hartPreambLen bytes in the telegram are skipped.
|
|
|
|
|
|
/// A complete telegram including preamble and one bytes reserved for the checksum
|
|
|
|
|
|
/// after data bytes must be supplied as an argument.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hartPreambLen">Length of HART telegram preamble (FF bytes)</param>
|
|
|
|
|
|
/// <param name="data">Data telegram</param>
|
|
|
|
|
|
public static void UpdateTelegramChecksum(int hartPreambLen, byte[] data)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (data.Length - hartPreambLen < 1) return;
|
2022-01-23 18:56:15 +00:00
|
|
|
|
data[data.Length - 1] = CalculateTelegramChecksum(hartPreambLen, data);
|
2018-10-24 14:28:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-02-18 17:05:06 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This function compares the last byte in the message buffer
|
|
|
|
|
|
/// with the checksum calculated from the remaining first (Lenght - 1) bytes.
|
|
|
|
|
|
/// A complete telegram including two bytes reserved for the checksum
|
|
|
|
|
|
/// must be supplied as an argument.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="data">Data telegram</param>
|
2018-10-24 14:28:56 +00:00
|
|
|
|
/// <returns>true if the checksum in the telegram is correct</returns>
|
2015-02-18 17:05:06 +00:00
|
|
|
|
public static bool VerifyTelegramChecksum(byte[] data)
|
|
|
|
|
|
{
|
|
|
|
|
|
int len = data.Length;
|
|
|
|
|
|
if (len < 1) return false;
|
2022-01-23 18:56:15 +00:00
|
|
|
|
return (data[len - 1] == CalculateTelegramChecksum(data));
|
2015-02-18 17:05:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-24 14:28:56 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This function compares the last byte in the message buffer
|
|
|
|
|
|
/// with the checksum calculated from the remaining first (Lenght - 1) bytes.
|
|
|
|
|
|
/// A complete telegram including two bytes reserved for the checksum
|
|
|
|
|
|
/// must be supplied as an argument.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hartPreambLen">Length of HART telegram preamble (FF bytes)</param>
|
|
|
|
|
|
/// <param name="data">Data telegram</param>
|
|
|
|
|
|
/// <returns>true if the checksum in the telegram is correct</returns>
|
|
|
|
|
|
public static bool VerifyTelegramChecksum(int hartPreambLen, byte[] data)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (data.Length - hartPreambLen < 1) return false;
|
2022-01-23 18:56:15 +00:00
|
|
|
|
return (data[data.Length - 1] == CalculateTelegramChecksum(hartPreambLen, data));
|
2018-10-24 14:28:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-02-18 17:05:06 +00:00
|
|
|
|
|
2019-01-16 13:52:20 +00:00
|
|
|
|
public static string LogTelegram(string intro, byte[] data, int from = 0, int len = -1)
|
2015-02-18 17:05:06 +00:00
|
|
|
|
{
|
2019-01-16 13:52:20 +00:00
|
|
|
|
System.Text.StringBuilder sb = new System.Text.StringBuilder(256);
|
2015-02-18 17:05:06 +00:00
|
|
|
|
|
2019-01-16 13:52:20 +00:00
|
|
|
|
int length = (len >= 0) ? len : (data.Length - from);
|
|
|
|
|
|
|
|
|
|
|
|
sb.Append(intro);
|
|
|
|
|
|
sb.Append(length.ToString());
|
2015-02-18 17:05:06 +00:00
|
|
|
|
sb.Append(" bytes:");
|
2019-01-16 13:52:20 +00:00
|
|
|
|
|
|
|
|
|
|
for (int i = from; i < from + length; i++)
|
2015-02-18 17:05:06 +00:00
|
|
|
|
{
|
2019-01-16 13:52:20 +00:00
|
|
|
|
byte b = data[i];
|
2015-02-18 17:05:06 +00:00
|
|
|
|
sb.Append(" ");
|
2019-01-16 13:52:20 +00:00
|
|
|
|
sb.Append(((int)b).ToString("X2"));
|
2015-02-18 17:05:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-01-23 18:56:15 +00:00
|
|
|
|
|
2019-01-16 13:52:20 +00:00
|
|
|
|
public static string LogTelegram(string intro, IList<byte> data, int from = 0, int len = -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
System.Text.StringBuilder sb = new System.Text.StringBuilder(256);
|
|
|
|
|
|
|
|
|
|
|
|
int length = (len >= 0) ? len : (data.Count - from);
|
|
|
|
|
|
|
|
|
|
|
|
sb.Append(intro);
|
|
|
|
|
|
sb.Append(length.ToString());
|
|
|
|
|
|
sb.Append(" bytes:");
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = from; i < from + length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
byte b = data[i];
|
|
|
|
|
|
sb.Append(" ");
|
|
|
|
|
|
sb.Append(((int)b).ToString("X2"));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-02-18 17:05:06 +00:00
|
|
|
|
|
|
|
|
|
|
public static string LogTelegram(string intro, string data)
|
|
|
|
|
|
{
|
|
|
|
|
|
System.Text.StringBuilder sb = new System.Text.StringBuilder(80);
|
|
|
|
|
|
|
|
|
|
|
|
sb.Append(intro);
|
|
|
|
|
|
sb.Append(data.Length.ToString());
|
|
|
|
|
|
sb.Append(" bytes:");
|
|
|
|
|
|
foreach (var d in data)
|
|
|
|
|
|
{
|
|
|
|
|
|
sb.Append(" ");
|
|
|
|
|
|
sb.Append(((int)d).ToString("X2"));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|