//****************************************************************************************************** // DataCellBase.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.Runtime.Serialization; using GSF.TimeSeries; namespace GSF.PhasorProtocols { #region [ Enumerations ] /// /// Protocol independent common status flags enumeration. /// /// /// /// These flags are expected to exist in the high-word of a double-word flag set such that original word flags remain in-tact /// in low-word of double-word flag set. /// /// /// Note that value being stored in a historian using a 32-bit single precision floating number value will only have castable /// conversion accuracy back to an unsigned 32-bit integer up to the 24th bit, so any bits used beyond Bit23 will be lossy. /// /// [Flags, Serializable] public enum CommonStatusFlags : uint { /// /// Data was modified by a post-processing device, e.g., a PDC (0 for no modifications, 1 when data modified). /// DataModified = (uint)Bits.Bit21, /// /// Data was discarded from real-time stream due to late arrival (0 when not discarded, 1 when discarded). /// DataDiscarded = (uint)Bits.Bit20, /// /// Data is valid (0 when device data is valid, 1 when invalid or device is in test mode). /// DataIsValid = (uint)Bits.Bit19, /// /// Synchronization is valid (0 when in device is in sync, 1 when it is not). /// SynchronizationIsValid = (uint)Bits.Bit18, /// /// Data sorting type, 0 by timestamp, 1 by arrival. /// DataSortingType = (uint)Bits.Bit17, /// /// Device error (including configuration error), 0 when no error. /// DeviceError = (uint)Bits.Bit16, /// /// Reserved bits for future common flags, presently set to 0. /// ReservedFlags = (uint)(Bits.Bit22 | Bits.Bit23 | Bits.Bit24 | Bits.Bit25 | Bits.Bit26 | Bits.Bit27 | Bits.Bit28 | Bits.Bit29 | Bits.Bit30 | Bits.Bit31), /// /// No flags. /// NoFlags = (uint)Bits.Nil } #endregion /// /// Represents the protocol independent common implementation of all elements for cells in a . /// [Serializable] public abstract class DataCellBase : ChannelCellBase, IDataCell { #region [ Members ] // Fields private IConfigurationCell m_configurationCell; private ushort m_statusFlags; private bool m_statusAssigned; private readonly PhasorValueCollection m_phasorValues; private IFrequencyValue m_frequencyValue; private readonly AnalogValueCollection m_analogValues; private readonly DigitalValueCollection m_digitalValues; private bool m_isDiscarded; #endregion #region [ Constructors ] /// /// Creates a new from specified parameters. /// /// The reference to parent of this . /// The associated with this . /// The default status flags to apply to this . /// Sets the maximum number of phasors for the collection. /// Sets the maximum number of phasors for the collection. /// Sets the maximum number of phasors for the collection. protected DataCellBase(IDataFrame parent, IConfigurationCell configurationCell, ushort statusFlags, int maximumPhasors, int maximumAnalogs, int maximumDigitals) : base(parent, 0) { m_configurationCell = configurationCell; m_statusFlags = statusFlags; m_phasorValues = new PhasorValueCollection(maximumPhasors); m_analogValues = new AnalogValueCollection(maximumAnalogs); m_digitalValues = new DigitalValueCollection(maximumDigitals); } /// /// Creates a new from serialization parameters. /// /// The with populated with data. /// The source for this deserialization. protected DataCellBase(SerializationInfo info, StreamingContext context) : base(info, context) { // Deserialize data cell values m_configurationCell = (IConfigurationCell)info.GetValue("configurationCell", typeof(IConfigurationCell)); m_statusFlags = info.GetUInt16("statusFlags"); m_phasorValues = (PhasorValueCollection)info.GetValue("phasorValues", typeof(PhasorValueCollection)); m_frequencyValue = (IFrequencyValue)info.GetValue("frequencyValue", typeof(IFrequencyValue)); m_analogValues = (AnalogValueCollection)info.GetValue("analogValues", typeof(AnalogValueCollection)); m_digitalValues = (DigitalValueCollection)info.GetValue("digitalValues", typeof(DigitalValueCollection)); m_statusAssigned = true; } #endregion #region [ Properties ] /// /// Gets or sets the reference to parent of this . /// public new virtual IDataFrame Parent { get => base.Parent as IDataFrame; set => base.Parent = value; } /// /// Gets or sets the associated with this . /// public virtual IConfigurationCell ConfigurationCell { get => m_configurationCell; set => m_configurationCell = value; } /// /// Gets or sets the parsing state for the this . /// public new virtual IDataCellParsingState State { get => base.State as IDataCellParsingState; set => base.State = value; } /// /// Gets station name of this . /// public virtual string StationName => m_configurationCell is null ? "" : m_configurationCell.StationName.ToNonNullString(); /// /// Gets ID label of this . /// public virtual string IDLabel => m_configurationCell is null ? "" : m_configurationCell.IDLabel.ToNonNullString(); /// /// Gets or sets 16-bit status flags of this . /// public virtual ushort StatusFlags { get => m_statusFlags; set { m_statusFlags = value; m_statusAssigned = true; } } /// /// Gets the numeric ID code for this . /// /// /// This value is read-only for ; assigning a value will throw an exception. Value returned /// is the of the associated . /// /// IDCode of a data cell is read-only, change IDCode is associated configuration cell instead. public override ushort IDCode { get => m_configurationCell?.IDCode ?? 0; set => throw new NotSupportedException("IDCode of a data cell is read-only, change IDCode is associated configuration cell instead"); } /// /// Gets or sets common status flags of this . /// public uint CommonStatusFlags { get { // Start with lo-word protocol specific flags uint commonFlags = StatusFlags; // Add hi-word protocol independent common flags if (!DataIsValid) commonFlags |= (uint)PhasorProtocols.CommonStatusFlags.DataIsValid; if (!SynchronizationIsValid) commonFlags |= (uint)PhasorProtocols.CommonStatusFlags.SynchronizationIsValid; if (DataSortingType != DataSortingType.ByTimestamp) commonFlags |= (uint)PhasorProtocols.CommonStatusFlags.DataSortingType; if (DeviceError) commonFlags |= (uint)PhasorProtocols.CommonStatusFlags.DeviceError; if (m_isDiscarded) commonFlags |= (uint)PhasorProtocols.CommonStatusFlags.DataDiscarded; if (DataModified) commonFlags |= (uint)PhasorProtocols.CommonStatusFlags.DataModified; return commonFlags; } set { // Deriving common states requires clearing of base status flags... if (value != uint.MaxValue) StatusFlags = 0; // Derive common states via common status flags DataIsValid = (value & (uint)PhasorProtocols.CommonStatusFlags.DataIsValid) == 0; SynchronizationIsValid = (value & (uint)PhasorProtocols.CommonStatusFlags.SynchronizationIsValid) == 0; DataSortingType = (value & (uint)PhasorProtocols.CommonStatusFlags.DataSortingType) == 0 ? DataSortingType.ByTimestamp : DataSortingType.ByArrival; DeviceError = (value & (uint)PhasorProtocols.CommonStatusFlags.DeviceError) > 0; m_isDiscarded = (value & (uint)PhasorProtocols.CommonStatusFlags.DataDiscarded) > 0; DataModified = (value & (uint)PhasorProtocols.CommonStatusFlags.DataModified) > 0; } } /// /// Gets flag that determines if all values of this have been assigned. /// public virtual bool AllValuesAssigned => m_statusAssigned && PhasorValues.AllValuesAssigned && !FrequencyValue.IsEmpty && AnalogValues.AllValuesAssigned && DigitalValues.AllValuesAssigned; /// /// Gets of this . /// public virtual PhasorValueCollection PhasorValues => m_phasorValues; /// /// Gets of this . /// public virtual IFrequencyValue FrequencyValue { get => m_frequencyValue; set => m_frequencyValue = value; } /// /// Gets of this . /// public virtual AnalogValueCollection AnalogValues => m_analogValues; /// /// Gets of this . /// public virtual DigitalValueCollection DigitalValues => m_digitalValues; /// /// Gets or sets flag that determines if data of this is valid. /// /// /// This value is used to abstractly assign the protocol independent set of . /// public abstract bool DataIsValid { get; set; } /// /// Gets or sets flag that determines if timestamp of this is valid based on GPS lock. /// /// /// This value is used to abstractly assign the protocol independent set of . /// public abstract bool SynchronizationIsValid { get; set; } /// /// Gets or sets of this . /// /// /// This value is used to abstractly assign the protocol independent set of . /// public abstract DataSortingType DataSortingType { get; set; } /// /// Gets or sets flag that determines if source device of this is reporting an error. /// /// /// This value is used to abstractly assign the protocol independent set of . /// public abstract bool DeviceError { get; set; } /// /// Gets or sets flag that determines if data is modified by a post-processing /// device, such as a PDC. /// /// /// This value is used to abstractly assign the protocol independent set of . /// public virtual bool DataModified { get; set; } /// /// Gets the length of the . /// protected override int BodyLength => 2 + m_phasorValues.BinaryLength + m_frequencyValue.BinaryLength + m_analogValues.BinaryLength + m_digitalValues.BinaryLength; /// /// Gets the binary body image of the object. /// protected override byte[] BodyImage { get { byte[] buffer = new byte[BodyLength]; int index = 0; // Copy in common cell image BigEndian.CopyBytes(m_statusFlags, buffer, index); index += 2; m_phasorValues.CopyImage(buffer, ref index); m_frequencyValue.CopyImage(buffer, ref index); m_analogValues.CopyImage(buffer, ref index); m_digitalValues.CopyImage(buffer, ref index); return buffer; } } /// /// of string based property names and values for the object. /// public override Dictionary Attributes { get { Dictionary baseAttributes = base.Attributes; byte[] valueBytes = BitConverter.GetBytes(StatusFlags); baseAttributes.Add("Station Name", StationName); baseAttributes.Add("ID Label", IDLabel); baseAttributes.Add("Status Flags", StatusFlags.ToString()); baseAttributes.Add("Status Flags (Big Endian Bits)", ByteEncoding.BigEndianBinary.GetString(valueBytes)); baseAttributes.Add("Status Flags (Hexadecimal)", $"0x{ByteEncoding.Hexadecimal.GetString(valueBytes)}"); baseAttributes.Add("Data Is Valid", DataIsValid.ToString()); baseAttributes.Add("Synchronization Is Valid", SynchronizationIsValid.ToString()); baseAttributes.Add("Data Sorting Type", Enum.GetName(typeof(DataSortingType), DataSortingType)); baseAttributes.Add("Device Error", DeviceError.ToString()); baseAttributes.Add("Data Discarded", m_isDiscarded.ToString()); baseAttributes.Add("Data Modified", DataModified.ToString()); baseAttributes.Add("Frequency Value", m_frequencyValue?.Frequency.ToString()); baseAttributes.Add("dF/dt Value", m_frequencyValue?.DfDt.ToString()); baseAttributes.Add("Total Phasor Values", PhasorValues.Count.ToString()); baseAttributes.Add("Total Analog Values", AnalogValues.Count.ToString()); baseAttributes.Add("Total Digital Values", DigitalValues.Count.ToString()); baseAttributes.Add("All Values Assigned", AllValuesAssigned.ToString()); return baseAttributes; } } #endregion #region [ Methods ] /// /// Parses the binary body 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 ParseBodyImage(byte[] buffer, int startIndex, int length) { // Length is validated at a frame level well in advance so that low level parsing routines do not have // to re-validate that enough length is available to parse needed information as an optimization... IDataCellParsingState parsingState = State; IPhasorValue phasorValue; IAnalogValue analogValue; IDigitalValue digitalValue; int x, parsedLength, index = startIndex; StatusFlags = BigEndian.ToUInt16(buffer, startIndex); index += 2; // By the very nature of the major phasor protocols supporting the same order of phasors, frequency, df/dt, analog and digitals // we are able to "automatically" parse this data out in the data cell base class - BEAUTIFUL!!! // Parse out phasor values for (x = 0; x < parsingState.PhasorCount; x++) { phasorValue = parsingState.CreateNewPhasorValue(this, m_configurationCell.PhasorDefinitions[x], buffer, index, out parsedLength); m_phasorValues.Add(phasorValue); index += parsedLength; } // Parse out frequency and dF/dt values m_frequencyValue = parsingState.CreateNewFrequencyValue(this, m_configurationCell.FrequencyDefinition, buffer, index, out parsedLength); index += parsedLength; // Parse out analog values for (x = 0; x < parsingState.AnalogCount; x++) { analogValue = parsingState.CreateNewAnalogValue(this, m_configurationCell.AnalogDefinitions[x], buffer, index, out parsedLength); m_analogValues.Add(analogValue); index += parsedLength; } // Parse out digital values for (x = 0; x < parsingState.DigitalCount; x++) { digitalValue = parsingState.CreateNewDigitalValue(this, m_configurationCell.DigitalDefinitions[x], buffer, index, out parsedLength); m_digitalValues.Add(digitalValue); index += parsedLength; } // Return total parsed length return index - startIndex; } /// /// 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 data cell values info.AddValue("configurationCell", m_configurationCell, typeof(IConfigurationCell)); info.AddValue("statusFlags", m_statusFlags); info.AddValue("phasorValues", m_phasorValues, typeof(PhasorValueCollection)); info.AddValue("frequencyValue", m_frequencyValue, typeof(IFrequencyValue)); info.AddValue("analogValues", m_analogValues, typeof(AnalogValueCollection)); info.AddValue("digitalValues", m_digitalValues, typeof(DigitalValueCollection)); } // Gets the status flags of the data cell as a measurement value. IMeasurement IDataCell.GetStatusFlagsMeasurement() { return new Measurement() { Timestamp = Parent.Timestamp, Value = CommonStatusFlags }; } #endregion } }