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