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