/// <summary> /// Encodes sampled value tag with only a 16-bit length. /// </summary> /// <param name="length">Value to encode.</param> /// <param name="tag">Sampled value tag to encode.</param> /// <param name="buffer">Buffer to hold encoded sampled value.</param> /// <param name="index">Start index of buffer where tag will begin - will be auto-incremented.</param> public static void EncodeTagLength(this ushort length, SampledValueTag tag, byte[] buffer, ref int index) { buffer[index++] = (byte)tag; buffer[index++] = 0x80 | 2; buffer[index++] = (byte)((length & 0xFF00) >> 8); buffer[index++] = (byte)(length & 0x00FF); }
/// <summary> /// Encodes byte based sampled value tag. /// </summary> /// <param name="value">Value to encode.</param> /// <param name="tag">Sampled value tag to encode.</param> /// <param name="buffer">Buffer to hold encoded sampled value.</param> /// <param name="index">Start index of buffer where tag will begin - will be auto-incremented.</param> public static void EncodeTagValue(this byte value, SampledValueTag tag, byte[] buffer, ref int index) { const ushort length = 1; buffer[index++] = (byte)tag; length.EncodeTagLength(buffer, ref index); buffer[index++] = value; }
/// <summary> /// Validates sample value tag exists and skips past it. /// </summary> /// <param name="buffer">Buffer containing sampled value tag length.</param> /// <param name="tag">Sampled value tag to validate.</param> /// <param name="index">Start index of buffer where tag length begins - will be auto-incremented.</param> public static int ValidateTag(this byte[] buffer, SampledValueTag tag, ref int index) { if ((SampledValueTag)buffer[index] != tag) { throw new InvalidOperationException($"Encountered out-of-sequence or unknown sampled value tag: 0x{buffer[index].ToString("X").PadLeft(2, '0')}"); } index++; return(buffer.ParseTagLength(ref index)); }
/// <summary> /// Validates and parses string sample value tag. /// </summary> /// <param name="buffer">Buffer containing sampled value.</param> /// <param name="tag">Sampled value tag to parse.</param> /// <param name="index">Start index of buffer where tag length begins - will be auto-incremented.</param> public static string ParseStringTag(this byte[] buffer, SampledValueTag tag, ref int index) { if ((SampledValueTag)buffer[index] != tag) { throw new InvalidOperationException($"Encountered out-of-sequence or unknown sampled value tag: 0x{buffer[index].ToString("X").PadLeft(2, '0')}"); } index++; int tagLength = buffer.ParseTagLength(ref index); string result = Encoding.ASCII.GetString(buffer, index, tagLength); index += tagLength; return(result); }
/// <summary> /// Validates and parses 8-byte length sample value tag. /// </summary> /// <param name="buffer">Buffer containing sampled value.</param> /// <param name="tag">Sampled value tag to parse.</param> /// <param name="index">Start index of buffer where tag length begins - will be auto-incremented.</param> public static ulong ParseUInt64Tag(this byte[] buffer, SampledValueTag tag, ref int index) { if ((SampledValueTag)buffer[index] != tag) { throw new InvalidOperationException($"Encountered out-of-sequence or unknown sampled value tag: 0x{buffer[index].ToString("X").PadLeft(2, '0')}"); } index++; int tagLength = buffer.ParseTagLength(ref index); if (tagLength < 8) { throw new InvalidOperationException($"Unexpected length for \"{tag}\" tag: {tagLength}"); } ulong result = BigEndian.ToUInt64(buffer, index); index += tagLength; return(result); }
/// <summary> /// Validates and parses 4-byte length sample value tag. /// </summary> /// <param name="buffer">Buffer containing sampled value.</param> /// <param name="tag">Sampled value tag to parse.</param> /// <param name="index">Start index of buffer where tag length begins - will be auto-incremented.</param> public static uint ParseUInt32Tag(this byte[] buffer, SampledValueTag tag, ref int index) { if ((SampledValueTag)buffer[index] != tag) { throw new InvalidOperationException("Encountered out-of-sequence or unknown sampled value tag: 0x" + buffer[index].ToString("X").PadLeft(2, '0')); } index++; int tagLength = buffer.ParseTagLength(ref index); if (tagLength < 4) { throw new InvalidOperationException(string.Format("Unexpected length for \"{0}\" tag: {1}", tag, tagLength)); } uint result = BigEndian.ToUInt32(buffer, index); index += tagLength; return(result); }
/// <summary> /// Encodes primitive type sampled value tag. /// </summary> /// <param name="value">Value to encode.</param> /// <param name="tag">Sampled value tag to encode.</param> /// <param name="buffer">Buffer to hold encoded sampled value.</param> /// <param name="index">Start index of buffer where tag will begin - will be auto-incremented.</param> public static void EncodeTagValue <T>(this T value, SampledValueTag tag, byte[] buffer, ref int index) where T : struct, IConvertible { if (!typeof(T).IsPrimitive) { throw new ArgumentException("Value type is not primitive", nameof(value)); } // Not sure if booleans would be encoded correctly here (due to Marshal sizeof) - also not sure // how IEC 61850 deals with booleans - as a result, booleans should likely be avoided. // I wonder if compiler is smart enough to exclude this expression in implementations since this // is always false for non boolean types - where is my WHERE expression like "~bool"... if (typeof(T) == typeof(bool)) { throw new ArgumentOutOfRangeException(nameof(value), "Boolean encoding is currently not supported"); } ushort length = (ushort)Marshal.SizeOf(typeof(T)); buffer[index++] = (byte)tag; length.EncodeTagLength(buffer, ref index); index += BigEndian.CopyBytes(value, buffer, index); }
/// <summary> /// Encodes string based sampled value tag. /// </summary> /// <param name="value">String to encode - null string will be encoded as empty string.</param> /// <param name="tag">Sampled value tag to encode.</param> /// <param name="buffer">Buffer to hold encoded sampled value.</param> /// <param name="index">Start index of buffer where tag will begin - will be auto-incremented.</param> public static void EncodeTagValue(this string value, SampledValueTag tag, byte[] buffer, ref int index) { if (value is null) { value = ""; } if (value.Length > ushort.MaxValue) { throw new ArgumentOutOfRangeException(nameof(value), $"Current implementation will not encode a string larger than {ushort.MaxValue}"); } ushort length = (ushort)value.Length; buffer[index++] = (byte)tag; length.EncodeTagLength(buffer, ref index); if (length > 0) { byte[] bytes = Encoding.ASCII.GetBytes(value); Buffer.BlockCopy(bytes, 0, buffer, index, bytes.Length); index += bytes.Length; } }