//******************************************************************************************************// 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{ /// <summary> /// Represents the base class for action adapters. /// </summary> /// <remarks> /// This base class acts as a measurement concentrator which time aligns all incoming measurements for proper processing. /// Derived classes are expected to override <see cref="ConcentratorBase.PublishFrame"/> to handle time aligned measurements /// and call <see cref="OnNewMeasurements"/> for any new measurements that may get created. /// </remarks> public abstract class ActionAdapterBase : ConcentratorBase, IActionAdapter { #region [ Members ] // Events /// <summary> /// Provides status messages to consumer. /// </summary> /// <remarks> /// <see cref="EventArgs{T}.Argument"/> is new status message. /// </remarks> public event EventHandler<EventArgs<string>> StatusMessage; /// <summary> /// Event is raised when <see cref="InputMeasurementKeys"/> are updated. /// </summary> public event EventHandler InputMeasurementKeysUpdated; /// <summary> /// Event is raised when <see cref="OutputMeasurements"/> are updated. /// </summary> public event EventHandler OutputMeasurementsUpdated; /// <summary> /// Provides new measurements from action adapter. /// </summary> /// <remarks> /// <see cref="EventArgs{T}.Argument"/> is a collection of new measurements for host to process. /// </remarks> public event EventHandler<EventArgs<ICollection<IMeasurement>>> NewMeasurements; // Fields private string m_name; private uint m_id; private string m_connectionString; private IAdapterCollection m_parent; private Dictionary<string, string> 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<MeasurementKey> m_inputMeasurementKeysHash; private IMeasurement[] m_outputMeasurements; private MeasurementKey[] m_requestedInputMeasurementKeys; private MeasurementKey[] m_requestedOutputMeasurementKeys; private List<string> m_inputSourceIDs; private List<string> 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 ] /// <summary> /// Creates a new <see cref="ActionAdapterBase"/>. /// </summary> protected ActionAdapterBase() { m_name = this.GetType().Name; m_settings = new Dictionary<string, string>(); 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 ] /// <summary> /// Gets or sets key/value pair connection information specific to action adapter. /// </summary> /// <remarks> /// <para> /// Example connection string using manually defined measurements:<br/> /// <c>framesPerSecond=30; lagTime=1.0; leadTime=0.5; minimumMeasurementsToUse=-1; /// useLocalClockAsRealTime=true; allowSortsByArrival=false;</c><br/> /// <c>inputMeasurementKeys={P1:1245;P1:1247;P2:1335};</c><br/> /// <c>outputMeasurements={P3:1345,60.0,1.0;P3:1346;P3:1347}</c><br/> /// When defined manually, elements in key:<br/> /// * inputMeasurementKeys are defined as "ArchiveSource:PointID"<br/> /// * outputMeasurements are defined as "ArchiveSource:PointID,Adder,Multiplier", the adder and multiplier are optional /// defaulting to 0.0 and 1.0 respectively. /// <br/> /// </para> /// <para> /// Example connection string using measurements defined in a <see cref="DataSource"/> table:<br/> /// <c>framesPerSecond=30; lagTime=1.0; leadTime=0.5; minimumMeasurementsToUse=-1; /// useLocalClockAsRealTime=true; allowSortsByArrival=false;</c><br/> /// <c>inputMeasurementKeys={FILTER ActiveMeasurements WHERE Company='GSF' AND SignalType='FREQ' ORDER BY ID};</c><br/> /// <c>outputMeasurements={FILTER ActiveMeasurements WHERE SignalType IN ('IPHA','VPHA') AND Phase='+'}</c><br/> /// <br/> /// Basic filtering syntax is as follows:<br/> /// <br/> /// {FILTER <TableName> WHERE <Expression> [ORDER BY <SortField>]}<br/> /// <br/> /// Source tables are expected to have at least the following fields:<br/> /// <list type="table"> /// <listheader> /// <term>Name</term> /// <term>Type</term> /// <description>Description.</description> /// </listheader> /// <item> /// <term>ID</term> /// <term>NVARCHAR</term> /// <description>Measurement key formatted as: ArchiveSource:PointID.</description> /// </item> /// <item> /// <term>PointTag</term> /// <term>NVARCHAR</term> /// <description>Point tag of measurement.</description> /// </item> /// <item> /// <term>Adder</term> /// <term>FLOAT</term> /// <description>Adder to apply to value, if any (default to 0.0).</description> /// </item> /// <item> /// <term>Multiplier</term> /// <term>FLOAT</term> /// <description>Multipler to apply to value, if any (default to 1.0).</description> /// </item> /// </list> /// </para> /// <para> /// Note that framesPerSecond, lagTime and leadTime are required parameters, all other parameters are optional. /// </para> /// </remarks> 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<string, string>(); else m_settings = m_connectionString.ParseKeyValuePairs(); } } /// <summary> /// Gets a read-only reference to the collection that contains this <see cref="ActionAdapterBase"/>. /// </summary> public ReadOnlyCollection<IAdapter> Parent { get { return new ReadOnlyCollection<IAdapter>(m_parent); } } /// <summary> /// Gets or sets <see cref="DataSet"/> based data source available to this <see cref="ActionAdapterBase"/>. /// </summary> public virtual DataSet DataSource { get { return m_dataSource; } set { m_dataSource = value; } } /// <summary> /// Gets or sets maximum time system will wait during <see cref="Start"/> for initialization. /// </summary> /// <remarks> /// Set to <see cref="Timeout.Infinite"/> to wait indefinitely. /// </remarks> public virtual int InitializationTimeout { get { return m_initializationTimeout; } set { m_initializationTimeout = value; } } /// <summary> /// Gets or sets flag indicating if adapter should automatically start or otherwise connect on demand. /// </summary> public virtual bool AutoStart { get { return m_autoStart; } set { m_autoStart = value; } } /// <summary> /// Gets or sets flag indicating if action adapter should respect auto-start requests based on input demands. /// </summary> /// <remarks> /// 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 <c>false</c> for an action adapter that calculated measurement, but <c>true</c> for an action adapter used to archive inputs. /// </remarks> public virtual bool RespectInputDemands { get { return m_respectInputDemands; } set { m_respectInputDemands = value; } } /// <summary> /// Gets or sets flag indicating if action adapter should respect auto-start requests based on output demands. /// </summary> /// <remarks> /// 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 <c>true</c> for an action adapter that calculated measurement, but <c>false</c> for an action adapter used to archive inputs. /// </remarks> public virtual bool RespectOutputDemands { get { return m_respectOutputDemands; } set { m_respectOutputDemands = value; } } /// <summary> /// Gets or sets flag that determines if measurements being queued for processing should be tested to see if they are in the <see cref="InputMeasurementKeys"/>. /// </summary> public virtual bool ProcessMeasurementFilter { get { return m_processMeasurementFilter; } set { m_processMeasurementFilter = value; } } /// <summary> /// Gets or sets the number of frames per second. /// </summary> /// <remarks> /// Valid frame rates for a <see cref="ConcentratorBase"/> are greater than 0 frames per second. /// </remarks> [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; } } /// <summary> /// Gets or sets the allowed past time deviation tolerance, in seconds (can be subsecond). /// </summary> /// <remarks> /// <para>Defines the time sensitivity to past measurement timestamps.</para> /// <para>The number of seconds allowed before assuming a measurement timestamp is too old.</para> /// <para>This becomes the amount of delay introduced by the concentrator to allow time for data to flow into the system.</para> /// </remarks> /// <exception cref="ArgumentOutOfRangeException">LagTime must be greater than zero, but it can be less than one.</exception> [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; } } /// <summary> /// Gets or sets the allowed future time deviation tolerance, in seconds (can be subsecond). /// </summary> /// <remarks> /// <para>Defines the time sensitivity to future measurement timestamps.</para> /// <para>The number of seconds allowed before assuming a measurement timestamp is too advanced.</para> /// <para>This becomes the tolerated +/- accuracy of the local clock to real-time.</para> /// </remarks> /// <exception cref="ArgumentOutOfRangeException">LeadTime must be greater than zero, but it can be less than one.</exception> [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; } } /// <summary> /// Gets or sets primary keys of input measurements the action adapter expects. /// </summary> /// <remarks> /// If your adapter needs to receive all measurements, you must explicitly set InputMeasurementKeys to null. /// </remarks> [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<MeasurementKey>(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(); } } /// <summary> /// Gets or sets output measurements that the action adapter will produce, if any. /// </summary> [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(); } } /// <summary> /// Gets or sets <see cref="MeasurementKey.Source"/> values used to filter input measurement keys. /// </summary> /// <remarks> /// This allows an adapter to associate itself with entire collections of measurements based on the source of the measurement keys. /// Set to <c>null</c> apply no filter. /// </remarks> 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<string>(value); m_inputSourceIDs.Sort(); } // Filter measurements to list of specified source IDs AdapterBase.LoadInputSourceIDs(this); } } /// <summary> /// Gets or sets <see cref="MeasurementKey.Source"/> values used to filter output measurements. /// </summary> /// <remarks> /// This allows an adapter to associate itself with entire collections of measurements based on the source of the measurement keys. /// Set to <c>null</c> apply no filter. /// </remarks> public virtual string[] OutputSourceIDs { get { if (m_outputSourceIDs == null) return null; return m_outputSourceIDs.ToArray(); } set { if (value == null) { m_outputSourceIDs = null;Lines 1–500 of 1396. Load more
//****************************************************************************************************** // 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 { /// <summary> /// Represents the base class for action adapters. /// </summary> /// <remarks> /// This base class acts as a measurement concentrator which time aligns all incoming measurements for proper processing. /// Derived classes are expected to override <see cref="ConcentratorBase.PublishFrame"/> to handle time aligned measurements /// and call <see cref="OnNewMeasurements"/> for any new measurements that may get created. /// </remarks> public abstract class ActionAdapterBase : ConcentratorBase, IActionAdapter { #region [ Members ] // Events /// <summary> /// Provides status messages to consumer. /// </summary> /// <remarks> /// <see cref="EventArgs{T}.Argument"/> is new status message. /// </remarks> public event EventHandler<EventArgs<string>> StatusMessage; /// <summary> /// Event is raised when <see cref="InputMeasurementKeys"/> are updated. /// </summary> public event EventHandler InputMeasurementKeysUpdated; /// <summary> /// Event is raised when <see cref="OutputMeasurements"/> are updated. /// </summary> public event EventHandler OutputMeasurementsUpdated; /// <summary> /// Provides new measurements from action adapter. /// </summary> /// <remarks> /// <see cref="EventArgs{T}.Argument"/> is a collection of new measurements for host to process. /// </remarks> public event EventHandler<EventArgs<ICollection<IMeasurement>>> NewMeasurements; // Fields private string m_name; private uint m_id; private string m_connectionString; private IAdapterCollection m_parent; private Dictionary<string, string> 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<MeasurementKey> m_inputMeasurementKeysHash; private IMeasurement[] m_outputMeasurements; private MeasurementKey[] m_requestedInputMeasurementKeys; private MeasurementKey[] m_requestedOutputMeasurementKeys; private List<string> m_inputSourceIDs; private List<string> 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 ] /// <summary> /// Creates a new <see cref="ActionAdapterBase"/>. /// </summary> protected ActionAdapterBase() { m_name = this.GetType().Name; m_settings = new Dictionary<string, string>(); 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 ] /// <summary> /// Gets or sets key/value pair connection information specific to action adapter. /// </summary> /// <remarks> /// <para> /// Example connection string using manually defined measurements:<br/> /// <c>framesPerSecond=30; lagTime=1.0; leadTime=0.5; minimumMeasurementsToUse=-1; /// useLocalClockAsRealTime=true; allowSortsByArrival=false;</c><br/> /// <c>inputMeasurementKeys={P1:1245;P1:1247;P2:1335};</c><br/> /// <c>outputMeasurements={P3:1345,60.0,1.0;P3:1346;P3:1347}</c><br/> /// When defined manually, elements in key:<br/> /// * inputMeasurementKeys are defined as "ArchiveSource:PointID"<br/> /// * outputMeasurements are defined as "ArchiveSource:PointID,Adder,Multiplier", the adder and multiplier are optional /// defaulting to 0.0 and 1.0 respectively. /// <br/> /// </para> /// <para> /// Example connection string using measurements defined in a <see cref="DataSource"/> table:<br/> /// <c>framesPerSecond=30; lagTime=1.0; leadTime=0.5; minimumMeasurementsToUse=-1; /// useLocalClockAsRealTime=true; allowSortsByArrival=false;</c><br/> /// <c>inputMeasurementKeys={FILTER ActiveMeasurements WHERE Company='GSF' AND SignalType='FREQ' ORDER BY ID};</c><br/> /// <c>outputMeasurements={FILTER ActiveMeasurements WHERE SignalType IN ('IPHA','VPHA') AND Phase='+'}</c><br/> /// <br/> /// Basic filtering syntax is as follows:<br/> /// <br/> /// {FILTER <TableName> WHERE <Expression> [ORDER BY <SortField>]}<br/> /// <br/> /// Source tables are expected to have at least the following fields:<br/> /// <list type="table"> /// <listheader> /// <term>Name</term> /// <term>Type</term> /// <description>Description.</description> /// </listheader> /// <item> /// <term>ID</term> /// <term>NVARCHAR</term> /// <description>Measurement key formatted as: ArchiveSource:PointID.</description> /// </item> /// <item> /// <term>PointTag</term> /// <term>NVARCHAR</term> /// <description>Point tag of measurement.</description> /// </item> /// <item> /// <term>Adder</term> /// <term>FLOAT</term> /// <description>Adder to apply to value, if any (default to 0.0).</description> /// </item> /// <item> /// <term>Multiplier</term> /// <term>FLOAT</term> /// <description>Multipler to apply to value, if any (default to 1.0).</description> /// </item> /// </list> /// </para> /// <para> /// Note that framesPerSecond, lagTime and leadTime are required parameters, all other parameters are optional. /// </para> /// </remarks> 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<string, string>(); else m_settings = m_connectionString.ParseKeyValuePairs(); } } /// <summary> /// Gets a read-only reference to the collection that contains this <see cref="ActionAdapterBase"/>. /// </summary> public ReadOnlyCollection<IAdapter> Parent { get { return new ReadOnlyCollection<IAdapter>(m_parent); } } /// <summary> /// Gets or sets <see cref="DataSet"/> based data source available to this <see cref="ActionAdapterBase"/>. /// </summary> public virtual DataSet DataSource { get { return m_dataSource; } set { m_dataSource = value; } } /// <summary> /// Gets or sets maximum time system will wait during <see cref="Start"/> for initialization. /// </summary> /// <remarks> /// Set to <see cref="Timeout.Infinite"/> to wait indefinitely. /// </remarks> public virtual int InitializationTimeout { get { return m_initializationTimeout; } set { m_initializationTimeout = value; } } /// <summary> /// Gets or sets flag indicating if adapter should automatically start or otherwise connect on demand. /// </summary> public virtual bool AutoStart { get { return m_autoStart; } set { m_autoStart = value; } } /// <summary> /// Gets or sets flag indicating if action adapter should respect auto-start requests based on input demands. /// </summary> /// <remarks> /// 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 <c>false</c> for an action adapter that calculated measurement, but <c>true</c> for an action adapter used to archive inputs. /// </remarks> public virtual bool RespectInputDemands { get { return m_respectInputDemands; } set { m_respectInputDemands = value; } } /// <summary> /// Gets or sets flag indicating if action adapter should respect auto-start requests based on output demands. /// </summary> /// <remarks> /// 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 <c>true</c> for an action adapter that calculated measurement, but <c>false</c> for an action adapter used to archive inputs. /// </remarks> public virtual bool RespectOutputDemands { get { return m_respectOutputDemands; } set { m_respectOutputDemands = value; } } /// <summary> /// Gets or sets flag that determines if measurements being queued for processing should be tested to see if they are in the <see cref="InputMeasurementKeys"/>. /// </summary> public virtual bool ProcessMeasurementFilter { get { return m_processMeasurementFilter; } set { m_processMeasurementFilter = value; } } /// <summary> /// Gets or sets the number of frames per second. /// </summary> /// <remarks> /// Valid frame rates for a <see cref="ConcentratorBase"/> are greater than 0 frames per second. /// </remarks> [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; } } /// <summary> /// Gets or sets the allowed past time deviation tolerance, in seconds (can be subsecond). /// </summary> /// <remarks> /// <para>Defines the time sensitivity to past measurement timestamps.</para> /// <para>The number of seconds allowed before assuming a measurement timestamp is too old.</para> /// <para>This becomes the amount of delay introduced by the concentrator to allow time for data to flow into the system.</para> /// </remarks> /// <exception cref="ArgumentOutOfRangeException">LagTime must be greater than zero, but it can be less than one.</exception> [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; } } /// <summary> /// Gets or sets the allowed future time deviation tolerance, in seconds (can be subsecond). /// </summary> /// <remarks> /// <para>Defines the time sensitivity to future measurement timestamps.</para> /// <para>The number of seconds allowed before assuming a measurement timestamp is too advanced.</para> /// <para>This becomes the tolerated +/- accuracy of the local clock to real-time.</para> /// </remarks> /// <exception cref="ArgumentOutOfRangeException">LeadTime must be greater than zero, but it can be less than one.</exception> [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; } } /// <summary> /// Gets or sets primary keys of input measurements the action adapter expects. /// </summary> /// <remarks> /// If your adapter needs to receive all measurements, you must explicitly set InputMeasurementKeys to null. /// </remarks> [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<MeasurementKey>(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(); } } /// <summary> /// Gets or sets output measurements that the action adapter will produce, if any. /// </summary> [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(); } } /// <summary> /// Gets or sets <see cref="MeasurementKey.Source"/> values used to filter input measurement keys. /// </summary> /// <remarks> /// This allows an adapter to associate itself with entire collections of measurements based on the source of the measurement keys. /// Set to <c>null</c> apply no filter. /// </remarks> 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<string>(value); m_inputSourceIDs.Sort(); } // Filter measurements to list of specified source IDs AdapterBase.LoadInputSourceIDs(this); } } /// <summary> /// Gets or sets <see cref="MeasurementKey.Source"/> values used to filter output measurements. /// </summary> /// <remarks> /// This allows an adapter to associate itself with entire collections of measurements based on the source of the measurement keys. /// Set to <c>null</c> apply no filter. /// </remarks> public virtual string[] OutputSourceIDs { get { if (m_outputSourceIDs == null) return null; return m_outputSourceIDs.ToArray(); } set { if (value == null) { m_outputSourceIDs = null;