//******************************************************************************************************
// ConfigurationCellBase.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:
// ----------------------------------------------------------------------------------------------------
// 01/14/2005 - J. Ritchie Carroll
// Generated original version of source code.
// 12/17/2012 - Starlynn Danyelle Gilliam
// Modified Header.
//
//******************************************************************************************************
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;
using GSF.Parsing;
using GSF.Units.EE;
// ReSharper disable NonReadonlyMemberInGetHashCode
// ReSharper disable VirtualMemberCallInConstructor
namespace GSF.PhasorProtocols
{
///
/// Represents the protocol independent common implementation of all configuration elements for cells in a .
///
[Serializable]
public abstract class ConfigurationCellBase : ChannelCellBase, IConfigurationCell
{
#region [ Members ]
// Fields
private string m_stationName;
private string m_idLabel;
private readonly PhasorDefinitionCollection m_phasorDefinitions;
private IFrequencyDefinition m_frequencyDefinition;
private readonly AnalogDefinitionCollection m_analogDefinitions;
private readonly DigitalDefinitionCollection m_digitalDefinitions;
private LineFrequency m_nominalFrequency;
private AngleFormat m_angleFormat = AngleFormat.Radians;
private ushort m_revisionCount;
private int m_hashCode;
#endregion
#region [ Constructors ]
///
/// Creates a new from specified parameters.
///
/// The reference to parent of this .
/// The numeric ID code for this .
/// Sets the maximum number of phasors for the collection.
/// Sets the maximum number of phasors for the collection.
/// Sets the maximum number of phasors for the collection.
/// Flag that indicates if collections elements have a fixed size.
protected ConfigurationCellBase(IConfigurationFrame parent, ushort idCode, int maximumPhasors, int maximumAnalogs, int maximumDigitals, bool fixedElementSize = true)
: base(parent, idCode)
{
m_nominalFrequency = LineFrequency.Hz60; // Defaulting to 60Hz
// We convert maximum counts to last valid indices (count - 1):
m_phasorDefinitions = new PhasorDefinitionCollection(maximumPhasors - 1, fixedElementSize);
m_analogDefinitions = new AnalogDefinitionCollection(maximumAnalogs - 1, fixedElementSize);
m_digitalDefinitions = new DigitalDefinitionCollection(maximumDigitals - 1, fixedElementSize);
}
///
/// Creates a new from serialization parameters.
///
/// The with populated with data.
/// The source for this deserialization.
protected ConfigurationCellBase(SerializationInfo info, StreamingContext context)
: base(info, context)
{
// Deserialize configuration cell values
StationName = info.GetString("stationName");
IDLabel = info.GetString("idLabel");
m_phasorDefinitions = (PhasorDefinitionCollection)info.GetValue("phasorDefinitions", typeof(PhasorDefinitionCollection));
m_frequencyDefinition = (IFrequencyDefinition)info.GetValue("frequencyDefinition", typeof(IFrequencyDefinition));
m_analogDefinitions = (AnalogDefinitionCollection)info.GetValue("analogDefinitions", typeof(AnalogDefinitionCollection));
m_digitalDefinitions = (DigitalDefinitionCollection)info.GetValue("digitalDefinitions", typeof(DigitalDefinitionCollection));
m_nominalFrequency = (LineFrequency)info.GetValue("nominalFrequency", typeof(LineFrequency));
m_revisionCount = info.GetUInt16("revisionCount");
}
#endregion
#region [ Properties ]
///
/// Gets or sets a reference to the parent for this .
///
public new virtual IConfigurationFrame Parent
{
get => base.Parent as IConfigurationFrame;
set => base.Parent = value;
}
///
/// Gets or sets the parsing state for the this .
///
public new virtual IConfigurationCellParsingState State
{
get => base.State as IConfigurationCellParsingState;
set => base.State = value;
}
///
/// Gets or sets the station name of this .
///
public virtual string StationName
{
get => m_stationName;
set
{
if (string.IsNullOrEmpty(value))
value = "undefined";
value = value.GetValidLabel();
if (value.Length > MaximumStationNameLength)
throw new OverflowException($"Station name length cannot exceed {MaximumStationNameLength} characters.");
m_stationName = value;
}
}
///
/// Gets the binary image of the of this .
///
public virtual byte[] StationNameImage =>
Encoding.ASCII.GetBytes(MaximumStationNameLength < int.MaxValue && AutoPadStationNameImage ? StationName.PadRight(MaximumStationNameLength): StationName);
///
/// Gets the maximum length of the of this .
///
public virtual int MaximumStationNameLength => 16; // Typical station name length is 16 characters
///
/// Gets flag that indicates if should auto pad-right value to .
///
///
/// Padding will only be applied when is less than .
///
public virtual bool AutoPadStationNameImage => true;
///
/// Gets or sets the ID label of this .
///
public virtual string IDLabel
{
get => m_idLabel;
set
{
if (value is null)
value = "";
value = value.GetValidLabel();
if (value.Length > IDLabelLength)
throw new OverflowException($"ID label length cannot exceed {IDLabelLength} characters.");
m_idLabel = value;
}
}
///
/// Gets the binary image of the of this .
///
public virtual byte[] IDLabelImage =>
Encoding.ASCII.GetBytes(IDLabelLength < int.MaxValue && AutoPadIDLabelImage ? IDLabel.PadRight(IDLabelLength) : IDLabel);
///
/// Gets the length of the of this .
///
public virtual int IDLabelLength => int.MaxValue; // This is not restricted for most protocols
///
/// Gets flag that indicates if should auto pad-right value to .
///
///
/// Padding will only be applied when is less than .
///
public virtual bool AutoPadIDLabelImage => true;
///
/// Gets a reference to the of this .
///
public virtual PhasorDefinitionCollection PhasorDefinitions => m_phasorDefinitions;
///
/// Gets or sets the for the objects in the of this .
///
public abstract DataFormat PhasorDataFormat { get; set; }
///
/// Gets or sets the for the objects in the of this .
///
public abstract CoordinateFormat PhasorCoordinateFormat { get; set; }
///
/// Gets or sets the for the objects in the of this .
///
///
/// Base class defines default angle format since this is rarely not radians.
///
public virtual AngleFormat PhasorAngleFormat
{
get => m_angleFormat;
set => m_angleFormat = value;
}
///
/// Gets or sets the of this .
///
public virtual IFrequencyDefinition FrequencyDefinition
{
get => m_frequencyDefinition;
set => m_frequencyDefinition = value;
}
///
/// Gets or sets the of the of this .
///
public abstract DataFormat FrequencyDataFormat { get; set; }
///
/// Gets or sets the nominal of the of this .
///
public virtual LineFrequency NominalFrequency
{
get => m_nominalFrequency;
set => m_nominalFrequency = value;
}
///
/// Gets a reference to the of this .
///
public virtual AnalogDefinitionCollection AnalogDefinitions => m_analogDefinitions;
///
/// Gets or sets the for the objects in the of this .
///
public abstract DataFormat AnalogDataFormat { get; set; }
///
/// Gets a reference to the of this .
///
public virtual DigitalDefinitionCollection DigitalDefinitions => m_digitalDefinitions;
///
/// Gets the specified frame rate of this .
///
public virtual ushort FrameRate => Parent.FrameRate;
///
/// Gets or sets the revision count of this .
///
public virtual ushort RevisionCount
{
get => m_revisionCount;
set => m_revisionCount = value;
}
///
/// Gets the length of the .
///
///
/// Base implementation provides station name length from the header which is common to configuration frame headers in IEEE protocols.
///
protected override int HeaderLength => MaximumStationNameLength;
///
/// Gets the binary header image of the object.
///
///
/// Base implementation provides station name image from the header which is common to configuration frame headers in IEEE protocols.
///
protected override byte[] HeaderImage => StationNameImage;
///
/// Gets the length of the .
///
///
/// Channel names of IEEE protocol configuration frames are common in order and type so these are defined in the base class.
///
protected override int BodyLength => m_phasorDefinitions.BinaryLength + m_analogDefinitions.BinaryLength + m_digitalDefinitions.BinaryLength;
///
/// Gets the binary body image of the object.
///
///
/// Channel names of IEEE protocol configuration frames are common in order and type so these are defined in the base class.
///
protected override byte[] BodyImage
{
get
{
byte[] buffer = new byte[BodyLength];
int index = 0;
// Copy in common cell images (channel names)
m_phasorDefinitions.CopyImage(buffer, ref index);
m_analogDefinitions.CopyImage(buffer, ref index);
m_digitalDefinitions.CopyImage(buffer, ref index);
return buffer;
}
}
///
/// Gets the length of the .
///
///
/// Bottom of the IEEE protocol configuration frames contain a nominal frequency definition, so base implementation exposes frequency definition as the footer.
///
protected override int FooterLength => 2;
///
/// Gets the binary footer image of the object.
///
///
/// Bottom of the IEEE protocol configuration frames contain a nominal frequency definition, so base implementation exposes frequency definition as the footer.
///
protected override byte[] FooterImage => m_frequencyDefinition.BinaryImage();
///
/// of string based property names and values for the object.
///
public override Dictionary Attributes
{
get
{
Dictionary baseAttributes = base.Attributes;
baseAttributes.Add("Station Name", StationName);
baseAttributes.Add("ID Label", IDLabel);
baseAttributes.Add("Phasor Coordinate Format", $"{(int)PhasorCoordinateFormat}: {PhasorCoordinateFormat}");
baseAttributes.Add("Phasor Data Format", $"{(int)PhasorDataFormat}: {PhasorDataFormat}");
baseAttributes.Add("Phasor Angle Format", $"{(int)PhasorAngleFormat}: {PhasorAngleFormat}");
baseAttributes.Add("Frequency Data Format", $"{(int)FrequencyDataFormat}: {FrequencyDataFormat}");
baseAttributes.Add("Analog Data Format", $"{(int)AnalogDataFormat}: {AnalogDataFormat}");
baseAttributes.Add("Total Phasor Definitions", PhasorDefinitions.Count.ToString());
baseAttributes.Add("Total Analog Definitions", AnalogDefinitions.Count.ToString());
baseAttributes.Add("Total Digital Definitions", DigitalDefinitions.Count.ToString());
baseAttributes.Add("Nominal Frequency", $"{(int)NominalFrequency}Hz");
baseAttributes.Add("Revision Count", RevisionCount.ToString());
baseAttributes.Add("Maximum Station Name Length", MaximumStationNameLength.ToString());
baseAttributes.Add("ID Label Length", IDLabelLength.ToString());
return baseAttributes;
}
}
#endregion
#region [ Methods ]
///
/// Parses the binary header image.
///
/// Binary image to parse.
/// Start index into to begin parsing.
/// Length of valid data within .
/// The length of the data that was parsed.
protected override int ParseHeaderImage(byte[] buffer, int startIndex, int length)
{
// Parse station name from header...
int stationNameLength = MaximumStationNameLength;
StationName = Encoding.ASCII.GetString(buffer, startIndex, stationNameLength);
return stationNameLength;
}
///
/// 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.
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...
IConfigurationCellParsingState parsingState = State;
IPhasorDefinition phasorDefinition;
IAnalogDefinition analogDefinition;
IDigitalDefinition digitalDefinition;
int x, parsedLength, index = startIndex;
// By the very nature of the IEEE protocols supporting the same order of phasor, analog and digital labels
// we are able to "automatically" parse this data out in the configuration cell base class - BEAUTIFUL!!!
// Parse out phasor definitions
for (x = 0; x < parsingState.PhasorCount; x++)
{
phasorDefinition = parsingState.CreateNewPhasorDefinition(this, buffer, index, out parsedLength);
m_phasorDefinitions.Add(phasorDefinition);
index += parsedLength;
}
// Parse out analog definitions
for (x = 0; x < parsingState.AnalogCount; x++)
{
analogDefinition = parsingState.CreateNewAnalogDefinition(this, buffer, index, out parsedLength);
m_analogDefinitions.Add(analogDefinition);
index += parsedLength;
}
// Parse out digital definitions
for (x = 0; x < parsingState.DigitalCount; x++)
{
digitalDefinition = parsingState.CreateNewDigitalDefinition(this, buffer, index, out parsedLength);
m_digitalDefinitions.Add(digitalDefinition);
index += parsedLength;
}
// Return total parsed length
return index - startIndex;
}
///
/// Parses the binary footer image.
///
/// Binary image to parse.
/// Start index into to begin parsing.
/// Length of valid data within .
/// The length of the data that was parsed.
protected override int ParseFooterImage(byte[] buffer, int startIndex, int length)
{
// Parse nominal frequency definition from footer...
m_frequencyDefinition = State.CreateNewFrequencyDefinition(this, buffer, startIndex, out int parsedLength);
return parsedLength;
}
///
/// 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 IConfigurationCell);
}
///
/// 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(IConfigurationCell other)
{
return other is not null && 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 not IConfigurationCell other)
throw new ArgumentException("ConfigurationCell can only be compared to other IConfigurationCells");
return CompareTo(other);
}
///
/// 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 virtual int CompareTo(IConfigurationCell other)
{
// We sort configuration cells by ID code...
return IDCode.CompareTo(other.IDCode);
}
///
/// Returns the hash code for this instance.
///
/// A 32-bit signed integer hash code.
public override int GetHashCode()
{
if (m_hashCode == 0)
m_hashCode = Guid.NewGuid().GetHashCode();
return m_hashCode;
}
///
/// Gets the string representation of this .
///
/// String representation of this .
public override string ToString()
{
string stationName = StationName;
if (!string.IsNullOrWhiteSpace(stationName))
return stationName;
return base.ToString();
}
///
/// Populates a with the data needed to serialize the target object.
///
/// The to populate with data.
/// The destination for this serialization.
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
// Serialize configuration cell values
info.AddValue("stationName", StationName);
info.AddValue("idLabel", IDLabel);
info.AddValue("phasorDefinitions", m_phasorDefinitions, typeof(PhasorDefinitionCollection));
info.AddValue("frequencyDefinition", m_frequencyDefinition, typeof(IFrequencyDefinition));
info.AddValue("analogDefinitions", m_analogDefinitions, typeof(AnalogDefinitionCollection));
info.AddValue("digitalDefinitions", m_digitalDefinitions, typeof(DigitalDefinitionCollection));
info.AddValue("nominalFrequency", m_nominalFrequency, typeof(LineFrequency));
info.AddValue("revisionCount", m_revisionCount);
}
#endregion
}
}