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