//******************************************************************************************************
// 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
}
}