//****************************************************************************************************** // ConfigurationCell.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: // ---------------------------------------------------------------------------------------------------- // 11/12/2004 - J. Ritchie Carroll // Generated original version of source code. // 09/15/2009 - Stephen C. Wills // Added new header and license agreement. // 05/19/2011 - J. Ritchie Carroll // Added DST file support. // 12/17/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.Serialization; using System.Text; using GSF.Units.EE; // ReSharper disable VirtualMemberCallInConstructor namespace GSF.PhasorProtocols.BPAPDCstream { /// /// Represents the BPA PDCstream implementation of a that can be sent or received. /// [Serializable] public class ConfigurationCell : ConfigurationCellBase { #region [ Members ] // Constants private const int FixedBodyLength = 8; // Fields private ConfigurationCell m_configurationFileCell; private string m_sectionEntry; private FormatFlags m_formatFlags; private ushort m_offset; private ushort m_reserved; #endregion #region [ Constructors ] /// /// Creates a new . /// /// The reference to parent of this . public ConfigurationCell(IConfigurationFrame parent) : base(parent, 0, Common.MaximumPhasorValues, Common.MaximumAnalogValues, Common.MaximumDigitalValues) { // Define new parsing state which defines constructors for key configuration values State = new ConfigurationCellParsingState( PhasorDefinition.CreateNewDefinition, BPAPDCstream.FrequencyDefinition.CreateNewDefinition, AnalogDefinition.CreateNewDefinition, DigitalDefinition.CreateNewDefinition); } /// /// Creates a new from specified parameters. /// /// The reference to parent of this . /// The numeric ID code for this . /// The nominal of the of this . public ConfigurationCell(ConfigurationFrame parent, ushort idCode, LineFrequency nominalFrequency = LineFrequency.Hz60) : this(parent) { IDCode = idCode; NominalFrequency = nominalFrequency; } /// /// Creates a new from serialization parameters. /// /// The with populated with data. /// The source for this deserialization. protected ConfigurationCell(SerializationInfo info, StreamingContext context) : base(info, context) { // Deserialize configuration cell m_sectionEntry = info.GetString("sectionEntry"); m_formatFlags = (FormatFlags)info.GetValue("formatFlags", typeof(FormatFlags)); m_offset = info.GetUInt16("offset"); m_reserved = info.GetUInt16("reserved"); } #endregion #region [ Properties ] /// /// Gets a reference to the parent for this . /// public new ConfigurationFrame Parent { get => base.Parent as ConfigurationFrame; set => base.Parent = value; } /// /// Gets or sets reference to the loaded from the configuration file associated this . /// public ConfigurationCell ConfigurationFileCell { get => m_configurationFileCell; set => m_configurationFileCell = value; } /// /// Gets a reference to the of this . /// public override PhasorDefinitionCollection PhasorDefinitions => m_configurationFileCell is null ? base.PhasorDefinitions : m_configurationFileCell.PhasorDefinitions; /// /// Gets or sets the station name of this . /// public override string StationName { get => m_configurationFileCell is null ? base.StationName : m_configurationFileCell.StationName; set { if (m_configurationFileCell is null) base.StationName = value; else m_configurationFileCell.StationName = value; } } /// /// Gets the length of the of this . /// public override int IDLabelLength => 4; // BPA PDCstream ID label length is 4 characters - max! /// /// Gets flag that determines if source data is in the Phasor Data File Format (i.e., a DST file). /// public bool UsePhasorDataFileFormat => Parent.UsePhasorDataFileFormat; /// /// Gets or sets section entry in INI based configuration file for this . /// public string SectionEntry { get => m_configurationFileCell is null ? m_sectionEntry : m_configurationFileCell.SectionEntry; set { m_sectionEntry = value.Trim(); // Get ID label as substring of section entry if (!string.IsNullOrEmpty(m_sectionEntry)) IDLabel = m_sectionEntry.Length > IDLabelLength ? m_sectionEntry.Substring(0, IDLabelLength) : m_sectionEntry; } } /// /// Gets flag that determines if current defines a PDC block section. /// public bool IsPdcBlockSection { get { if (m_configurationFileCell is null) { if (string.IsNullOrEmpty(m_sectionEntry)) return false; return m_sectionEntry.Length > IDLabelLength; } return m_configurationFileCell.IsPdcBlockSection; } } /// /// Gets or sets the numeric ID code for this . /// public override ushort IDCode { get => m_configurationFileCell?.IDCode ?? base.IDCode; set { if (m_configurationFileCell is null) base.IDCode = value; else m_configurationFileCell.IDCode = value; } } /// /// Gets or sets the of this . /// public override IFrequencyDefinition FrequencyDefinition { get => m_configurationFileCell is null ? base.FrequencyDefinition : m_configurationFileCell.FrequencyDefinition; set { if (m_configurationFileCell is null) base.FrequencyDefinition = value; else m_configurationFileCell.FrequencyDefinition = value; } } /// /// Gets or sets the nominal of the of this . /// public override LineFrequency NominalFrequency { get => m_configurationFileCell?.NominalFrequency ?? base.NominalFrequency; set { if (m_configurationFileCell is null) base.NominalFrequency = value; else m_configurationFileCell.NominalFrequency = value; } } /// /// Gets the maximum length of the of this . /// public override int MaximumStationNameLength => int.MaxValue; // The station name in the PDCstream is read from an INI file, so there is no set limit /// /// Gets or sets BPA PDCstream descriptor offset of this in its data packet. /// public ushort Offset { get => m_offset; set => m_offset = value; } /// /// Gets or sets reserved word of this . /// public ushort Reserved { get => m_reserved; set => m_reserved = value; } /// /// Gets or sets format flags of this . /// /// /// These are bit flags, use properties to change basic values. /// [EditorBrowsable(EditorBrowsableState.Advanced)] public FormatFlags FormatFlags { get => m_formatFlags; set => m_formatFlags = value; } /// /// Gets or sets the for the objects in the of this . /// public override DataFormat PhasorDataFormat { get => (m_formatFlags & FormatFlags.Phasors) > 0 ? DataFormat.FloatingPoint : DataFormat.FixedInteger; set { if (value == DataFormat.FloatingPoint) m_formatFlags |= FormatFlags.Phasors; else m_formatFlags &= ~FormatFlags.Phasors; } } /// /// Gets or sets the for the objects in the of this . /// public override CoordinateFormat PhasorCoordinateFormat { get => (m_formatFlags & FormatFlags.Coordinates) > 0 ? CoordinateFormat.Polar : CoordinateFormat.Rectangular; set { if (value == CoordinateFormat.Polar) m_formatFlags |= FormatFlags.Coordinates; else m_formatFlags &= ~FormatFlags.Coordinates; } } /// /// Gets or sets the of the of this . /// public override DataFormat FrequencyDataFormat { get => (m_formatFlags & FormatFlags.Frequency) > 0 ? DataFormat.FloatingPoint : DataFormat.FixedInteger; set { if (value == DataFormat.FloatingPoint) m_formatFlags |= FormatFlags.Frequency; else m_formatFlags &= ~FormatFlags.Frequency; } } /// /// Gets or sets the for the objects in the of this . /// public override DataFormat AnalogDataFormat { get => (m_formatFlags & FormatFlags.Analog) > 0 ? DataFormat.FloatingPoint : DataFormat.FixedInteger; set { if (value == DataFormat.FloatingPoint) m_formatFlags |= FormatFlags.Analog; else m_formatFlags &= ~FormatFlags.Analog; } } // The descriptor cell broadcasted by PDCstream only includes PMUID and offset, all // other metadata is defined in an external INI based configuration file - so we // override the base class image implementations which attempt to generate and // parse data based on a common nature of IEEE configuration frames /// /// Gets the length of the . /// protected override int HeaderLength => 0; /// /// Gets the binary header image of the object. /// protected override byte[] HeaderImage => null; /// /// Gets the length of the . /// protected override int BodyLength => FixedBodyLength; /// /// Gets the binary body image of the object. /// protected override byte[] BodyImage { get { byte[] buffer = new byte[FixedBodyLength]; int index = 0; IDLabelImage.CopyImage(buffer, ref index, IDLabelLength); // PMUID BigEndian.CopyBytes(Reserved, buffer, index); // Reserved BigEndian.CopyBytes(Offset, buffer, index + 2); // Offset return buffer; } } /// /// Gets the length of the . /// protected override int FooterLength => 0; /// /// Gets the binary footer image of the object. /// protected override byte[] FooterImage => null; /// /// of string based property names and values for the object. /// public override Dictionary Attributes { get { Dictionary baseAttributes = base.Attributes; baseAttributes.Add("INI File Section Entry", SectionEntry); baseAttributes.Add("Offset", Offset.ToString()); baseAttributes.Add("Reserved", Reserved.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) { // BPA PDC stream doesn't use standard configuration cell header like IEEE protocols do - so we override this function to do nothing return 0; } /// /// 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) { IDLabel = Encoding.ASCII.GetString(buffer, startIndex, 4); Reserved = BigEndian.ToUInt16(buffer, startIndex + 4); Offset = BigEndian.ToUInt16(buffer, startIndex + 6); return FixedBodyLength; } /// /// 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) { // BPA PDC stream doesn't use standard configuration cell footer like IEEE protocols do - so we override this function to do nothing return 0; } /// /// 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 info.AddValue("sectionEntry", SectionEntry); info.AddValue("formatFlags", m_formatFlags, typeof(FormatFlags)); info.AddValue("offset", m_offset); info.AddValue("reserved", m_reserved); } #endregion #region [ Static ] // Static Methods // Delegate handler to create a new BPA PDCstream configuration cell internal static IConfigurationCell CreateNewCell(IChannelFrame parent, IChannelFrameParsingState state, int index, byte[] buffer, int startIndex, out int parsedLength) { ConfigurationCell configCell = new(parent as IConfigurationFrame); parsedLength = configCell.ParseBinaryImage(buffer, startIndex, 0); return configCell; } #endregion } }