//****************************************************************************************************** // IndependentAdapterManagerExtensions.cs - Gbtc // // Copyright © 2020, 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://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: // ---------------------------------------------------------------------------------------------------- // 02/13/2020 - J. Ritchie Carroll // Generated original version of source code. // //****************************************************************************************************** using System; using System.Data; using System.Linq; using GSF.Data; using GSF.Data.Model; using GSF.TimeSeries.Data; using GSF.TimeSeries.Model; using DeviceRecord = GSF.TimeSeries.Model.Device; using MeasurementRecord = GSF.TimeSeries.Model.Measurement; using SignalTypeRecord = GSF.TimeSeries.Model.SignalType; using HistorianRecord = GSF.TimeSeries.Model.Historian; using SignalType = GSF.Units.EE.SignalType; namespace GSF.TimeSeries.Adapters { /// /// Represents an adapter base class that provides functionality to manage and distribute measurements to a collection of action adapters. /// public static class IndependentAdapterManagerExtensions { /// /// Defines the default value for the . /// public const int DefaultConfigurationReloadWaitTimeout = 10000; /// /// Defines the default value for the . /// public const int DefaultConfigurationReloadWaitAttempts = 2; /// /// Defines the default value for the . /// public const string DefaultDatabaseConnectionString = ""; /// /// Defines the default value for the . /// public const string DefaultDatabaseProviderString = "AssemblyName={System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}; ConnectionType=System.Data.SqlClient.SqlConnection; AdapterType=System.Data.SqlClient.SqlDataAdapter"; /// /// Defines the default value for the . /// public const string DefaultPointTagTemplate = "IAM!{0}"; /// /// Defines the default value for the . /// public const string DefaultAlternateTagTemplate = ""; /// /// Defines the default value for the . /// public const string DefaultSignalReferenceTemplate = DefaultPointTagTemplate + "-CV"; /// /// Defines the default value for the . /// public const string DefaultDescriptionTemplate = "{0} [{1}] measurement created for {2} [{3}]."; /// /// Defines the default value for the . /// public const string DefaultParentDeviceAcronymTemplate = "IAM!{0}"; /// /// Defines the default value for the . /// public const string DefaultSignalType = "CALC"; /// /// Defines the default value for the . /// public const string DefaultTargetHistorianAcronym = "PPA"; /// /// Defines the default value for the . /// public const string DefaultSourceMeasurementTable = "ActiveMeasurements"; /// /// Finds child adapter with specified . /// /// Target instance. /// Adapter name to find. /// instance with , if found; otherwise, null. public static IAdapter FindAdapter(this IIndependentAdapterManager instance, string adapterName) => instance.FirstOrDefault(adapter => adapterName.Equals(adapter.Name)); /// /// Lookups up point tag name from provided . /// /// Target instance. /// signal ID to lookup. /// Measurement table name used for meta-data lookup. /// Point tag name, if found; otherwise, string representation of provided signal ID. public static string LookupPointTag(this IIndependentAdapterManager instance, Guid signalID, string measurementTable = "ActiveMeasurements") { DataRow record = instance.DataSource.LookupMetadata(signalID, measurementTable); string pointTag = null; if (record != null) pointTag = record["PointTag"].ToString(); if (string.IsNullOrWhiteSpace(pointTag)) pointTag = signalID.ToString(); return pointTag.ToUpper(); } /// /// Lookups up associated device name from provided . /// /// Target instance. /// signal ID to lookup. /// Measurement table name used for meta-data lookup. /// Device name, if found; otherwise, string representation of associated point tag. public static string LookupDevice(this IIndependentAdapterManager instance, Guid signalID, string measurementTable = "ActiveMeasurements") { DataRow record = instance.DataSource.LookupMetadata(signalID, measurementTable); string device = null; if (record != null) device = record["Device"].ToString(); if (string.IsNullOrWhiteSpace(device)) device = instance.LookupPointTag(signalID, measurementTable); return device.ToUpper(); } /// /// Lookups up associated phasor label from provided . /// /// Target instance. /// signal ID to lookup. /// Measurement table name used for meta-data lookup. /// Phasor label name, if found; otherwise, string representation associated point tag. public static string LookupPhasorLabel(this IIndependentAdapterManager instance, Guid signalID, string measurementTable = "ActiveMeasurements") { DataRow record = instance.DataSource.LookupMetadata(signalID, measurementTable); int phasorID = 0; if (record != null) phasorID = record.ConvertNullableField("PhasorID") ?? 0; if (phasorID == 0) return instance.LookupPointTag(signalID, measurementTable); using (AdoDataConnection connection = instance.HandleGetConfiguredConnection()) { TableOperations phasorTable = new TableOperations(connection); Phasor phasorRecord = phasorTable.QueryRecordWhere("ID = {0}", phasorID); return phasorRecord is null ? instance.LookupPointTag(signalID, measurementTable) : phasorRecord.Label.Trim().ToUpper(); } } /// /// Determines if exists in local configuration. /// /// Target instance. /// Signal ID to find. /// Measurement table name used for meta-data lookup. /// true, if is found; otherwise, false. public static bool SignalIDExists(this IIndependentAdapterManager instance, Guid signalID, string measurementTable = "ActiveMeasurements") => instance.DataSource.LookupMetadata(signalID, measurementTable) != null; /// /// Gets measurement record, creating it if needed. /// /// Target instance. /// Device ID associated with current adapter, or zero if none. /// Point tag of measurement. /// Alternate tag of measurement. /// Signal reference of measurement. /// Description of measurement. /// Signal type of measurement. /// Acronym of target historian for measurement. /// Measurement record. public static MeasurementRecord GetMeasurementRecord(this IIndependentAdapterManager instance, int currentDeviceID, string pointTag, string alternateTag, string signalReference, string description, SignalType signalType = SignalType.CALC, string targetHistorianAcronym = "PPA") { // Open database connection as defined in configuration file "systemSettings" category using (AdoDataConnection connection = instance.GetConfiguredConnection()) { TableOperations deviceTable = new TableOperations(connection); TableOperations measurementTable = new TableOperations(connection); TableOperations historianTable = new TableOperations(connection); TableOperations signalTypeTable = new TableOperations(connection); // Lookup target device ID int? deviceID = currentDeviceID > 0 ? currentDeviceID : deviceTable.QueryRecordWhere("Acronym = {0}", instance.Name)?.ID; // Lookup target historian ID int? historianID = historianTable.QueryRecordWhere("Acronym = {0}", targetHistorianAcronym)?.ID; // Lookup signal type ID int signalTypeID = signalTypeTable.QueryRecordWhere("Acronym = {0}", signalType.ToString())?.ID ?? 1; // Lookup measurement record by point tag, creating a new record if one does not exist MeasurementRecord measurement = measurementTable.QueryRecordWhere("SignalReference = {0}", signalReference) ?? measurementTable.NewRecord(); // Update record fields measurement.DeviceID = deviceID; measurement.HistorianID = historianID; measurement.PointTag = pointTag; measurement.AlternateTag = alternateTag; measurement.SignalReference = signalReference; measurement.SignalTypeID = signalTypeID; measurement.Description = description; // Save record updates measurementTable.AddNewOrUpdateRecord(measurement); // Re-query new records to get any database assigned information, e.g., unique Guid-based signal ID if (measurement.PointID == 0) measurement = measurementTable.QueryRecordWhere("SignalReference = {0}", signalReference); // Notify host system of configuration changes instance.OnConfigurationChanged(); return measurement; } } /// /// Waits for to be loaded in system configuration. /// /// Target instance. /// Signal IDs to wait for. /// Measurement table name used for meta-data lookup. public static void WaitForSignalsToLoad(this IIndependentAdapterManager instance, Guid[] signalIDs, string measurementTable = "ActiveMeasurements") { int attempts = 0; bool signalExists(Guid signalID) => instance.SignalIDExists(signalID, measurementTable); while (attempts++ < instance.ConfigurationReloadWaitAttempts) { instance.ConfigurationReloadedWaitHandle.Reset(); if (signalIDs.All(signalExists)) break; instance.ConfigurationReloadedWaitHandle.Wait(instance.ConfigurationReloadWaitTimeout); } } } }