//****************************************************************************************************** // ChannelDefinitionBase.cs - Gbtc // // Copyright © 2012, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 03/07/2005 - J. Ritchie Carroll // Generated original version of source code. // 09/15/2009 - Stephen C. Wills // Added new header and license agreement. // 10/5/2012 - Gavin E. Holden // Added new header and license agreement. // 12/17/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.Runtime.Serialization; using System.Text; // ReSharper disable VirtualMemberCallInConstructor namespace GSF.PhasorProtocols { /// /// Represents the common implementation of the protocol independent definition of any kind of data. /// [Serializable] public abstract class ChannelDefinitionBase : ChannelBase, IChannelDefinition { #region [ Members ] // Fields private IConfigurationCell m_parent; private int m_index; private string m_label; private uint m_scale; private double m_offset; #endregion #region [ Constructors ] /// /// Creates a new . /// /// The parent of this . protected ChannelDefinitionBase(IConfigurationCell parent) { m_parent = parent; } /// /// Creates a new from specified parameters. /// /// The parent of this . /// The label of this . /// The integer scaling value of this . /// The offset of this . protected ChannelDefinitionBase(IConfigurationCell parent, string label, uint scale, double offset) { m_parent = parent; Label = label; m_scale = scale; m_offset = offset; } /// /// Creates a new from serialization parameters. /// /// The with populated with data. /// The source for this deserialization. protected ChannelDefinitionBase(SerializationInfo info, StreamingContext context) { // Deserialize channel definition m_parent = (IConfigurationCell)info.GetValue("parent", typeof(IConfigurationCell)); m_index = info.GetInt32("index"); Label = info.GetString("label"); m_scale = info.GetUInt32("scale"); m_offset = info.GetDouble("offset"); } #endregion #region [ Properties ] /// /// Gets or sets the parent of this . /// public virtual IConfigurationCell Parent { get => m_parent; set => m_parent = value; } /// /// Gets the of this . /// public abstract DataFormat DataFormat { get; } /// /// Gets or sets the index of this . /// /// /// Index is automatically maintained by . /// public virtual int Index { get => m_index; set => m_index = value; } /// /// Gets or sets the offset of this . /// public virtual double Offset { get => m_offset; set => m_offset = value; } /// /// Gets or sets the conversion factor of this . /// public virtual double ConversionFactor { get => ScalingValue * ScalePerBit; set { unchecked { ScalingValue = (uint)(value / ScalePerBit); } } } /// /// Gets the scale/bit for the of this . /// /// /// The base implementation assumes scale/bit of 10^-5. /// public virtual double ScalePerBit => 0.00001D; // Typical scale/bit is 10^-5 /// /// Gets or sets the integer scaling value of this . /// public virtual uint ScalingValue { get => m_scale; set { if (value > MaximumScalingValue) throw new OverflowException($"Scaling value cannot exceed {MaximumScalingValue}"); m_scale = value; } } /// /// Gets the maximum value for the of this . /// /// /// The base implementation accommodates maximum scaling factor of 24-bits of space. /// public virtual uint MaximumScalingValue => UInt24.MaxValue; // Typical scaling/conversion factors should fit within 3 bytes (i.e., 24 bits) of space /// /// Gets or sets the label of this . /// public virtual string Label { get => m_label; set { if (string.IsNullOrEmpty(value)) value = "undefined"; value = value.GetValidLabel(); if (value.Length > MaximumLabelLength) throw new OverflowException($"Label length cannot exceed {MaximumLabelLength} characters."); m_label = value; } } /// /// Gets the binary image of the of this . /// public virtual byte[] LabelImage => Encoding.ASCII.GetBytes(MaximumLabelLength < int.MaxValue && AutoPadLabelImage ? Label.PadRight(MaximumLabelLength): Label); /// /// Gets the maximum length of the of this . /// public virtual int MaximumLabelLength => 16; // Typical label length is 16 bytes /// /// Gets flag that indicates if should auto pad-right value to . /// /// /// Padding will only be applied when is less than . /// public virtual bool AutoPadLabelImage => true; /// /// Gets the length of the . /// protected override int BodyLength => AutoPadLabelImage ? MaximumLabelLength : LabelImage.Length; /// /// Gets the binary body image of the object. /// protected override byte[] BodyImage => LabelImage; /// /// Gets a of string based property names and values for this object. /// public override Dictionary Attributes { get { Dictionary baseAttributes = base.Attributes; baseAttributes.Add("Label", Label.ToNonNullString("Undefined")); baseAttributes.Add("Index", Index.ToString()); baseAttributes.Add("Offset", Offset.ToString()); baseAttributes.Add("Data Format", $"{(int)DataFormat}: {DataFormat}"); baseAttributes.Add("Conversion Factor", ConversionFactor.ToString()); baseAttributes.Add("Scaling Value", ScalingValue.ToString()); baseAttributes.Add("Scale per Bit", ScalePerBit.ToString()); baseAttributes.Add("Maximum Scaling Value", MaximumScalingValue.ToString()); baseAttributes.Add("Maximum Label Length", MaximumLabelLength.ToString()); return baseAttributes; } } #endregion #region [ Methods ] /// /// Parses the binary body image. /// /// Binary image to parse. /// Start index into to begin parsing. /// Length of valid data within . /// The length of the data that was parsed. /// /// The base implementation assumes that all channel definitions begin with a label as this is /// the general case, override functionality if this is not the case. /// protected override int ParseBodyImage(byte[] buffer, int startIndex, int length) { // Length is validated at a frame level well in advance so that low level parsing routines do not have // to re-validate that enough length is available to parse needed information as an optimization... Label = Encoding.ASCII.GetString(buffer, startIndex, MaximumLabelLength); return MaximumLabelLength; } /// /// Returns a value indicating whether this instance is equal to a specified object. /// /// An object to compare, or null. /// /// True if obj is an instance of and equals the value of this instance; /// otherwise, False. /// /// value is not an . public override bool Equals(object obj) { return Equals(obj as IChannelDefinition); } /// /// Returns a value indicating whether this instance is equal to specified value. /// /// A object to compare to this instance. /// /// True if has the same value as this instance; otherwise, False. /// public bool Equals(IChannelDefinition other) { return CompareTo(other) == 0; } /// /// Compares this instance to a specified object and returns an indication of their relative values. /// /// An object to compare, or null. /// /// A signed number indicating the relative values of this instance and value. Returns less than zero /// if this instance is less than value, zero if this instance is equal to value, or greater than zero /// if this instance is greater than value. /// /// value is not an . public virtual int CompareTo(object obj) { if (obj is IChannelDefinition other) return CompareTo(other); throw new ArgumentException("ChannelDefinition can only be compared to other IChannelDefinitions"); } /// /// Compares this instance to a specified object and returns an indication of their /// relative values. /// /// A object to compare. /// /// A signed number indicating the relative values of this instance and value. Returns less than zero /// if this instance is less than value, zero if this instance is equal to value, or greater than zero /// if this instance is greater than value. /// public int CompareTo(IChannelDefinition other) { // We sort channel Definitions by index return m_index.CompareTo(other.Index); } /// /// Returns the hash code for this instance. /// /// A 32-bit signed integer hash code. public override int GetHashCode() { // ReSharper disable once NonReadonlyMemberInGetHashCode return m_index.GetHashCode(); } /// /// Gets the string representation of this . /// /// String representation of this . public override string ToString() { return Label; } /// /// Populates a with the data needed to serialize the target object. /// /// The to populate with data. /// The destination for this serialization. public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { // Serialize channel definition info.AddValue("parent", m_parent, typeof(IConfigurationCell)); info.AddValue("index", m_index); info.AddValue("label", m_label); info.AddValue("scale", m_scale); info.AddValue("offset", m_offset); } #endregion } }