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