public static class AxdrEncoder
Namespace: SharpMeter.Dlms.Codec
Methods
| Name | Description |
|---|---|
DecodeBoolean(ReadOnlySpan<byte> source) static |
Decodes a boolean value from A-XDR data. |
DecodeDateTime(ReadOnlySpan<byte> source) static |
Decodes a COSEM date-time (12 bytes) from A-XDR data. |
DecodeDoubleLong(ReadOnlySpan<byte> source) static |
Decodes a signed 32-bit integer from A-XDR data (big-endian). |
DecodeDoubleLongUnsigned(ReadOnlySpan<byte> source) static |
Decodes an unsigned 32-bit integer from A-XDR data (big-endian). |
DecodeInteger(ReadOnlySpan<byte> source) static |
Decodes a signed 8-bit integer from A-XDR data. |
DecodeLength(ReadOnlySpan<byte> source, int bytesConsumed) static |
Decodes a BER-encoded length from the given span. |
DecodeLong(ReadOnlySpan<byte> source) static |
Decodes a signed 16-bit integer from A-XDR data (big-endian). |
DecodeLong64Unsigned(ReadOnlySpan<byte> source) static |
Decodes an unsigned 64-bit integer from A-XDR data (big-endian). |
DecodeLongUnsigned(ReadOnlySpan<byte> source) static |
Decodes an unsigned 16-bit integer from A-XDR data (big-endian). |
DecodeOctetString(ReadOnlySpan<byte> source, int bytesConsumed) static |
Decodes an octet string from A-XDR data. |
DecodeUnsigned(ReadOnlySpan<byte> source) static |
Decodes an unsigned 8-bit integer from A-XDR data. |
DecodeVisibleString(ReadOnlySpan<byte> source, int bytesConsumed) static |
Decodes a visible (ASCII) string from A-XDR data. |
EncodeBoolean(bool value) static |
Encodes a boolean value. |
EncodeDoubleLong(int value) static |
Encodes a signed 32-bit integer (big-endian). |
EncodeDoubleLongUnsigned(uint value) static |
Encodes an unsigned 32-bit integer (big-endian). |
EncodeEnum(byte value) static |
Encodes an enum value (unsigned 8-bit). |
EncodeInteger(sbyte value) static |
Encodes a signed 8-bit integer. |
EncodeLength(int length) static |
Encodes a length value using ASN.1 BER length encoding. |
EncodeLong(short value) static |
Encodes a signed 16-bit integer (big-endian). |
EncodeLongUnsigned(ushort value) static |
Encodes an unsigned 16-bit integer (big-endian). |
EncodeNull() static |
Encodes null data (no value). |
EncodeOctetString(ReadOnlySpan<byte> value) static |
Encodes an octet string (byte array). |
EncodeUnsigned(byte value) static |
Encodes an unsigned 8-bit integer. |
EncodeVisibleString(string value) static |
Encodes a visible (ASCII) string. |
ReadDataType(ReadOnlySpan<byte> source) static |
Decodes the data type tag from the given span. |
DecodeBoolean(ReadOnlySpan source)
bool AxdrEncoder.DecodeBoolean(ReadOnlySpan<byte> source)
Decodes a boolean value from A-XDR data.
Parameters
| Name | Type | Description |
|---|---|---|
source | ReadOnlySpan<byte> | Source starting after the type tag. |
Returns: The decoded boolean value.
DecodeDateTime(ReadOnlySpan source)
DateTime AxdrEncoder.DecodeDateTime(ReadOnlySpan<byte> source)
Decodes a COSEM date-time (12 bytes) from A-XDR data.
Parameters
| Name | Type | Description |
|---|---|---|
source | ReadOnlySpan<byte> | Source starting after the type tag (must be at least 12 bytes). |
Returns: The decoded DateTime value in UTC.
DecodeDoubleLong(ReadOnlySpan source)
int AxdrEncoder.DecodeDoubleLong(ReadOnlySpan<byte> source)
Decodes a signed 32-bit integer from A-XDR data (big-endian).
Parameters
| Name | Type | Description |
|---|---|---|
source | ReadOnlySpan<byte> | Source starting after the type tag. |
Returns: The decoded value.
DecodeDoubleLongUnsigned(ReadOnlySpan source)
uint AxdrEncoder.DecodeDoubleLongUnsigned(ReadOnlySpan<byte> source)
Decodes an unsigned 32-bit integer from A-XDR data (big-endian).
DecodeInteger(ReadOnlySpan source)
sbyte AxdrEncoder.DecodeInteger(ReadOnlySpan<byte> source)
Decodes a signed 8-bit integer from A-XDR data.
Parameters
| Name | Type | Description |
|---|---|---|
source | ReadOnlySpan<byte> | Source starting after the type tag. |
Returns: The decoded value.
DecodeLength(ReadOnlySpan source, int bytesConsumed)
int AxdrEncoder.DecodeLength(ReadOnlySpan<byte> source, out int bytesConsumed)
Decodes a BER-encoded length from the given span.
Parameters
| Name | Type | Description |
|---|---|---|
source | ReadOnlySpan<byte> | The source data. |
bytesConsumed | out int | The number of bytes consumed by the length encoding. |
Returns: The decoded length value.
DecodeLong(ReadOnlySpan source)
short AxdrEncoder.DecodeLong(ReadOnlySpan<byte> source)
Decodes a signed 16-bit integer from A-XDR data (big-endian).
Parameters
| Name | Type | Description |
|---|---|---|
source | ReadOnlySpan<byte> | Source starting after the type tag. |
Returns: The decoded value.
DecodeLong64Unsigned(ReadOnlySpan source)
ulong AxdrEncoder.DecodeLong64Unsigned(ReadOnlySpan<byte> source)
Decodes an unsigned 64-bit integer from A-XDR data (big-endian).
Parameters
| Name | Type | Description |
|---|---|---|
source | ReadOnlySpan<byte> | Source starting after the type tag. |
Returns: The decoded value.
DecodeLongUnsigned(ReadOnlySpan source)
ushort AxdrEncoder.DecodeLongUnsigned(ReadOnlySpan<byte> source)
Decodes an unsigned 16-bit integer from A-XDR data (big-endian).
DecodeOctetString(ReadOnlySpan source, int bytesConsumed)
byte[] AxdrEncoder.DecodeOctetString(ReadOnlySpan<byte> source, out int bytesConsumed)
Decodes an octet string from A-XDR data.
Parameters
| Name | Type | Description |
|---|---|---|
source | ReadOnlySpan<byte> | Source starting after the type tag. |
bytesConsumed | out int | Total bytes consumed including length prefix. |
Returns: The decoded byte array.
DecodeUnsigned(ReadOnlySpan source)
byte AxdrEncoder.DecodeUnsigned(ReadOnlySpan<byte> source)
Decodes an unsigned 8-bit integer from A-XDR data.
Parameters
| Name | Type | Description |
|---|---|---|
source | ReadOnlySpan<byte> | Source starting after the type tag. |
Returns: The decoded value.
DecodeVisibleString(ReadOnlySpan source, int bytesConsumed)
string AxdrEncoder.DecodeVisibleString(ReadOnlySpan<byte> source, out int bytesConsumed)
Decodes a visible (ASCII) string from A-XDR data.
Parameters
| Name | Type | Description |
|---|---|---|
source | ReadOnlySpan<byte> | Source starting after the type tag. |
bytesConsumed | out int | Total bytes consumed including length prefix. |
Returns: The decoded string.
EncodeBoolean(bool value)
byte[] AxdrEncoder.EncodeBoolean(bool value)
Encodes a boolean value.
EncodeDoubleLong(int value)
byte[] AxdrEncoder.EncodeDoubleLong(int value)
Encodes a signed 32-bit integer (big-endian).
EncodeDoubleLongUnsigned(uint value)
byte[] AxdrEncoder.EncodeDoubleLongUnsigned(uint value)
Encodes an unsigned 32-bit integer (big-endian).
EncodeEnum(byte value)
byte[] AxdrEncoder.EncodeEnum(byte value)
Encodes an enum value (unsigned 8-bit).
EncodeInteger(sbyte value)
byte[] AxdrEncoder.EncodeInteger(sbyte value)
Encodes a signed 8-bit integer.
EncodeLength(int length)
byte[] AxdrEncoder.EncodeLength(int length)
Encodes a length value using ASN.1 BER length encoding.
Parameters
| Name | Type | Description |
|---|---|---|
length | int | The length to encode. |
Returns: The encoded length bytes.
EncodeLong(short value)
byte[] AxdrEncoder.EncodeLong(short value)
Encodes a signed 16-bit integer (big-endian).
EncodeLongUnsigned(ushort value)
byte[] AxdrEncoder.EncodeLongUnsigned(ushort value)
Encodes an unsigned 16-bit integer (big-endian).
EncodeNull()
byte[] AxdrEncoder.EncodeNull()
Encodes null data (no value).
EncodeOctetString(ReadOnlySpan value)
byte[] AxdrEncoder.EncodeOctetString(ReadOnlySpan<byte> value)
Encodes an octet string (byte array).
EncodeUnsigned(byte value)
byte[] AxdrEncoder.EncodeUnsigned(byte value)
Encodes an unsigned 8-bit integer.
EncodeVisibleString(string value)
byte[] AxdrEncoder.EncodeVisibleString(string value)
Encodes a visible (ASCII) string.
ReadDataType(ReadOnlySpan source)
DlmsDataType AxdrEncoder.ReadDataType(ReadOnlySpan<byte> source)
Decodes the data type tag from the given span.
Parameters
| Name | Type | Description |
|---|---|---|
source | ReadOnlySpan<byte> | The source data starting with a type tag. |
Returns: The DLMS data type.
View Source
/// <summary>
/// Encodes and decodes DLMS data values using A-XDR (Adapted External Data Representation) per IEC 62056.
/// </summary>
public static class AxdrEncoder
{
#region Encoding
/// <summary>Encodes a boolean value.</summary>
public static byte[] EncodeBoolean(bool value) => [(byte)DlmsDataType.Boolean, value ? (byte)0xFF : (byte)0x00];
/// <summary>Encodes an unsigned 8-bit integer.</summary>
public static byte[] EncodeUnsigned(byte value) => [(byte)DlmsDataType.Unsigned, value];
/// <summary>Encodes a signed 8-bit integer.</summary>
public static byte[] EncodeInteger(sbyte value) => [(byte)DlmsDataType.Integer, (byte)value];
/// <summary>Encodes an unsigned 16-bit integer (big-endian).</summary>
public static byte[] EncodeLongUnsigned(ushort value) => [(byte)DlmsDataType.LongUnsigned, (byte)(value >> 8), (byte)(value & 0xFF)];
/// <summary>Encodes a signed 16-bit integer (big-endian).</summary>
public static byte[] EncodeLong(short value) => [(byte)DlmsDataType.Long, (byte)(value >> 8), (byte)(value & 0xFF)];
/// <summary>Encodes an unsigned 32-bit integer (big-endian).</summary>
public static byte[] EncodeDoubleLongUnsigned(uint value) => [(byte)DlmsDataType.DoubleLongUnsigned, (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)(value & 0xFF)];
/// <summary>Encodes a signed 32-bit integer (big-endian).</summary>
public static byte[] EncodeDoubleLong(int value) => [(byte)DlmsDataType.DoubleLong, (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)(value & 0xFF)];
/// <summary>Encodes an octet string (byte array).</summary>
public static byte[] EncodeOctetString(ReadOnlySpan<byte> value)
{
var result = new byte[1 + EncodeLength(value.Length).Length + value.Length];
var offset = 0;
result[offset++] = (byte)DlmsDataType.OctetString;
var lenBytes = EncodeLength(value.Length);
lenBytes.CopyTo(result, offset);
offset += lenBytes.Length;
value.CopyTo(result.AsSpan(offset));
return result;
}
/// <summary>Encodes a visible (ASCII) string.</summary>
public static byte[] EncodeVisibleString(string value)
{
var bytes = Encoding.ASCII.GetBytes(value);
var result = new byte[1 + EncodeLength(bytes.Length).Length + bytes.Length];
var offset = 0;
result[offset++] = (byte)DlmsDataType.VisibleString;
var lenBytes = EncodeLength(bytes.Length);
lenBytes.CopyTo(result, offset);
offset += lenBytes.Length;
bytes.CopyTo(result, offset);
return result;
}
/// <summary>Encodes null data (no value).</summary>
public static byte[] EncodeNull() => [(byte)DlmsDataType.NullData];
/// <summary>Encodes an enum value (unsigned 8-bit).</summary>
public static byte[] EncodeEnum(byte value) => [(byte)DlmsDataType.Enum, value];
#endregion
#region Length Encoding
/// <summary>
/// Encodes a length value using ASN.1 BER length encoding.
/// </summary>
/// <param name = "length">The length to encode.</param>
/// <returns>The encoded length bytes.</returns>
public static byte[] EncodeLength(int length)
{
if (length < 0x80)
return[(byte)length];
if (length <= 0xFF)
return[0x81, (byte)length];
return[0x82, (byte)(length >> 8), (byte)(length & 0xFF)];
}
/// <summary>
/// Decodes a BER-encoded length from the given span.
/// </summary>
/// <param name = "source">The source data.</param>
/// <param name = "bytesConsumed">The number of bytes consumed by the length encoding.</param>
/// <returns>The decoded length value.</returns>
public static int DecodeLength(ReadOnlySpan<byte> source, out int bytesConsumed)
{
if (source.Length < 1)
{
bytesConsumed = 0;
return 0;
}
if (source[0] < 0x80)
{
bytesConsumed = 1;
return source[0];
}
var numBytes = source[0] & 0x7F;
bytesConsumed = 1 + numBytes;
return numBytes switch
{
1 when source.Length >= 2 => source[1],
2 when source.Length >= 3 => (source[1] << 8) | source[2],
3 when source.Length >= 4 => (source[1] << 16) | (source[2] << 8) | source[3],
_ => 0
};
}
#endregion
#region Decoding
/// <summary>
/// Decodes the data type tag from the given span.
/// </summary>
/// <param name = "source">The source data starting with a type tag.</param>
/// <returns>The DLMS data type.</returns>
public static DlmsDataType ReadDataType(ReadOnlySpan<byte> source) => source.Length > 0 ? (DlmsDataType)source[0] : DlmsDataType.NullData;
/// <summary>
/// Decodes an unsigned 8-bit integer from A-XDR data.
/// </summary>
/// <param name = "source">Source starting after the type tag.</param>
/// <returns>The decoded value.</returns>
public static byte DecodeUnsigned(ReadOnlySpan<byte> source) => source.Length > 0 ? source[0] : (byte)0;
/// <summary>
/// Decodes an unsigned 16-bit integer from A-XDR data (big-endian).
/// </summary>
public static ushort DecodeLongUnsigned(ReadOnlySpan<byte> source) => source.Length >= 2 ? (ushort)((source[0] << 8) | source[1]) : (ushort)0;
/// <summary>
/// Decodes an unsigned 32-bit integer from A-XDR data (big-endian).
/// </summary>
public static uint DecodeDoubleLongUnsigned(ReadOnlySpan<byte> source) => source.Length >= 4 ? (uint)((source[0] << 24) | (source[1] << 16) | (source[2] << 8) | source[3]) : 0u;
/// <summary>
/// Decodes an octet string from A-XDR data.
/// </summary>
/// <param name = "source">Source starting after the type tag.</param>
/// <param name = "bytesConsumed">Total bytes consumed including length prefix.</param>
/// <returns>The decoded byte array.</returns>
public static byte[] DecodeOctetString(ReadOnlySpan<byte> source, out int bytesConsumed)
{
var length = DecodeLength(source, out var lenBytes);
bytesConsumed = lenBytes + length;
if (source.Length < bytesConsumed)
throw new ArgumentException("Truncated octet string data");
return source.Slice(lenBytes, length).ToArray();
}
/// <summary>
/// Decodes a boolean value from A-XDR data.
/// </summary>
/// <param name = "source">Source starting after the type tag.</param>
/// <returns>The decoded boolean value.</returns>
public static bool DecodeBoolean(ReadOnlySpan<byte> source) => source.Length > 0 && source[0] != 0x00;
/// <summary>
/// Decodes a signed 8-bit integer from A-XDR data.
/// </summary>
/// <param name = "source">Source starting after the type tag.</param>
/// <returns>The decoded value.</returns>
public static sbyte DecodeInteger(ReadOnlySpan<byte> source) => source.Length > 0 ? (sbyte)source[0] : (sbyte)0;
/// <summary>
/// Decodes a signed 16-bit integer from A-XDR data (big-endian).
/// </summary>
/// <param name = "source">Source starting after the type tag.</param>
/// <returns>The decoded value.</returns>
public static short DecodeLong(ReadOnlySpan<byte> source) => source.Length >= 2 ? (short)((source[0] << 8) | source[1]) : (short)0;
/// <summary>
/// Decodes a signed 32-bit integer from A-XDR data (big-endian).
/// </summary>
/// <param name = "source">Source starting after the type tag.</param>
/// <returns>The decoded value.</returns>
public static int DecodeDoubleLong(ReadOnlySpan<byte> source) => source.Length >= 4 ? (source[0] << 24) | (source[1] << 16) | (source[2] << 8) | source[3] : 0;
/// <summary>
/// Decodes an unsigned 64-bit integer from A-XDR data (big-endian).
/// </summary>
/// <param name = "source">Source starting after the type tag.</param>
/// <returns>The decoded value.</returns>
public static ulong DecodeLong64Unsigned(ReadOnlySpan<byte> source) => source.Length >= 8 ? ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7] : 0UL;
/// <summary>
/// Decodes a visible (ASCII) string from A-XDR data.
/// </summary>
/// <param name = "source">Source starting after the type tag.</param>
/// <param name = "bytesConsumed">Total bytes consumed including length prefix.</param>
/// <returns>The decoded string.</returns>
public static string DecodeVisibleString(ReadOnlySpan<byte> source, out int bytesConsumed)
{
var length = DecodeLength(source, out var lenBytes);
bytesConsumed = lenBytes + length;
if (source.Length < bytesConsumed)
throw new ArgumentException("Truncated visible string data");
return Encoding.ASCII.GetString(source.Slice(lenBytes, length));
}
/// <summary>
/// Decodes a COSEM date-time (12 bytes) from A-XDR data.
/// </summary>
/// <remarks>
/// COSEM date-time structure: year(2) month(1) day(1) dow(1) hour(1) min(1) sec(1) hundredths(1) deviation(2)
/// status(1).
/// Total: 12 bytes.
/// </remarks>
/// <param name = "source">Source starting after the type tag (must be at least 12 bytes).</param>
/// <returns>The decoded DateTime value in UTC.</returns>
public static DateTime DecodeDateTime(ReadOnlySpan<byte> source)
{
if (source.Length < 12)
throw new ArgumentException("COSEM date-time requires 12 bytes");
var year = (ushort)((source[0] << 8) | source[1]);
var month = source[2];
var day = source[3];
// source[4] = day of week (ignored)
var hour = source[5];
var minute = source[6];
var second = source[7];
var hundredths = source[8];
var deviation = (short)((source[9] << 8) | source[10]);
// source[11] = clock status (ignored)
// Handle "not specified" wildcard values
if (year == 0xFFFF)
year = 2000;
if (month == 0xFF)
month = 1;
if (day == 0xFF)
day = 1;
if (hour == 0xFF)
hour = 0;
if (minute == 0xFF)
minute = 0;
if (second == 0xFF)
second = 0;
if (hundredths == 0xFF)
hundredths = 0;
var dt = new DateTime(year, month, day, hour, minute, second, hundredths * 10, DateTimeKind.Utc);
// Apply deviation (minutes from UTC) if specified
if (deviation != unchecked((short)0x8000))
dt = dt.AddMinutes(-deviation);
return dt;
}
#endregion
}