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