//****************************************************************************************************** // CommonFrameHeader.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. // 08/07/2009 - Josh L. Patterson // Edited Comments. // 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.Runtime.Serialization; using GSF.IO.Checksums; using GSF.Parsing; namespace GSF.PhasorProtocols.IEEE1344 { /// /// Represents the common header for all IEEE 1344 frames of data. /// [Serializable] public class CommonFrameHeader : ICommonHeader, ISerializable { #region [ Members ] // Constants /// /// Total fixed length of . /// public const ushort FixedLength = 6; // Fields private FrameImageCollector m_frameImages; private ushort m_sampleCount; private ushort m_statusFlags; private Ticks m_timestamp; private IChannelParsingState m_state; #endregion #region [ Constructors ] /// /// Creates a new from specified parameters. /// /// The IEEE 1344 specific frame type of this frame. /// The timestamp of this frame. public CommonFrameHeader(FrameType typeID, Ticks timestamp) { m_timestamp = timestamp; TypeID = typeID; FrameCount = 1; IsFirstFrame = true; IsLastFrame = true; } /// /// Creates a new from given . /// /// Buffer that contains data to parse. /// Start index into buffer where valid data begins. public CommonFrameHeader(byte[] buffer, int startIndex) { uint secondOfCentury = BigEndian.ToUInt32(buffer, startIndex); m_sampleCount = BigEndian.ToUInt16(buffer, startIndex + 4); // We go ahead and pre-grab cell's status flags so we can determine frame length - we // leave start index at 6 so that cell will be able to parse flags as needed - note // this increases needed common frame header size by 2 (i.e., BinaryLength + 2) m_statusFlags = BigEndian.ToUInt16(buffer, startIndex + FixedLength); // NTP timestamps based on NtpTimeTag class are designed to work for dates between // 1968-01-20 and 2104-02-26 based on recommended bit interpretation in RFC-2030. NtpTimeTag timetag = new(secondOfCentury, 0); // Cache timestamp value m_timestamp = timetag.ToDateTime().Ticks; } /// /// Creates a new from serialization parameters. /// /// The with populated with data. /// The source for this deserialization. protected CommonFrameHeader(SerializationInfo info, StreamingContext context) { // Deserialize common frame header m_sampleCount = info.GetUInt16("sampleCount"); m_statusFlags = info.GetUInt16("statusFlags"); } #endregion #region [ Properties ] /// /// Gets the timestamp of this frame in NTP format. /// public NtpTimeTag TimeTag => new(m_timestamp); /// /// Gets or sets timestamp of this . /// public Ticks Timestamp { get => m_timestamp; set => m_timestamp = value; } /// /// Gets or sets the IEEE 1344 specific frame type of this frame. /// /// /// /// This returns the protocol specific frame classification which uniquely identifies the frame type. /// /// /// This is the implementation. /// /// public FrameType TypeID { get => (FrameType)(m_sampleCount & Common.FrameTypeMask); set => m_sampleCount = (ushort)((m_sampleCount & ~Common.FrameTypeMask) | (ushort)value); } /// /// Gets or sets flag from that determines if this is the first frame image in a series of images representing an entire frame. /// public bool IsFirstFrame { get => (m_sampleCount & (ushort)Bits.Bit12) == 0; set { if (value) m_sampleCount = (ushort)(m_sampleCount & ~(ushort)Bits.Bit12); else m_sampleCount = (ushort)(m_sampleCount | (ushort)Bits.Bit12); } } /// /// Gets or sets flag that determines if this is the last frame image in a series of images representing an entire frame. /// public bool IsLastFrame { get => (m_sampleCount & (ushort)Bits.Bit11) == 0; set { if (value) m_sampleCount = (ushort)(m_sampleCount & ~(ushort)Bits.Bit11); else m_sampleCount = (ushort)(m_sampleCount | (ushort)Bits.Bit11); } } /// /// Gets or sets the total frame count. /// public ushort FrameCount { get => (ushort)(m_sampleCount & Common.FrameCountMask); set { if (value > Common.MaximumFrameCount) throw new OverflowException($"Frame count value cannot exceed {Common.MaximumFrameCount}"); m_sampleCount = (ushort)((m_sampleCount & ~Common.FrameCountMask) | value); } } /// /// Gets or sets the parsing state for the object. /// public IChannelParsingState State { get => m_state; set => m_state = value; } // Gets or sets any additional state information - satisfies ICommonHeader.State interface property object ICommonHeader.State { get => m_state; set => m_state = value as IChannelParsingState; } /// /// Gets the fundamental frame type of this frame. /// /// /// Frames are generally classified as data, configuration or header frames. This returns the general frame classification. /// public FundamentalFrameType FrameType { get { // Translate IEEE 1344 specific frame type to fundamental frame type return TypeID switch { IEEE1344.FrameType.DataFrame => FundamentalFrameType.DataFrame, IEEE1344.FrameType.ConfigurationFrame => FundamentalFrameType.ConfigurationFrame, IEEE1344.FrameType.HeaderFrame => FundamentalFrameType.HeaderFrame, _ => FundamentalFrameType.Undetermined, }; } } /// /// Gets or sets reference to a associated the IEEE 1344 frame, if any. /// public FrameImageCollector FrameImages { get => m_frameImages; set => m_frameImages = value; } /// /// Gets or sets the entire length of the IEEE 1344 frame. /// public ushort FrameLength { get => (ushort)(m_statusFlags & Common.FrameLengthMask); set { if (value > Common.MaximumFrameLength) throw new OverflowException($"Frame length value cannot exceed {Common.MaximumFrameLength}"); m_statusFlags = (ushort)((m_statusFlags & ~Common.FrameLengthMask) | value); } } /// /// Gets or sets the length of the data in the IEEE 1344 frame (i.e., the minus the header length and checksum: - 8). /// public ushort DataLength { get => // Data length will be frame length minus common header length minus crc16 (ushort)(FrameLength - FixedLength - 2); set { if (value > Common.MaximumDataLength) throw new OverflowException($"Data length value cannot exceed {Common.MaximumDataLength}"); FrameLength = (ushort)(value + FixedLength + 2); } } /// /// Gets or sets the sample number (i.e., frame count) of this frame. /// public ushort SampleCount { get => (ushort)(m_sampleCount & ~Common.FrameTypeMask); set { if (value > Common.MaximumSampleCount) throw new OverflowException($"Sample count value cannot exceed {Common.MaximumSampleCount}"); m_sampleCount = (ushort)((m_sampleCount & Common.FrameTypeMask) | value); } } /// /// Gets the binary image of the common header portion of this frame. /// public byte[] BinaryImage { get { byte[] buffer = new byte[FixedLength]; // Make sure frame length gets included in status flags for generated binary image FrameLength = FixedLength; BigEndian.CopyBytes((uint)TimeTag.Value, buffer, 0); BigEndian.CopyBytes(m_sampleCount, buffer, 4); return buffer; } } #endregion #region [ Methods ] /// /// Appends header specific attributes to dictionary. /// /// Dictionary to append header specific attributes to. internal void AppendHeaderAttributes(Dictionary attributes) { attributes.Add("Frame Type", $"{(ushort)TypeID}: {TypeID}"); if (FrameImages is null) { attributes.Add("Frame Length", FrameLength.ToString()); attributes.Add("Frame Images", "0"); } else { attributes.Add("Frame Length", FrameImages.BinaryLength.ToString()); attributes.Add("Frame Images", FrameImages.Count.ToString()); } attributes.Add("Frame Count", FrameCount.ToString()); attributes.Add("Sample Count", m_sampleCount.ToString()); attributes.Add("Status Flags", m_statusFlags.ToString()); attributes.Add("Is First Frame", IsFirstFrame.ToString()); attributes.Add("Is Last Frame", IsLastFrame.ToString()); } /// /// 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 unique common frame header values info.AddValue("sampleCount", m_sampleCount); info.AddValue("statusFlags", m_statusFlags); } #endregion #region [ Static ] // Static Methods /// /// Validates the CRC-CCITT for the specified IEEE 1344 buffer. /// /// A indicating whether the checksum is valid. /// A array buffer. /// An value as the number of bytes to read for the checksum. /// An value as the start index into being reading the value at. public static bool ChecksumIsValid(byte[] buffer, int startIndex, int length) { int sumLength = length - 2; return BigEndian.ToUInt16(buffer, startIndex + sumLength) == buffer.CrcCCITTChecksum(startIndex, sumLength); } #endregion } }