//****************************************************************************************************** // 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: // ---------------------------------------------------------------------------------------------------- // 04/26/2009 - 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.Runtime.Serialization; using GSF.Parsing; namespace GSF.PhasorProtocols.SelFastMessage { /// /// Represents the common header for all SEL Fast Message frames of data. /// [Serializable] public class CommonFrameHeader : CommonHeaderBase, ISerializable { #region [ Members ] // Constants /// /// Total fixed length of . /// public const ushort FixedLength = 24; /// /// Total header length of . /// public const ushort HeaderLength = 18; // Fields private FrameSize m_frameSize; private uint m_idCode; private Ticks m_timestamp; #endregion #region [ Constructors ] /// /// Creates a new from specified parameters. /// /// The of this frame. /// The timestamp of this frame. public CommonFrameHeader(FrameSize frameSize, Ticks timestamp) { m_frameSize = frameSize; m_timestamp = timestamp; } /// /// 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) { // Validate SEL Fast Message data image if (buffer[startIndex] != Common.HeaderByte1 || buffer[startIndex + 1] != Common.HeaderByte2) throw new InvalidOperationException($"Bad data stream, expected header bytes 0xA546 as first bytes in SEL Fast Message frame, got 0x{buffer[startIndex].ToString("X").PadLeft(2, '0')}{buffer[startIndex + 1].ToString("X").PadLeft(2, '0')}"); ushort sampleCount; uint secondOfCentury; NtpTimeTag timetag; // Parse relevant common header values m_frameSize = (FrameSize)buffer[startIndex + 2]; m_idCode = BigEndian.ToUInt32(buffer, startIndex + 12); sampleCount = BigEndian.ToUInt16(buffer, startIndex + 18); secondOfCentury = BigEndian.ToUInt32(buffer, startIndex + 20); // We use an NTP time tag since SEL Fast Message SOC also starts at 1/1/1900 timetag = new NtpTimeTag(secondOfCentury, 0); // Data frames have subsecond time information, so we add this fraction of time to current seconds value timetag = new NtpTimeTag(timetag.Value + sampleCount * 50.0M / 1000.0M); // 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_frameSize = (FrameSize)info.GetValue("frameSize", typeof(FrameSize)); m_idCode = info.GetUInt32("idCode32Bit"); } #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 SEL Fast Message destination address (PMADDR setting) of this . /// public uint IDCode { get => m_idCode; set => m_idCode = value; } /// /// Gets the SEL Fast Message second of century of this . /// public uint SecondOfCentury => (uint)Math.Truncate(TimeTag.Value); /// /// Gets the SEL Fast Message sample number of this . /// public ushort SampleNumber => (ushort)(m_timestamp.DistanceBeyondSecond().ToMilliseconds() / 50); /// /// Gets or sets the SEL Fast Message frame size of this . /// public FrameSize FrameSize { get => m_frameSize; set => m_frameSize = value; } /// /// Gets the SEL Fast Message register count of this . /// public ushort RegisterCount => (ushort)(DataLength / 2); /// /// Gets the length of the data in the SEL Fast Message frame (i.e., the minus the and checksum) of this . /// public ushort DataLength => // Data length will be frame length minus common header length minus crc16 (ushort)((ushort)m_frameSize - HeaderLength - 2); /// /// Gets the binary image of the common header portion of this . /// public byte[] BinaryImage { get { byte[] buffer = new byte[FixedLength]; buffer[0] = Common.HeaderByte1; buffer[1] = Common.HeaderByte2; buffer[2] = (byte)m_frameSize; buffer[9] = 0x20; // Function code buffer[10] = 0xC0; // Sequence byte // Include destination address (PMADDR setting) BigEndian.CopyBytes(m_idCode, buffer, 12); // Include register count BigEndian.CopyBytes(RegisterCount, buffer, 16); // Include sample number BigEndian.CopyBytes(SampleNumber, buffer, 18); // Include second of century BigEndian.CopyBytes(SecondOfCentury, buffer, 20); return buffer; } } #endregion #region [ Methods ] /// /// 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("frameSize", m_frameSize, typeof(FrameSize)); info.AddValue("idCode32Bit", m_idCode); } #endregion } }