//******************************************************************************************************
// ActionAdapterBase.cs - Gbtc
//
// Copyright © 2010, 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 Eclipse Public License -v 1.0 (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/eclipse-1.0.php
//
// 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:
// ----------------------------------------------------------------------------------------------------
// 09/02/2010 - J. Ritchie Carroll
// Generated original version of source code.
// 02/08/2011 - J. Ritchie Carroll
// Added invokable ExamineQueueState method to analyze real-time concentrator frame queue state.
//
//******************************************************************************************************
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
namespace GSF.TimeSeries.Adapters
{
///
/// Represents the base class for action adapters.
///
///
/// This base class acts as a measurement concentrator which time aligns all incoming measurements for proper processing.
/// Derived classes are expected to override to handle time aligned measurements
/// and call for any new measurements that may get created.
///
public abstract class ActionAdapterBase : ConcentratorBase, IActionAdapter
{
#region [ Members ]
// Events
///
/// Provides status messages to consumer.
///
///
/// is new status message.
///
public event EventHandler> StatusMessage;
///
/// Event is raised when are updated.
///
public event EventHandler InputMeasurementKeysUpdated;
///
/// Event is raised when are updated.
///
public event EventHandler OutputMeasurementsUpdated;
///
/// Provides new measurements from action adapter.
///
///
/// is a collection of new measurements for host to process.
///
public event EventHandler>> NewMeasurements;
// Fields
private string m_name;
private uint m_id;
private string m_connectionString;
private IAdapterCollection m_parent;
private Dictionary m_settings;
private DataSet m_dataSource;
private int m_initializationTimeout;
private bool m_autoStart;
private bool m_respectInputDemands;
private bool m_respectOutputDemands;
private bool m_processMeasurementFilter;
private MeasurementKey[] m_inputMeasurementKeys;
private List m_inputMeasurementKeysHash;
private IMeasurement[] m_outputMeasurements;
private MeasurementKey[] m_requestedInputMeasurementKeys;
private MeasurementKey[] m_requestedOutputMeasurementKeys;
private List m_inputSourceIDs;
private List m_outputSourceIDs;
private int m_minimumMeasurementsToUse;
private ManualResetEvent m_initializeWaitHandle;
private DateTime m_startTimeConstraint;
private DateTime m_stopTimeConstraint;
private int m_hashCode;
private bool m_initialized;
private bool m_disposed;
#endregion
#region [ Constructors ]
///
/// Creates a new .
///
protected ActionAdapterBase()
{
m_name = this.GetType().Name;
m_settings = new Dictionary();
m_startTimeConstraint = DateTime.MinValue;
m_stopTimeConstraint = DateTime.MaxValue;
GenHashCode();
// Create wait handle to use for adapter initialization
m_initializeWaitHandle = new ManualResetEvent(false);
m_initializationTimeout = AdapterBase.DefaultInitializationTimeout;
// For most implementations millisecond resolution will be sufficient
base.TimeResolution = Ticks.PerMillisecond;
}
#endregion
#region [ Properties ]
///
/// Gets or sets key/value pair connection information specific to action adapter.
///
///
///
/// Example connection string using manually defined measurements:
/// framesPerSecond=30; lagTime=1.0; leadTime=0.5; minimumMeasurementsToUse=-1;
/// useLocalClockAsRealTime=true; allowSortsByArrival=false;
/// inputMeasurementKeys={P1:1245;P1:1247;P2:1335};
/// outputMeasurements={P3:1345,60.0,1.0;P3:1346;P3:1347}
/// When defined manually, elements in key:
/// * inputMeasurementKeys are defined as "ArchiveSource:PointID"
/// * outputMeasurements are defined as "ArchiveSource:PointID,Adder,Multiplier", the adder and multiplier are optional
/// defaulting to 0.0 and 1.0 respectively.
///
///
///
/// Example connection string using measurements defined in a table:
/// framesPerSecond=30; lagTime=1.0; leadTime=0.5; minimumMeasurementsToUse=-1;
/// useLocalClockAsRealTime=true; allowSortsByArrival=false;
/// inputMeasurementKeys={FILTER ActiveMeasurements WHERE Company='GSF' AND SignalType='FREQ' ORDER BY ID};
/// outputMeasurements={FILTER ActiveMeasurements WHERE SignalType IN ('IPHA','VPHA') AND Phase='+'}
///
/// Basic filtering syntax is as follows:
///
/// {FILTER <TableName> WHERE <Expression> [ORDER BY <SortField>]}
///
/// Source tables are expected to have at least the following fields:
///
///
/// Name
/// Type
/// Description.
///
/// -
/// ID
/// NVARCHAR
/// Measurement key formatted as: ArchiveSource:PointID.
///
/// -
/// PointTag
/// NVARCHAR
/// Point tag of measurement.
///
/// -
/// Adder
/// FLOAT
/// Adder to apply to value, if any (default to 0.0).
///
/// -
/// Multiplier
/// FLOAT
/// Multipler to apply to value, if any (default to 1.0).
///
///
///
///
/// Note that framesPerSecond, lagTime and leadTime are required parameters, all other parameters are optional.
///
///
public virtual string ConnectionString
{
get
{
return m_connectionString;
}
set
{
m_connectionString = value;
// Preparse settings upon connection string assignment
if (m_connectionString.IsNullOrWhiteSpace())
m_settings = new Dictionary();
else
m_settings = m_connectionString.ParseKeyValuePairs();
}
}
///
/// Gets a read-only reference to the collection that contains this .
///
public ReadOnlyCollection Parent
{
get
{
return new ReadOnlyCollection(m_parent);
}
}
///
/// Gets or sets based data source available to this .
///
public virtual DataSet DataSource
{
get
{
return m_dataSource;
}
set
{
m_dataSource = value;
}
}
///
/// Gets or sets maximum time system will wait during for initialization.
///
///
/// Set to to wait indefinitely.
///
public virtual int InitializationTimeout
{
get
{
return m_initializationTimeout;
}
set
{
m_initializationTimeout = value;
}
}
///
/// Gets or sets flag indicating if adapter should automatically start or otherwise connect on demand.
///
public virtual bool AutoStart
{
get
{
return m_autoStart;
}
set
{
m_autoStart = value;
}
}
///
/// Gets or sets flag indicating if action adapter should respect auto-start requests based on input demands.
///
///
/// Action adapters are in the curious position of being able to both consume and produce points, as such the user needs to be able to control how their
/// adapter will behave concerning routing demands when the adapter is setup to connect on demand. In the case of respecting auto-start input demands,
/// as an example, this would be false for an action adapter that calculated measurement, but true for an action adapter used to archive inputs.
///
public virtual bool RespectInputDemands
{
get
{
return m_respectInputDemands;
}
set
{
m_respectInputDemands = value;
}
}
///
/// Gets or sets flag indicating if action adapter should respect auto-start requests based on output demands.
///
///
/// Action adapters are in the curious position of being able to both consume and produce points, as such the user needs to be able to control how their
/// adapter will behave concerning routing demands when the adapter is setup to connect on demand. In the case of respecting auto-start output demands,
/// as an example, this would be true for an action adapter that calculated measurement, but false for an action adapter used to archive inputs.
///
public virtual bool RespectOutputDemands
{
get
{
return m_respectOutputDemands;
}
set
{
m_respectOutputDemands = value;
}
}
///
/// Gets or sets flag that determines if measurements being queued for processing should be tested to see if they are in the .
///
public virtual bool ProcessMeasurementFilter
{
get
{
return m_processMeasurementFilter;
}
set
{
m_processMeasurementFilter = value;
}
}
///
/// Gets or sets the number of frames per second.
///
///
/// Valid frame rates for a are greater than 0 frames per second.
///
[ConnectionStringParameter,
Description("Defines the number of frames per second expected by the adapter.")]
public new int FramesPerSecond
{
get
{
return base.FramesPerSecond;
}
set
{
base.FramesPerSecond = value;
}
}
///
/// Gets or sets the allowed past time deviation tolerance, in seconds (can be subsecond).
///
///
/// Defines the time sensitivity to past measurement timestamps.
/// The number of seconds allowed before assuming a measurement timestamp is too old.
/// This becomes the amount of delay introduced by the concentrator to allow time for data to flow into the system.
///
/// LagTime must be greater than zero, but it can be less than one.
[ConnectionStringParameter,
Description("Defines the allowed past time deviation tolerance, in seconds (can be subsecond).")]
public new double LagTime
{
get
{
return base.LagTime;
}
set
{
base.LagTime = value;
}
}
///
/// Gets or sets the allowed future time deviation tolerance, in seconds (can be subsecond).
///
///
/// Defines the time sensitivity to future measurement timestamps.
/// The number of seconds allowed before assuming a measurement timestamp is too advanced.
/// This becomes the tolerated +/- accuracy of the local clock to real-time.
///
/// LeadTime must be greater than zero, but it can be less than one.
[ConnectionStringParameter,
Description("Defines the allowed future time deviation tolerance, in seconds (can be subsecond).")]
public new double LeadTime
{
get
{
return base.LeadTime;
}
set
{
base.LeadTime = value;
}
}
///
/// Gets or sets primary keys of input measurements the action adapter expects.
///
///
/// If your adapter needs to receive all measurements, you must explicitly set InputMeasurementKeys to null.
///
[ConnectionStringParameter,
DefaultValue(null),
Description("Defines primary keys of input measurements the action adapter expects; can be one of a filter expression, measurement key, point tag or Guid.")]
public virtual MeasurementKey[] InputMeasurementKeys
{
get
{
return m_inputMeasurementKeys;
}
set
{
m_inputMeasurementKeys = value;
// Update input key lookup hash table
if (value != null && value.Length > 0)
{
m_inputMeasurementKeysHash = new List(value);
m_inputMeasurementKeysHash.Sort();
// The input measurements typically define the "expected measurements" of the action adapter, so
// we use the number of these items to define the expected measurement count
ExpectedMeasurements = m_inputMeasurementKeys.Length;
}
else
{
m_inputMeasurementKeysHash = null;
ExpectedMeasurements = 0;
}
OnInputMeasurementKeysUpdated();
}
}
///
/// Gets or sets output measurements that the action adapter will produce, if any.
///
[ConnectionStringParameter,
DefaultValue(null),
Description("Defines primary keys of output measurements the action adapter expects; can be one of a filter expression, measurement key, point tag or Guid.")]
public virtual IMeasurement[] OutputMeasurements
{
get
{
return m_outputMeasurements;
}
set
{
m_outputMeasurements = value;
OnOutputMeasurementsUpdated();
}
}
///
/// Gets or sets values used to filter input measurement keys.
///
///
/// This allows an adapter to associate itself with entire collections of measurements based on the source of the measurement keys.
/// Set to null apply no filter.
///
public virtual string[] InputSourceIDs
{
get
{
if (m_inputSourceIDs == null)
return null;
return m_inputSourceIDs.ToArray();
}
set
{
if (value == null)
{
m_inputSourceIDs = null;
}
else
{
m_inputSourceIDs = new List(value);
m_inputSourceIDs.Sort();
}
// Filter measurements to list of specified source IDs
AdapterBase.LoadInputSourceIDs(this);
}
}
///
/// Gets or sets values used to filter output measurements.
///
///
/// This allows an adapter to associate itself with entire collections of measurements based on the source of the measurement keys.
/// Set to null apply no filter.
///
public virtual string[] OutputSourceIDs
{
get
{
if (m_outputSourceIDs == null)
return null;
return m_outputSourceIDs.ToArray();
}
set
{
if (value == null)
{
m_outputSourceIDs = null;
}
else
{
m_outputSourceIDs = new List(value);
m_outputSourceIDs.Sort();
}
// Filter measurements to list of specified source IDs
AdapterBase.LoadOutputSourceIDs(this);
}
}
///
/// Gets or sets input measurement keys that are requested by other adapters based on what adapter says it can provide.
///
public virtual MeasurementKey[] RequestedInputMeasurementKeys
{
get
{
return m_requestedInputMeasurementKeys;
}
set
{
m_requestedInputMeasurementKeys = value;
}
}
///
/// Gets or sets output measurement keys that are requested by other adapters based on what adapter says it can provide.
///
public virtual MeasurementKey[] RequestedOutputMeasurementKeys
{
get
{
return m_requestedOutputMeasurementKeys;
}
set
{
m_requestedOutputMeasurementKeys = value;
}
}
///
/// Gets the flag indicating if this adapter supports temporal processing.
///
public abstract bool SupportsTemporalProcessing
{
get;
}
///
/// Gets the start time temporal procesing constraint defined by call to .
///
///
/// This value will be when start time constraint is not set - meaning the adapter
/// is processing data in real-time.
///
public virtual DateTime StartTimeConstraint
{
get
{
return m_startTimeConstraint;
}
}
///
/// Gets the stop time temporal processing constraint defined by call to .
///
///
/// This value will be when stop time constraint is not set - meaning the adapter
/// is processing data in real-time.
///
public virtual DateTime StopTimeConstraint
{
get
{
return m_stopTimeConstraint;
}
}
///
/// Gets or sets minimum number of input measurements required for adapter. Set to -1 to require all.
///
public virtual int MinimumMeasurementsToUse
{
get
{
// Default to all measurements if minimum is not specified
if (m_minimumMeasurementsToUse < 1 && InputMeasurementKeys != null)
return InputMeasurementKeys.Length;
return m_minimumMeasurementsToUse;
}
set
{
m_minimumMeasurementsToUse = value;
}
}
///
/// Gets name of the action adapter.
///
public virtual string Name
{
get
{
return m_name;
}
set
{
m_name = value;
GenHashCode();
}
}
///
/// Gets or sets numeric ID associated with this action adapter.
///
public virtual uint ID
{
get
{
return m_id;
}
set
{
m_id = value;
GenHashCode();
}
}
///
/// Gets or sets flag indicating if the action adapter has been initialized successfully.
///
public virtual bool Initialized
{
get
{
return m_initialized;
}
set
{
m_initialized = value;
// When initialization is complete we send notification
if ((object)m_initializeWaitHandle != null)
{
if (value)
m_initializeWaitHandle.Set();
else
m_initializeWaitHandle.Reset();
}
}
}
///
/// Gets settings parsed when was assigned.
///
public Dictionary Settings
{
get
{
return m_settings;
}
}
///
/// Returns the detailed status of the action adapter.
///
///
/// Derived classes should extend status with implementation specific information.
///
public override string Status
{
get
{
const int MaxMeasurementsToShow = 10;
StringBuilder status = new StringBuilder();
DataSet dataSource = this.DataSource;
status.AppendFormat(" Data source defined: {0}", (dataSource != null));
status.AppendLine();
if (dataSource != null)
{
status.AppendFormat(" Referenced data source: {0}, {1} tables", dataSource.DataSetName, dataSource.Tables.Count);
status.AppendLine();
}
status.AppendFormat(" Initialization timeout: {0}", InitializationTimeout < 0 ? "Infinite" : InitializationTimeout.ToString() + " milliseconds");
status.AppendLine();
status.AppendFormat(" Using measurement routing: {0}", !ProcessMeasurementFilter);
status.AppendLine();
status.AppendFormat(" Adapter initialized: {0}", Initialized);
status.AppendLine();
status.AppendFormat(" Parent collection: {0}", m_parent == null ? "Undefined" : m_parent.Name);
status.AppendLine();
status.AppendFormat(" Operational state: {0}", Enabled ? "Running" : "Stopped");
status.AppendLine();
status.AppendFormat(" Connect on demand: {0}", !AutoStart);
status.AppendLine();
status.AppendFormat(" Respecting input demands: {0}", RespectInputDemands);
status.AppendLine();
status.AppendFormat(" Respecting output demands: {0}", RespectOutputDemands);
status.AppendLine();
status.AppendFormat(" Processed measurements: {0}", ProcessedMeasurements);
status.AppendLine();
status.AppendFormat(" Total adapter run time: {0}", RunTime.ToString());
status.AppendLine();
status.AppendFormat(" Temporal processing: {0}", SupportsTemporalProcessing ? "Supported" : "Unsupported");
status.AppendLine();
if (SupportsTemporalProcessing)
{
status.AppendFormat(" Start time constraint: {0}", StartTimeConstraint == DateTime.MinValue ? "Unspecified" : StartTimeConstraint.ToString("yyyy-MM-dd HH:mm:ss.fff"));
status.AppendLine();
status.AppendFormat(" Stop time constraint: {0}", StopTimeConstraint == DateTime.MaxValue ? "Unspecified" : StopTimeConstraint.ToString("yyyy-MM-dd HH:mm:ss.fff"));
status.AppendLine();
status.AppendFormat(" Processing interval: {0}", ProcessingInterval < 0 ? "Default" : (ProcessingInterval == 0 ? "As fast as possible" : ProcessingInterval + " milliseconds"));
status.AppendLine();
}
status.AppendFormat(" Adpater ID: {0}", ID);
status.AppendLine();
Dictionary keyValuePairs = Settings;
char[] keyChars;
string value;
status.AppendFormat(" Connection string: {0} key/value pairs", keyValuePairs.Count);
// 1 2 3 4 5 6 7
// 123456789012345678901234567890123456789012345678901234567890123456789012345678
// Key = Value
// 1 2 3 4 5
// 12345678901234567890123456789012345678901234567890
status.AppendLine();
status.AppendLine();
foreach (KeyValuePair item in keyValuePairs)
{
keyChars = item.Key.Trim().ToCharArray();
keyChars[0] = char.ToUpper(keyChars[0]);
value = item.Value.Trim();
if (value.Length > 50)
value = value.TruncateRight(47) + "...";
status.AppendFormat("{0} = {1}", (new string(keyChars)).TruncateRight(25).PadLeft(25), value.PadRight(50));
status.AppendLine();
}
status.AppendLine();
if (OutputMeasurements != null && OutputMeasurements.Length > OutputMeasurements.Count(m => m.Key == MeasurementKey.Undefined))
{
status.AppendFormat(" Output measurements: {0} defined measurements", OutputMeasurements.Length);
status.AppendLine();
status.AppendLine();
for (int i = 0; i < Common.Min(OutputMeasurements.Length, MaxMeasurementsToShow); i++)
{
status.Append(OutputMeasurements[i].ToString().TruncateRight(40).PadLeft(40));
status.Append(" ");
status.AppendLine(OutputMeasurements[i].ID.ToString());
}
if (OutputMeasurements.Length > MaxMeasurementsToShow)
status.AppendLine("...".PadLeft(26));
status.AppendLine();
}
if (InputMeasurementKeys != null && InputMeasurementKeys.Length > InputMeasurementKeys.Count(k => k == MeasurementKey.Undefined))
{
status.AppendFormat(" Input measurements: {0} defined measurements", InputMeasurementKeys.Length);
status.AppendLine();
status.AppendLine();
for (int i = 0; i < Common.Min(InputMeasurementKeys.Length, MaxMeasurementsToShow); i++)
{
status.AppendLine(InputMeasurementKeys[i].ToString().TruncateRight(25).CenterText(50));
}
if (InputMeasurementKeys.Length > MaxMeasurementsToShow)
status.AppendLine("...".CenterText(50));
status.AppendLine();
}
if (RequestedInputMeasurementKeys != null && RequestedInputMeasurementKeys.Length > 0)
{
status.AppendFormat(" Requested input keys: {0} defined measurements", RequestedInputMeasurementKeys.Length);
status.AppendLine();
status.AppendLine();
for (int i = 0; i < Common.Min(RequestedInputMeasurementKeys.Length, MaxMeasurementsToShow); i++)
{
status.AppendLine(RequestedInputMeasurementKeys[i].ToString().TruncateRight(25).CenterText(50));
}
if (RequestedInputMeasurementKeys.Length > MaxMeasurementsToShow)
status.AppendLine("...".CenterText(50));
status.AppendLine();
}
if (RequestedOutputMeasurementKeys != null && RequestedOutputMeasurementKeys.Length > 0)
{
status.AppendFormat(" Requested output keys: {0} defined measurements", RequestedOutputMeasurementKeys.Length);
status.AppendLine();
status.AppendLine();
for (int i = 0; i < Common.Min(RequestedOutputMeasurementKeys.Length, MaxMeasurementsToShow); i++)
{
status.AppendLine(RequestedOutputMeasurementKeys[i].ToString().TruncateRight(25).CenterText(50));
}
if (RequestedOutputMeasurementKeys.Length > MaxMeasurementsToShow)
status.AppendLine("...".CenterText(50));
status.AppendLine();
}
status.AppendFormat(" Minimum measurements used: {0}", MinimumMeasurementsToUse);
status.AppendLine();
status.Append(base.Status);
return status.ToString();
}
}
#endregion
#region [ Methods ]
///
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected override void Dispose(bool disposing)
{
if (!m_disposed)
{
try
{
if (disposing)
{
if ((object)m_initializeWaitHandle != null)
m_initializeWaitHandle.Close();
m_initializeWaitHandle = null;
}
}
finally
{
m_disposed = true; // Prevent duplicate dispose.
base.Dispose(disposing); // Call base class Dispose().
}
}
}
///
/// Initializes .
///
public virtual void Initialize()
{
Initialized = false;
Dictionary settings = Settings;
const string errorMessage = "{0} is missing from Settings - Example: framesPerSecond=30; lagTime=3; leadTime=1";
string setting;
// Load required parameters
if (!settings.TryGetValue("framesPerSecond", out setting))
throw new ArgumentException(string.Format(errorMessage, "framesPerSecond"));
base.FramesPerSecond = int.Parse(setting);
if (!settings.TryGetValue("lagTime", out setting))
throw new ArgumentException(string.Format(errorMessage, "lagTime"));
base.LagTime = double.Parse(setting);
if (!settings.TryGetValue("leadTime", out setting))
throw new ArgumentException(string.Format(errorMessage, "leadTime"));
base.LeadTime = double.Parse(setting);
// Load optional parameters
if (settings.TryGetValue("usePrecisionTimer", out setting))
UsePrecisionTimer = setting.ParseBoolean();
if (settings.TryGetValue("useLocalClockAsRealTime", out setting))
UseLocalClockAsRealTime = setting.ParseBoolean();
if (settings.TryGetValue("ignoreBadTimestamps", out setting))
IgnoreBadTimestamps = setting.ParseBoolean();
if (settings.TryGetValue("allowSortsByArrival", out setting))
AllowSortsByArrival = setting.ParseBoolean();
if (settings.TryGetValue("inputMeasurementKeys", out setting))
InputMeasurementKeys = AdapterBase.ParseInputMeasurementKeys(DataSource, setting);
else
InputMeasurementKeys = new MeasurementKey[0];
if (settings.TryGetValue("outputMeasurements", out setting))
OutputMeasurements = AdapterBase.ParseOutputMeasurements(DataSource, setting);
if (settings.TryGetValue("minimumMeasurementsToUse", out setting))
MinimumMeasurementsToUse = int.Parse(setting);
if (settings.TryGetValue("timeResolution", out setting))
TimeResolution = long.Parse(setting);
if (settings.TryGetValue("allowPreemptivePublishing", out setting))
AllowPreemptivePublishing = setting.ParseBoolean();
if (settings.TryGetValue("performTimestampReasonabilityCheck", out setting))
PerformTimestampReasonabilityCheck = setting.ParseBoolean();
if (settings.TryGetValue("processByReceivedTimestamp", out setting))
ProcessByReceivedTimestamp = setting.ParseBoolean();
if (settings.TryGetValue("trackPublishedTimestamp", out setting))
TrackPublishedTimestamp = setting.ParseBoolean();
if (settings.TryGetValue("maximumPublicationTimeout", out setting))
MaximumPublicationTimeout = int.Parse(setting);
if (settings.TryGetValue("downsamplingMethod", out setting))
{
DownsamplingMethod method = DownsamplingMethod.LastReceived;
if (method.TryParse(setting, true, out method))
{
DownsamplingMethod = method;
}
else
{
OnStatusMessage("WARNING: No downsampling method labeled \"{0}\" exists, \"LastReceived\" method was selected.", setting);
DownsamplingMethod = DownsamplingMethod.LastReceived;
}
}
if (settings.TryGetValue("inputSourceIDs", out setting))
InputSourceIDs = setting.Split(',');
if (settings.TryGetValue("outputSourceIDs", out setting))
OutputSourceIDs = setting.Split(',');
if (settings.TryGetValue("connectOnDemand", out setting))
AutoStart = !setting.ParseBoolean();
else
AutoStart = true;
if (settings.TryGetValue("respectInputDemands", out setting))
RespectInputDemands = setting.ParseBoolean();
else
RespectInputDemands = false;
if (settings.TryGetValue("respectOutputDemands", out setting))
RespectOutputDemands = setting.ParseBoolean();
else
RespectOutputDemands = true;
string startTime, stopTime, parameters;
bool startTimeDefined = settings.TryGetValue("startTimeConstraint", out startTime);
bool stopTimeDefined = settings.TryGetValue("stopTimeConstraint", out stopTime);
if (startTimeDefined || stopTimeDefined)
{
settings.TryGetValue("timeConstraintParameters", out parameters);
SetTemporalConstraint(startTime, stopTime, parameters);
}
int processingInterval;
if (settings.TryGetValue("processingInterval", out setting) && !setting.IsNullOrWhiteSpace() && int.TryParse(setting, out processingInterval))
ProcessingInterval = processingInterval;
// // Establish any defined external event wait handles needed for inter-adapter synchronization
// if (settings.TryGetValue("waitHandleNames", out setting) && !setting.IsNullOrWhiteSpace())
// ExternalEventHandles = setting.Split(',').Select(GetExternalEventHandle).ToArray();
int waitHandleTimeout;
if (settings.TryGetValue("waitHandleTimeout", out setting) && !setting.IsNullOrWhiteSpace() && int.TryParse(setting, out waitHandleTimeout))
ExternalEventTimeout = waitHandleTimeout;
}
///
/// Starts the or restarts it if it is already running.
///
[AdapterCommand("Starts the action adapter or restarts it if it is already running.")]
public override void Start()
{
// Make sure we are stopped (e.g., disconnected) before attempting to start (e.g., connect)
if (Enabled)
Stop();
// Wait for adapter intialization to complete...
if (WaitForInitialize(InitializationTimeout))
base.Start();
else
OnProcessException(new TimeoutException("Failed to start action adapter due to timeout waiting for initialization."));
}
///
/// Stops the .
///
[AdapterCommand("Stops the action adapter.")]
public override void Stop()
{
base.Stop();
}
///
/// Examines the concentrator frame queue state of the .
///
[AdapterCommand("Examines concentration frame queue state.")]
public void ExamineQueueState()
{
OnStatusMessage(QueueState);
}
///
/// Assigns the reference to the parent that will contain this .
///
/// Parent adapter collection.
protected virtual void AssignParentCollection(IAdapterCollection parent)
{
m_parent = parent;
}
void IAdapter.AssignParentCollection(IAdapterCollection parent)
{
AssignParentCollection(parent);
}
///
/// Gets a common wait handle for inter-adapter synchronization.
///
/// Case-insensitive wait handle name.
/// A based wait handle associated with the given .
public virtual AutoResetEvent GetExternalEventHandle(string name)
{
return m_parent.GetExternalEventHandle(name);
}
///
/// Resets the statistics of the .
///
[AdapterCommand("Resets the statistics of the action adapter.")]
public override void ResetStatistics()
{
base.ResetStatistics();
if (Enabled)
OnStatusMessage("Action adapter concentration statistics have been reset.");
}
///
/// Manually sets the intialized state of the .
///
/// Desired initialized state.
[AdapterCommand("Manually sets the intialized state of the action adapter.")]
public virtual void SetInitializedState(bool initialized)
{
this.Initialized = initialized;
}
///
/// Gets a short one-line status of this .
///
/// Maximum number of available characters for display.
/// A short one-line summary of the current status of this .
public virtual string GetShortStatus(int maxLength)
{
int inputCount = 0, outputCount = 0;
if (InputMeasurementKeys != null)
inputCount = InputMeasurementKeys.Length;
if (OutputMeasurements != null)
outputCount = OutputMeasurements.Length;
return string.Format("Total input measurements: {0}, total output measurements: {1}", inputCount, outputCount).PadLeft(maxLength);
}
///
/// Queues a single measurement for processing. Measurements is automatically filtered to the defined .
///
/// Measurement to queue for processing.
///
/// Measurement is filtered against the defined .
///
public virtual void QueueMeasurementForProcessing(IMeasurement measurement)
{
if (m_disposed)
return;
// If this is an input measurement to this adapter, sort it!
if (!ProcessMeasurementFilter || IsInputMeasurement(measurement.Key))
SortMeasurement(measurement);
}
///
/// Queues a collection of measurements for processing. Measurements are automatically filtered to the defined .
///
/// Collection of measurements to queue for processing.
///
/// Measurements are filtered against the defined .
///
public virtual void QueueMeasurementsForProcessing(IEnumerable measurements)
{
if (m_disposed)
return;
if (ProcessMeasurementFilter)
{
List inputMeasurements = new List();
foreach (IMeasurement measurement in measurements)
{
if (IsInputMeasurement(measurement.Key))
inputMeasurements.Add(measurement);
}
if (inputMeasurements.Count > 0)
SortMeasurements(inputMeasurements);
}
else
{
SortMeasurements(measurements);
}
}
///
/// Determines if specified measurement key is defined in .
///
/// Primary key of measurement to find.
/// true if specified measurement key is defined in .
public virtual bool IsInputMeasurement(MeasurementKey item)
{
if (m_inputMeasurementKeysHash != null)
return (m_inputMeasurementKeysHash.BinarySearch(item) >= 0);
// If no input measurements are defined we must assume user wants to accept all measurements - yikes!
return true;
}
///
/// Blocks the until the action adapter is .
///
/// The number of milliseconds to wait.
/// true if the initialization succeeds; otherwise, false.
public virtual bool WaitForInitialize(int timeout)
{
if ((object)m_initializeWaitHandle != null)
return m_initializeWaitHandle.WaitOne(timeout);
return false;
}
///
/// Defines a temporal processing constraint for the adapter.
///
/// Defines a relative or exact start time for the temporal constraint.
/// Defines a relative or exact stop time for the temporal constraint.
/// Defines any temporal parameters related to the constraint.
///
///
/// This method defines a temporal processing contraint for an adapter, i.e., the start and stop time over which an
/// adapter will process data. Actual implementation of the constraint will be adapter specific. Implementations
/// should be able to dynamically handle multitple calls to this function with new constraints. Passing in null
/// for the and should cancel the temporal constraint and
/// return the adapter to standard / real-time operation.
///
///
/// The and parameters can be specified in one of the
/// following formats:
///
///
/// Time Format
/// Format Description
///
/// -
/// 12-30-2000 23:59:59.033
/// Absolute date and time.
///
/// -
/// *
/// Evaluates to .
///
/// -
/// *-20s
/// Evaluates to 20 seconds before .
///
/// -
/// *-10m
/// Evaluates to 10 minutes before .
///
/// -
/// *-1h
/// Evaluates to 1 hour before .
///
/// -
/// *-1d
/// Evaluates to 1 day before .
///
///
///
///
[AdapterCommand("Defines a temporal processing constraint for the adapter.")]
public virtual void SetTemporalConstraint(string startTime, string stopTime, string constraintParameters)
{
if (!startTime.IsNullOrWhiteSpace())
m_startTimeConstraint = AdapterBase.ParseTimeTag(startTime);
else
m_startTimeConstraint = DateTime.MinValue;
if (!stopTime.IsNullOrWhiteSpace())
m_stopTimeConstraint = AdapterBase.ParseTimeTag(stopTime);
else
m_stopTimeConstraint = DateTime.MaxValue;
// When processing historical data, timestamps should not be evaluated for reasonability
if (this.TemporalConstraintIsDefined())
{
PerformTimestampReasonabilityCheck = false;
LeadTime = double.MaxValue;
}
}
///
/// Serves as a hash function for the current .
///
/// A hash code for the current .
public override int GetHashCode()
{
return m_hashCode;
}
///
/// Attempts to retrieve the minimum needed number of measurements from the frame (as specified by MinimumMeasurementsToUse)
///
/// Source frame for the measurements
/// Return array of measurements
/// True if minimum needed number of measurements were returned in measurement array
///
///
/// Remember this function will *only* return the minimum needed number of measurements, no more. If you want to use
/// all available measurements in your adapter you should just use Frame.Measurements.Values directly.
///
///
/// Note that the measurements array parameter will be created if the reference is null, otherwise if caller creates
/// array it must be sized to MinimumMeasurementsToUse
///
///
protected virtual bool TryGetMinimumNeededMeasurements(IFrame frame, ref IMeasurement[] measurements)
{
int index = 0, minNeeded = MinimumMeasurementsToUse;
IDictionary frameMeasurements = frame.Measurements;
MeasurementKey[] measurementKeys = InputMeasurementKeys;
if (measurements == null || measurements.Length < minNeeded)
measurements = new IMeasurement[minNeeded];
if (measurementKeys == null)
{
// No input measurements are defined, just get first set of measurements in this frame
foreach (IMeasurement measurement in frameMeasurements.Values)
{
measurements[index++] = measurement;
if (index == minNeeded)
break;
}
}
else
{
// Loop through all input measurements to see if they exist in this frame
IMeasurement measurement;
for (int x = 0; x < measurementKeys.Length; x++)
{
if (frameMeasurements.TryGetValue(measurementKeys[x], out measurement))
{
measurements[index++] = measurement;
if (index == minNeeded)
break;
}
}
}
return (index == minNeeded);
}
///
/// Raises the event.
///
protected virtual void OnNewMeasurements(ICollection measurements)
{
try
{
if (NewMeasurements != null)
NewMeasurements(this, new EventArgs>(measurements));
}
catch (Exception ex)
{
// We protect our code from consumer thrown exceptions
OnProcessException(new InvalidOperationException(string.Format("Exception in consumer handler for NewMeasurements event: {0}", ex.Message), ex));
}
}
///
/// Raises the event.
///
/// New status message.
protected virtual void OnStatusMessage(string status)
{
try
{
if (StatusMessage != null)
StatusMessage(this, new EventArgs(status));
}
catch (Exception ex)
{
// We protect our code from consumer thrown exceptions
OnProcessException(new InvalidOperationException(string.Format("Exception in consumer handler for StatusMessage event: {0}", ex.Message), ex));
}
}
///
/// Raises the event with a formatted status message.
///
/// Formatted status message.
/// Arguments for .
///
/// This overload combines string.Format and SendStatusMessage for convienence.
///
protected virtual void OnStatusMessage(string formattedStatus, params object[] args)
{
try
{
if (StatusMessage != null)
StatusMessage(this, new EventArgs(string.Format(formattedStatus, args)));
}
catch (Exception ex)
{
// We protect our code from consumer thrown exceptions
OnProcessException(new InvalidOperationException(string.Format("Exception in consumer handler for StatusMessage event: {0}", ex.Message), ex));
}
}
///
/// Raises event.
///
protected virtual void OnInputMeasurementKeysUpdated()
{
try
{
if (InputMeasurementKeysUpdated != null)
InputMeasurementKeysUpdated(this, EventArgs.Empty);
}
catch (Exception ex)
{
// We protect our code from consumer thrown exceptions
OnProcessException(new InvalidOperationException(string.Format("Exception in consumer handler for InputMeasurementKeysUpdated event: {0}", ex.Message), ex));
}
}
///
/// Raises event.
///
protected virtual void OnOutputMeasurementsUpdated()
{
try
{
if (OutputMeasurementsUpdated != null)
OutputMeasurementsUpdated(this, EventArgs.Empty);
}
catch (Exception ex)
{
// We protect our code from consumer thrown exceptions
OnProcessException(new InvalidOperationException(string.Format("Exception in consumer handler for OutputMeasurementsUpdated event: {0}", ex.Message), ex));
}
}
private void GenHashCode()
{
// We cache hash code during construction or after element value change to speed usage
m_hashCode = (Name + ID.ToString()).GetHashCode();
}
#endregion
}
}