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