//****************************************************************************************************** // ConfigurationCell.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: // ---------------------------------------------------------------------------------------------------- // 05/05/2009 - J. Ritchie Carroll // Generated original version of source code. // 05/19/2011 - J. Ritchie Carroll // Removed generic dictionary from serialization since these can't be SOAP serialized. // 12/17/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.Runtime.Serialization; using GSF.TimeSeries; using GSF.Units.EE; namespace GSF.PhasorProtocols.Anonymous { /// /// Represents a protocol independent implementation of a that can be sent or received. /// [Serializable] public class ConfigurationCell : ConfigurationCellBase, IDevice { #region [ Members ] // Fields private DataFormat m_analogDataFormat; private DataFormat m_frequencyDataFormat; private DataFormat m_phasorDataFormat; private CoordinateFormat m_phasorCoordinateFormat; // Add cached signal type and statistical tracking information to our protocol independent configuration cell private readonly string[][] m_generatedSignalReferenceCache; private readonly MeasurementKey[][] m_generatedMeasurementKeyCache; // Keep track of the object that was last used to lookup the MeasurementMetadata. If objects changes, cache must be recomputed. private Dictionary m_keyLookupObject; private Ticks m_lastReportTime; private long m_totalFrames; private long m_dataQualityErrors; private long m_timeQualityErrors; private long m_deviceErrors; private long m_measurementsReceived; private long m_measurementsExpected; private long m_measurementsWithError; private long m_measurementsDefined; private object m_source; #endregion #region [ Constructors ] /// /// Creates a new from specified parameters. /// /// The numeric ID code for this . public ConfigurationCell(ushort idCode) : this(null, idCode) { } /// /// Creates a new from specified parameters. /// /// The reference to parent of this . /// The numeric ID code for this . public ConfigurationCell(ConfigurationFrame parent, ushort idCode) : base(parent, idCode, int.MaxValue, int.MaxValue, int.MaxValue) { // Create a cached signal reference dictionary for generated signal references m_generatedSignalReferenceCache = new string[Enum.GetValues(typeof(SignalKind)).Length][]; m_generatedMeasurementKeyCache = new MeasurementKey[Enum.GetValues(typeof(SignalKind)).Length][]; m_analogDataFormat = DataFormat.FloatingPoint; m_frequencyDataFormat = DataFormat.FloatingPoint; m_phasorDataFormat = DataFormat.FloatingPoint; m_phasorCoordinateFormat = CoordinateFormat.Polar; } /// /// Creates a new from serialization parameters. /// /// The with populated with data. /// The source for this deserialization. protected ConfigurationCell(SerializationInfo info, StreamingContext context) : base(info, context) { // Create a cached signal reference dictionary for generated signal references m_generatedSignalReferenceCache = new string[Enum.GetValues(typeof(SignalKind)).Length][]; m_generatedMeasurementKeyCache = new MeasurementKey[Enum.GetValues(typeof(SignalKind)).Length][]; // Deserialize configuration cell m_lastReportTime = info.GetInt64("lastReportTime"); m_analogDataFormat = (DataFormat)info.GetValue("analogDataFormat", typeof(DataFormat)); m_frequencyDataFormat = (DataFormat)info.GetValue("frequencyDataFormat", typeof(DataFormat)); m_phasorDataFormat = (DataFormat)info.GetValue("phasorDataFormat", typeof(DataFormat)); m_phasorCoordinateFormat = (CoordinateFormat)info.GetValue("phasorCoordinateFormat", typeof(CoordinateFormat)); } #endregion #region [ Properties ] /// /// Gets or sets the for the objects in the of this . /// public override DataFormat AnalogDataFormat { get => m_analogDataFormat; set => m_analogDataFormat = value; } /// /// Gets or sets the of the of this . /// public override DataFormat FrequencyDataFormat { get => m_frequencyDataFormat; set => m_frequencyDataFormat = value; } /// /// Gets or sets the for the objects in the of this . /// public override DataFormat PhasorDataFormat { get => m_phasorDataFormat; set => m_phasorDataFormat = value; } /// /// Gets or sets the for the objects in the of this . /// public override CoordinateFormat PhasorCoordinateFormat { get => m_phasorCoordinateFormat; set => m_phasorCoordinateFormat = value; } /// /// Gets or sets last report time of this . /// public Ticks LastReportTime { get => m_lastReportTime; set => m_lastReportTime = value; } /// /// Gets or sets total frames of this . /// public long TotalFrames { get => m_totalFrames; set => m_totalFrames = value; } /// /// Gets or sets total data quality errors of this . /// public long DataQualityErrors { get => m_dataQualityErrors; set => m_dataQualityErrors = value; } /// /// Gets or sets total time quality errors of this . /// public long TimeQualityErrors { get => m_timeQualityErrors; set => m_timeQualityErrors = value; } /// /// Gets or sets total device errors of this . /// public long DeviceErrors { get => m_deviceErrors; set => m_deviceErrors = value; } /// /// Gets or sets total measurements received for this . /// public long MeasurementsReceived { get => m_measurementsReceived; set => m_measurementsReceived = value; } /// /// Gets or sets total measurements expected to have been received for this . /// public long MeasurementsExpected { get => m_measurementsExpected; set => m_measurementsExpected = value; } /// /// Gets or sets the number of measurements received while this was reporting errors. /// public long MeasurementsWithError { get => m_measurementsWithError; set => m_measurementsWithError = value; } /// /// Gets or sets the number of measurements (per frame) defined for this . /// public long MeasurementsDefined { get => m_measurementsDefined; set => m_measurementsDefined = value; } /// /// Gets or sets the reference to the source of this . /// public object Source { get => m_source; set => m_source = value; } /// /// Gets the maximum length of the of this . /// /// /// This length is not restricted for anonymous protocol definitions. /// public override int MaximumStationNameLength => int.MaxValue; #endregion #region [ Methods ] /// /// Get signal reference for specified . /// /// to request signal reference for. /// Signal reference of given . public string GetSignalReference(SignalKind type) { // We cache non-indexed signal reference strings so they don't need to be generated at each mapping call. // This helps with performance since the mappings for each signal occur 30 times per second. int typeIndex = (int)type; // Look up synonym in dictionary based on signal type, if found return single element string[] references = m_generatedSignalReferenceCache[typeIndex]; if (references is not null) return references[0]; // Create a new signal reference array (for single element) references = new string[1]; // Create and cache new non-indexed signal reference references[0] = SignalReference.ToString(IDLabel, type); // Cache generated signal synonym m_generatedSignalReferenceCache[typeIndex] = references; return references[0]; } /// /// Get signal reference for specified and . /// /// to request signal reference for. /// Index to request signal reference for. /// Number of signals defined for this . /// Signal reference of given and . public string GetSignalReference(SignalKind type, int index, int count) { // We cache indexed signal reference strings so they don't need to be generated at each mapping call. // This helps with performance since the mappings for each signal occur 30 times per second. // For speed purposes we intentionally do not validate that signalIndex falls within signalCount, be // sure calling procedures are very careful with parameters... int typeIndex = (int)type; // Look up synonym in dictionary based on signal type string[] references = m_generatedSignalReferenceCache[typeIndex]; if (references is not null) { // Verify signal count has not changed (we may have received new configuration from device) if (count == references.Length) { // Create and cache new signal reference if it doesn't exist return references[index] ?? (references[index] = SignalReference.ToString(IDLabel, type, index + 1)); } } // Create a new indexed signal reference array references = new string[count]; // Create and cache new signal reference references[index] = SignalReference.ToString(IDLabel, type, index + 1); // Cache generated signal synonym array m_generatedSignalReferenceCache[typeIndex] = references; return references[index]; } /// /// Get for specified . /// /// A lookup table by signal reference. /// to request signal reference for. /// The for a given ; otherwise null if value does not exist. public MeasurementMetadata GetMetadata(Dictionary lookup, SignalKind type) { // Clear the cache if the lookup dictionary has changed. // Since the instance of Lookup is effectively readonly as implemented in PhasorMeasurementMapper // a simple reference check is all that is needed. If it could be modified, this would likely be a // concurrent dictionary instead. if (m_keyLookupObject != lookup) { Array.Clear(m_generatedMeasurementKeyCache, 0, m_generatedMeasurementKeyCache.Length); m_keyLookupObject = lookup; } // Gets the cache for the supplied SignalKind int typeIndex = (int)type; MeasurementKey[] keyArray = m_generatedMeasurementKeyCache[typeIndex]; // If this SignalKind is null, create the sub array and generate all item lookups if (keyArray is null) { keyArray = new MeasurementKey[1]; m_generatedMeasurementKeyCache[typeIndex] = keyArray; string signalReference = GetSignalReference(type); if (lookup.TryGetValue(signalReference, out MeasurementKey key)) keyArray[0] = key; } return keyArray[0]?.Metadata; } /// /// Get for specified . /// /// A lookup table by signal reference. /// to request signal reference for. /// Index to request signal reference for. /// Number of signals defined for this . /// The MeasurementMetadata for a given . Null if it does not exist. public MeasurementMetadata GetMetadata(Dictionary lookup, SignalKind type, int index, int count) { // Clear the cache if the lookup dictionary has changed. // Since the instance of Lookup is effectively readonly as implemented in PhasorMeasurementMapper // a simple reference check is all that is needed. If it could be modified, this would likely be a // concurrent dictionary instead. if (m_keyLookupObject != lookup) { Array.Clear(m_generatedMeasurementKeyCache, 0, m_generatedMeasurementKeyCache.Length); m_keyLookupObject = lookup; } // Gets the cache for the supplied SignalKind int typeIndex = (int)type; MeasurementKey[] keyArray = m_generatedMeasurementKeyCache[typeIndex]; // If this SignalKind is null, create the sub array and generate all item lookups, also, rebuild // if the count is not the same. This could be because a new config frame was received. if (keyArray is null || keyArray.Length != count) { keyArray = new MeasurementKey[count]; m_generatedMeasurementKeyCache[typeIndex] = keyArray; for (int x = 0; x < count; x++) { string signalReference = GetSignalReference(type, x, count); if (lookup.TryGetValue(signalReference, out MeasurementKey key)) keyArray[x] = key; else keyArray[x] = MeasurementKey.Undefined; } } return keyArray[index]?.Metadata; } /// /// 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 configuration cell info.AddValue("lastReportTime", (long)m_lastReportTime); info.AddValue("analogDataFormat", m_analogDataFormat, typeof(DataFormat)); info.AddValue("frequencyDataFormat", m_frequencyDataFormat, typeof(DataFormat)); info.AddValue("phasorDataFormat", m_phasorDataFormat, typeof(DataFormat)); info.AddValue("phasorCoordinateFormat", m_phasorCoordinateFormat, typeof(CoordinateFormat)); } #endregion } }