//****************************************************************************************************** // ConfigurationFrame.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. // 09/15/2009 - Stephen C. Wills // Added new header and license agreement. // 12/17/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.Serialization; using GSF.IO.Checksums; using GSF.Parsing; using GSF.Units.EE; namespace GSF.PhasorProtocols.IEEE1344 { /// /// Represents the IEEE 1344 implementation of a that can be sent or received. /// [Serializable] public class ConfigurationFrame : ConfigurationFrameBase, ISupportSourceIdentifiableFrameImage { #region [ Members ] // Constants private const int FixedFooterLength = 2; // Fields private CommonFrameHeader m_frameHeader; private ulong m_idCode; #endregion #region [ Constructors ] /// /// Creates a new . /// /// /// This constructor is used by to parse an IEEE 1344 configuration frame. /// [EditorBrowsable(EditorBrowsableState.Never)] public ConfigurationFrame() : base(0, new ConfigurationCellCollection(), 0, 0) { } /// /// Creates a new from specified parameters. /// /// The ID code of this . /// The exact timestamp, in , of the data represented by this . /// The defined frame rate of this . /// /// This constructor is used by a consumer to generate an IEEE 1344 configuration frame. /// public ConfigurationFrame(ulong idCode, Ticks timestamp, ushort frameRate) : base(0, new ConfigurationCellCollection(), timestamp, frameRate) { IDCode = idCode; } /// /// Creates a new from serialization parameters. /// /// The with populated with data. /// The source for this deserialization. protected ConfigurationFrame(SerializationInfo info, StreamingContext context) : base(info, context) { // Deserialize configuration frame m_frameHeader = (CommonFrameHeader)info.GetValue("frameHeader", typeof(CommonFrameHeader)); m_idCode = info.GetUInt64("idCode64Bit"); } #endregion #region [ Properties ] /// /// Gets reference to the for this . /// public new ConfigurationCellCollection Cells => base.Cells as ConfigurationCellCollection; /// /// Gets or sets the ID code of this . /// public new ulong IDCode { get => m_idCode; set { m_idCode = value; // Base classes constrain maximum value to 65535 base.IDCode = value > ushort.MaxValue ? ushort.MaxValue : (ushort)value; } } /// /// Gets or sets exact timestamp, in ticks, of the data represented by this . /// /// /// The value of this property represents the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001. /// public override Ticks Timestamp { get => CommonHeader.Timestamp; set { // Keep timestamp updates synchronized... CommonHeader.Timestamp = value; base.Timestamp = value; } } /// /// Gets the timestamp of this frame in NTP format. /// public new NtpTimeTag TimeTag => CommonHeader.TimeTag; /// /// Gets or sets the nominal of this . /// public LineFrequency NominalFrequency { // Since IEEE 1344 only supports a single device there will only be one cell, so we just share nominal frequency with our only child // and expose the value at the parent level for convenience get => Cells[0].NominalFrequency; set => Cells[0].NominalFrequency = value; } /// /// Gets or sets the IEEE 1344 period value. /// public ushort Period { get => (ushort)((double)NominalFrequency / FrameRate * 100.0D); set => FrameRate = (ushort)((double)NominalFrequency * 100.0D / value); } /// /// Gets the identifier that is used to identify the IEEE 1344 frame. /// public FrameType TypeID => IEEE1344.FrameType.ConfigurationFrame; /// /// Gets or sets current . /// public CommonFrameHeader CommonHeader { // Make sure frame header exists - using base class timestamp to // prevent recursion (m_frameHeader doesn't exist yet) get => m_frameHeader ??= new CommonFrameHeader(TypeID, base.Timestamp); set { m_frameHeader = value; if (m_frameHeader is null) return; State = m_frameHeader.State as IConfigurationFrameParsingState; base.Timestamp = m_frameHeader.Timestamp; } } // This interface implementation satisfies ISupportFrameImage.CommonHeader ICommonHeader ISupportFrameImage.CommonHeader { get => CommonHeader; set => CommonHeader = value as CommonFrameHeader; } /// /// Gets the length of the . /// protected override int HeaderLength => CommonFrameHeader.FixedLength; /// /// Gets the binary header image of the object. /// protected override byte[] HeaderImage { get { // Make sure to provide proper frame length for use in the common header image CommonHeader.FrameLength = (ushort)BinaryLength; return CommonHeader.BinaryImage; } } /// /// Gets the length of the . /// protected override int FooterLength => FixedFooterLength; /// /// Gets the binary footer image of the object. /// protected override byte[] FooterImage => BigEndian.GetBytes(Period); /// /// of string based property names and values for the object. /// public override Dictionary Attributes { get { Dictionary baseAttributes = base.Attributes; CommonHeader.AppendHeaderAttributes(baseAttributes); baseAttributes.Add("64-Bit ID Code", IDCode.ToString()); baseAttributes.Add("Period", Period.ToString()); return baseAttributes; } } #endregion #region [ Methods ] /// /// Parses the binary image. /// /// Binary image to parse. /// Start index into to begin parsing. /// Length of valid data within . /// The length of the data that was parsed. /// /// This method is overridden to parse from cumulated frame images. /// public override int ParseBinaryImage(byte[] buffer, int startIndex, int length) { // If frame image collector was used, make sure and parse from entire frame image... if (m_frameHeader is null) return base.ParseBinaryImage(buffer, startIndex, length); // If all configuration frame images have been received, we can safely start parsing if (m_frameHeader.IsLastFrame) { FrameImageCollector frameImages = m_frameHeader.FrameImages; if (frameImages is null) return State.ParsedBinaryLength; // Each individual frame will already have had a CRC check, so we implement standard parse to // bypass ChannelBase CRC frame validation on cumulative frame image buffer = frameImages.BinaryImage; length = frameImages.BinaryLength; startIndex = 0; // Parse out header, body and footer images startIndex += ParseHeaderImage(buffer, startIndex, length); startIndex += ParseBodyImage(buffer, startIndex, length - startIndex); startIndex += ParseFooterImage(buffer, startIndex, length - startIndex); // Include 2 bytes for CRC that was already validated return startIndex + 2; } // There are more configuration frame images coming, keep parser moving by returning total // frame length that was already parsed. return State.ParsedBinaryLength; } /// /// 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) { // We already parsed the frame header, so we just skip past it... return CommonFrameHeader.FixedLength; } /// /// 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) { // Period assignment calculates FrameRate using NominalFrequency which is shared // with first (and only) configuration cell, since cell was added during ParseBodyImage // of ChannelFrameBase, performing the following succeeds since parsing the footer // follows parsing the body :) Period = BigEndian.ToUInt16(buffer, startIndex); return FixedFooterLength; } /// /// Calculates checksum of given . /// /// Buffer image over which to calculate checksum. /// Start index into to calculate checksum. /// Length of data within to calculate checksum. /// Checksum over specified portion of . protected override ushort CalculateChecksum(byte[] buffer, int offset, int length) { // IEEE 1344 uses CRC-CCITT to calculate checksum for frames return buffer.CrcCCITTChecksum(offset, length); } /// /// 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 frame info.AddValue("frameHeader", m_frameHeader, typeof(CommonFrameHeader)); info.AddValue("idCode64Bit", m_idCode); } #endregion } }