/// <summary> /// Initializes <see cref="SynchronizedClientSubscription"/>. /// </summary> public override void Initialize() { MeasurementKey[] inputMeasurementKeys; string setting; if (Settings.TryGetValue("inputMeasurementKeys", out setting)) { // IMPORTANT: The allowSelect argument of ParseInputMeasurementKeys must be null // in order to prevent SQL injection via the subscription filter expression inputMeasurementKeys = AdapterBase.ParseInputMeasurementKeys(DataSource, false, setting); m_requestedInputFilter = setting; // IMPORTANT: We need to remove the setting before calling base.Initialize() // or else we will still be subject to SQL injection Settings.Remove("inputMeasurementKeys"); } else { inputMeasurementKeys = new MeasurementKey[0]; m_requestedInputFilter = null; } base.Initialize(); // Set the InputMeasurementKeys and UsePrecisionTimer properties after calling // base.Initialize() so that the base class does not overwrite our settings InputMeasurementKeys = inputMeasurementKeys; UsePrecisionTimer = false; if (Settings.TryGetValue("bufferBlockRetransmissionTimeout", out setting)) m_bufferBlockRetransmissionTimeout = double.Parse(setting); else m_bufferBlockRetransmissionTimeout = 5.0D; if (Settings.TryGetValue("requestNaNValueFilter", out setting)) m_isNaNFiltered = m_parent.AllowNaNValueFilter && setting.ParseBoolean(); else m_isNaNFiltered = false; m_bufferBlockRetransmissionTimer = new Timer(); m_bufferBlockRetransmissionTimer.AutoReset = false; m_bufferBlockRetransmissionTimer.Interval = m_bufferBlockRetransmissionTimeout * 1000.0D; m_bufferBlockRetransmissionTimer.Elapsed += BufferBlockRetransmissionTimer_Elapsed; // Handle temporal session initialization if (this.TemporalConstraintIsDefined()) m_iaonSession = this.CreateTemporalSession(); }
/// <summary> /// Initializes <see cref="SynchronizedClientSubscription"/>. /// </summary> public override void Initialize() { base.Initialize(); base.UsePrecisionTimer = false; if (!Settings.TryGetValue("inputMeasurementKeys", out m_requestedInputFilter)) m_requestedInputFilter = null; // Handle temporal session intialization if (this.TemporalConstraintIsDefined()) m_iaonSession = this.CreateTemporalSession(); }
/// <summary> /// Initializes <see cref="SynchronizedClientSubscription"/>. /// </summary> public override void Initialize() { string setting; base.Initialize(); base.UsePrecisionTimer = false; Settings.TryGetValue("inputMeasurementKeys", out m_requestedInputFilter); if (Settings.TryGetValue("bufferBlockRetransmissionTimeout", out setting)) m_bufferBlockRetransmissionTimeout = double.Parse(setting); else m_bufferBlockRetransmissionTimeout = 5.0D; if (Settings.TryGetValue("requestNaNValueFiltering", out setting)) m_isNaNFiltered = m_parent.AllowNaNValueFilter && setting.ParseBoolean(); else m_isNaNFiltered = false; m_bufferBlockRetransmissionTimer = new Timer(); m_bufferBlockRetransmissionTimer.AutoReset = false; m_bufferBlockRetransmissionTimer.Interval = m_bufferBlockRetransmissionTimeout * 1000.0D; m_bufferBlockRetransmissionTimer.Elapsed += BufferBlockRetransmissionTimer_Elapsed; // Handle temporal session initialization if (this.TemporalConstraintIsDefined()) m_iaonSession = this.CreateTemporalSession(); }
/// <summary> /// Event handler for service starting operations. /// </summary> /// <param name="sender">Event source.</param> /// <param name="e">Event arguments containing command line arguments passed into service at startup.</param> /// <remarks> /// Time-series framework uses this handler to load settings from configuration file as service is starting. /// </remarks> protected virtual void ServiceStartingHandler(object sender, EventArgs<string[]> e) { ShutdownHandler.Initialize(); // Define a run-time log m_runTimeLog = new RunTimeLog(); m_runTimeLog.FileName = "RunTimeLog.txt"; m_runTimeLog.ProcessException += ProcessExceptionHandler; m_runTimeLog.Initialize(); // Initialize Iaon session m_iaonSession = new IaonSession(); m_iaonSession.StatusMessage += StatusMessageHandler; m_iaonSession.ProcessException += ProcessExceptionHandler; m_iaonSession.ConfigurationChanged += ConfigurationChangedHandler; // Create a handler for unobserved task exceptions TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; // Make sure default service settings exist ConfigurationFile configFile = ConfigurationFile.Current; string servicePath = FilePath.GetAbsolutePath(""); string cachePath = string.Format("{0}{1}ConfigurationCache{1}", servicePath, Path.DirectorySeparatorChar); string defaultLogPath = string.Format("{0}{1}Logs{1}", servicePath, Path.DirectorySeparatorChar); // System settings CategorizedSettingsElementCollection systemSettings = configFile.Settings["systemSettings"]; systemSettings.Add("ConfigurationType", "Database", "Specifies type of configuration: Database, WebService, BinaryFile or XmlFile"); systemSettings.Add("ConnectionString", "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=IaonHost.mdb", "Configuration database connection string"); systemSettings.Add("DataProviderString", "AssemblyName={System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089};ConnectionType=System.Data.OleDb.OleDbConnection;AdapterType=System.Data.OleDb.OleDbDataAdapter", "Configuration database ADO.NET data provider assembly type creation string"); systemSettings.Add("ConfigurationCachePath", cachePath, "Defines the path used to cache serialized configurations"); systemSettings.Add("LogPath", defaultLogPath, "Defines the path used to archive log files"); systemSettings.Add("MaxLogFiles", DefaultMaxLogFiles, "Defines the maximum number of log files to keep"); systemSettings.Add("CachedConfigurationFile", "SystemConfiguration.xml", "File name for last known good system configuration (only cached for a Database or WebService connection)"); systemSettings.Add("UniqueAdaptersIDs", "True", "Set to true if all runtime adapter ID's will be unique to allow for easier adapter specification"); systemSettings.Add("ProcessPriority", "High", "Sets desired process priority: Normal, AboveNormal, High, RealTime"); systemSettings.Add("AllowRemoteRestart", "True", "Controls ability to remotely restart the host service."); systemSettings.Add("MinThreadPoolWorkerThreads", DefaultMinThreadPoolSize, "Defines the minimum number of allowed thread pool worker threads."); systemSettings.Add("MaxThreadPoolWorkerThreads", DefaultMaxThreadPoolSize, "Defines the maximum number of allowed thread pool worker threads."); systemSettings.Add("MinThreadPoolIOPortThreads", DefaultMinThreadPoolSize, "Defines the minimum number of allowed thread pool I/O completion port threads (used by socket layer)."); systemSettings.Add("MaxThreadPoolIOPortThreads", DefaultMaxThreadPoolSize, "Defines the maximum number of allowed thread pool I/O completion port threads (used by socket layer)."); systemSettings.Add("ConfigurationBackups", DefaultConfigurationBackups, "Defines the total number of older backup configurations to maintain."); systemSettings.Add("PreferCachedConfiguration", "False", "Set to true to try the cached configuration first, before loading database configuration - typically used when cache is updated by external process."); systemSettings.Add("LocalCertificate", $"{ServiceName}.cer", "Path to the local certificate used by this server for authentication."); systemSettings.Add("RemoteCertificatesPath", @"Certs\Remotes", "Path to the directory where remote certificates are stored."); systemSettings.Add("DefaultCulture", "en-US", "Default culture to use for language, country/region and calendar formats."); // Example connection settings CategorizedSettingsElementCollection exampleSettings = configFile.Settings["exampleConnectionSettings"]; exampleSettings.Add("SqlServer.ConnectionString", "Data Source=serverName; Initial Catalog=databaseName; User ID=userName; Password=password", "Example SQL Server database connection string"); exampleSettings.Add("SqlServer.DataProviderString", "AssemblyName={System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}; ConnectionType=System.Data.SqlClient.SqlConnection; AdapterType=System.Data.SqlClient.SqlDataAdapter", "Example SQL Server database .NET provider string"); exampleSettings.Add("MySQL.ConnectionString", "Server=serverName;Database=databaseName; Uid=root; Pwd=password; allow user variables = true;", "Example MySQL database connection string"); exampleSettings.Add("MySQL.DataProviderString", "AssemblyName={MySql.Data, Version=6.3.6.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d}; ConnectionType=MySql.Data.MySqlClient.MySqlConnection; AdapterType=MySql.Data.MySqlClient.MySqlDataAdapter", "Example MySQL database .NET provider string"); exampleSettings.Add("Oracle.ConnectionString", "Data Source=tnsName; User ID=schemaUserName; Password=schemaPassword", "Example Oracle database connection string"); exampleSettings.Add("Oracle.DataProviderString", "AssemblyName={Oracle.DataAccess, Version=2.112.2.0, Culture=neutral, PublicKeyToken=89b483f429c47342}; ConnectionType=Oracle.DataAccess.Client.OracleConnection; AdapterType=Oracle.DataAccess.Client.OracleDataAdapter", "Example Oracle database .NET provider string"); exampleSettings.Add("SQLite.ConnectionString", "Data Source=databaseName.db; Version=3; Foreign Keys=True; FailIfMissing=True", "Example SQLite database connection string"); exampleSettings.Add("SQLite.DataProviderString", "AssemblyName={System.Data.SQLite, Version=1.0.99.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139}; ConnectionType=System.Data.SQLite.SQLiteConnection; AdapterType=System.Data.SQLite.SQLiteDataAdapter", "Example SQLite database .NET provider string"); exampleSettings.Add("OleDB.ConnectionString", "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=databaseName.mdb", "Example Microsoft Access (via OleDb) database connection string"); exampleSettings.Add("OleDB.DataProviderString", "AssemblyName={System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}; ConnectionType=System.Data.OleDb.OleDbConnection; AdapterType=System.Data.OleDb.OleDbDataAdapter", "Example OleDb database .NET provider string"); exampleSettings.Add("Odbc.ConnectionString", "Driver={SQL Server Native Client 10.0}; Server=serverName; Database=databaseName; Uid=userName; Pwd=password;", "Example ODBC database connection string"); exampleSettings.Add("Odbc.DataProviderString", "AssemblyName={System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089}; ConnectionType=System.Data.Odbc.OdbcConnection; AdapterType=System.Data.Odbc.OdbcDataAdapter", "Example ODBC database .NET provider string"); exampleSettings.Add("WebService.ConnectionString", "http://localhost/ConfigSource/SystemConfiguration.xml", "Example web service connection string"); exampleSettings.Add("XmlFile.ConnectionString", "SystemConfiguration.xml", "Example XML configuration file connection string"); // Attempt to set default culture try { string defaultCulture = systemSettings["DefaultCulture"].ValueAs("en-US"); CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CreateSpecificCulture(defaultCulture); // Defaults for date formatting, etc. CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.CreateSpecificCulture(defaultCulture); // Culture for resource strings, etc. } catch (Exception ex) { DisplayStatusMessage("Failed to set default culture due to exception, defaulting to \"{1}\": {0}", UpdateType.Alarm, ex.Message, CultureInfo.CurrentCulture.Name.ToNonNullNorEmptyString("Undetermined")); LogException(ex); } // Retrieve application log path as defined in the config file string logPath = FilePath.GetAbsolutePath(systemSettings["LogPath"].Value); // Make sure log directory exists try { if (!Directory.Exists(logPath)) Directory.CreateDirectory(logPath); } catch (Exception ex) { // Attempt to default back to common log file path if (!Directory.Exists(defaultLogPath)) { try { Directory.CreateDirectory(defaultLogPath); } catch { defaultLogPath = servicePath; } } DisplayStatusMessage("Failed to create logging directory \"{0}\" due to exception, defaulting to \"{1}\": {2}", UpdateType.Alarm, logPath, defaultLogPath, ex.Message); LogException(ex); logPath = defaultLogPath; } int maxLogFiles = systemSettings["MaxLogFiles"].ValueAs(DefaultMaxLogFiles); try { Logger.FileWriter.SetPath(logPath); Logger.FileWriter.SetLoggingFileCount(maxLogFiles); } catch (Exception ex) { DisplayStatusMessage("Failed to set logging path \"{0}\" or max file count \"{1}\" due to exception: {2}", UpdateType.Alarm, logPath, maxLogFiles, ex.Message); LogException(ex); } // Retrieve configuration cache directory as defined in the config file cachePath = FilePath.GetAbsolutePath(systemSettings["ConfigurationCachePath"].Value); // Make sure configuration cache directory exists try { if (!Directory.Exists(cachePath)) Directory.CreateDirectory(cachePath); } catch (Exception ex) { DisplayStatusMessage("Failed to create configuration cache directory \"{0}\" due to exception: {1}", UpdateType.Alarm, cachePath, ex.Message); LogException(ex); } try { Directory.SetCurrentDirectory(servicePath); } catch (Exception ex) { DisplayStatusMessage("Failed to set current directory to execution path \"{0}\" due to exception: {1}", UpdateType.Alarm, servicePath, ex.Message); LogException(ex); } // Initialize system settings m_configurationType = systemSettings["ConfigurationType"].ValueAs<ConfigurationType>(); m_cachedXmlConfigurationFile = FilePath.AddPathSuffix(cachePath) + systemSettings["CachedConfigurationFile"].Value; m_cachedBinaryConfigurationFile = FilePath.AddPathSuffix(cachePath) + FilePath.GetFileNameWithoutExtension(m_cachedXmlConfigurationFile) + ".bin"; m_configurationBackups = systemSettings["ConfigurationBackups"].ValueAs(DefaultConfigurationBackups); m_uniqueAdapterIDs = systemSettings["UniqueAdaptersIDs"].ValueAsBoolean(true); m_allowRemoteRestart = systemSettings["AllowRemoteRestart"].ValueAsBoolean(true); m_preferCachedConfiguration = systemSettings["PreferCachedConfiguration"].ValueAsBoolean(false); m_reloadConfigQueue = ProcessQueue<Tuple<string, Action<bool>>>.CreateSynchronousQueue(ExecuteReloadConfig, 500.0D, Timeout.Infinite, false, false); m_reloadConfigQueue.ProcessException += m_iaonSession.ProcessExceptionHandler; m_configurationCacheOperation = new LongSynchronizedOperation(ExecuteConfigurationCache) { IsBackground = true }; // Setup default thread pool size try { ThreadPool.SetMinThreads(systemSettings["MinThreadPoolWorkerThreads"].ValueAs(DefaultMinThreadPoolSize), systemSettings["MinThreadPoolIOPortThreads"].ValueAs(DefaultMinThreadPoolSize)); ThreadPool.SetMaxThreads(systemSettings["MaxThreadPoolWorkerThreads"].ValueAs(DefaultMaxThreadPoolSize), systemSettings["MaxThreadPoolIOPortThreads"].ValueAs(DefaultMaxThreadPoolSize)); } catch (Exception ex) { DisplayStatusMessage("Failed to set desired thread pool size due to exception: {0}", UpdateType.Alarm, ex.Message); LogException(ex); } // Define guid with query string delimiters according to database needs if (string.IsNullOrWhiteSpace(m_nodeIDQueryString)) m_nodeIDQueryString = "'" + m_iaonSession.NodeID + "'"; // Set up the configuration loader switch (m_configurationType) { case ConfigurationType.Database: m_configurationLoader = new DatabaseConfigurationLoader { ConnectionString = systemSettings["ConnectionString"].Value, DataProviderString = systemSettings["DataProviderString"].Value, NodeIDQueryString = m_nodeIDQueryString }; break; case ConfigurationType.WebService: m_configurationLoader = new WebServiceConfigurationLoader { URI = systemSettings["ConnectionString"].Value }; break; case ConfigurationType.BinaryFile: m_configurationLoader = new BinaryFileConfigurationLoader { FilePath = systemSettings["ConnectionString"].Value }; break; case ConfigurationType.XmlFile: m_configurationLoader = new XMLConfigurationLoader { FilePath = systemSettings["ConnectionString"].Value }; break; } m_binaryCacheConfigurationLoader = new BinaryFileConfigurationLoader { FilePath = m_cachedBinaryConfigurationFile }; m_xmlCacheConfigurationLoader = new XMLConfigurationLoader { FilePath = m_cachedXmlConfigurationFile }; m_configurationLoader.StatusMessage += (o, args) => DisplayStatusMessage(args.Argument, UpdateType.Information); m_binaryCacheConfigurationLoader.StatusMessage += (o, args) => DisplayStatusMessage(args.Argument, UpdateType.Information); m_xmlCacheConfigurationLoader.StatusMessage += (o, args) => DisplayStatusMessage(args.Argument, UpdateType.Information); m_configurationLoader.ProcessException += ConfigurationLoader_ProcessException; m_binaryCacheConfigurationLoader.ProcessException += ConfigurationLoader_ProcessException; m_xmlCacheConfigurationLoader.ProcessException += ConfigurationLoader_ProcessException; m_reloadConfigQueue.Start(); #if !MONO try { // Attempt to assign desired process priority. Note that process will require SeIncreaseBasePriorityPrivilege or // Administrative privileges to make this change Process.GetCurrentProcess().PriorityClass = systemSettings["ProcessPriority"].ValueAs<ProcessPriorityClass>(); } catch (Exception ex) { LogException(ex); } #endif }
/// <summary> /// Event handler for service stopping operation. /// </summary> /// <param name="sender">Event source.</param> /// <param name="e">Event arguments.</param> /// <remarks> /// Time-series framework uses this handler to un-wire events and dispose of system objects. /// </remarks> protected virtual void ServiceStoppingHandler(object sender, EventArgs e) { // Dispose system health exporter if ((object)m_healthExporter != null) { m_healthExporter.Enabled = false; m_serviceHelper.ServiceComponents.Remove(m_healthExporter); m_healthExporter.Dispose(); m_healthExporter.StatusMessage -= m_iaonSession.StatusMessageHandler; m_healthExporter.ProcessException -= m_iaonSession.ProcessExceptionHandler; m_healthExporter = null; } // Dispose system status exporter if ((object)m_statusExporter != null) { m_statusExporter.Enabled = false; m_serviceHelper.ServiceComponents.Remove(m_statusExporter); m_statusExporter.Dispose(); m_statusExporter.StatusMessage -= m_iaonSession.StatusMessageHandler; m_statusExporter.ProcessException -= m_iaonSession.ProcessExceptionHandler; m_statusExporter = null; } // Dispose reload config queue if ((object)m_reloadConfigQueue != null) { m_reloadConfigQueue.ProcessException -= m_iaonSession.ProcessExceptionHandler; m_reloadConfigQueue.Dispose(); m_reloadConfigQueue = null; } // Dispose reporting processes if ((object)m_reportingProcesses != null) { m_serviceHelper.ServiceComponents.Remove(m_reportingProcesses); m_reportingProcesses = null; } // Dispose Iaon session if ((object)m_iaonSession != null) { m_serviceHelper.ServiceComponents.Remove(m_iaonSession.InputAdapters); m_serviceHelper.ServiceComponents.Remove(m_iaonSession.ActionAdapters); m_serviceHelper.ServiceComponents.Remove(m_iaonSession.OutputAdapters); m_iaonSession.Dispose(); m_iaonSession.StatusMessage -= StatusMessageHandler; m_iaonSession.ProcessException -= ProcessExceptionHandler; m_iaonSession.ConfigurationChanged -= ConfigurationChangedHandler; m_iaonSession = null; } // Dispose of run-time log if ((object)m_runTimeLog != null) { m_serviceHelper.ServiceComponents.Remove(m_runTimeLog); m_runTimeLog.ProcessException -= ProcessExceptionHandler; m_runTimeLog.Dispose(); m_runTimeLog = null; } m_serviceHelper.ServiceStarting -= ServiceStartingHandler; m_serviceHelper.ServiceStarted -= ServiceStartedHandler; m_serviceHelper.ServiceStopping -= ServiceStoppingHandler; m_serviceHelper.UpdatedStatus -= UpdatedStatusHandler; m_serviceHelper.LoggedException -= LoggedExceptionHandler; if ((object)m_serviceHelper.StatusLog != null) { m_serviceHelper.StatusLog.Flush(); m_serviceHelper.StatusLog.LogException -= LogExceptionHandler; } if ((object)m_serviceHelper.ErrorLogger != null && (object)m_serviceHelper.ErrorLogger.ErrorLog != null) { m_serviceHelper.ErrorLogger.ErrorLog.Flush(); m_serviceHelper.ErrorLogger.ErrorLog.LogException -= LogExceptionHandler; } // Detach from handler for unobserved task exceptions TaskScheduler.UnobservedTaskException -= TaskScheduler_UnobservedTaskException; ShutdownHandler.InitiateSafeShutdown(); }
/// <summary> /// Disposes a temporal <see cref="IaonSession"/> created using <see cref="CreateTemporalSession"/>. /// </summary> /// <param name="adapter"><see cref="IClientSubscription"/> source instance.</param> /// <param name="session"><see cref="IaonSession"/> instance to dispose.</param> public static void DisposeTemporalSession(this IClientSubscription adapter, ref IaonSession session) { if ((object)session != null) { EventHandler<EventArgs<string, UpdateType>> statusMessageFunction; EventHandler<EventArgs<Exception>> processExceptionFunction; EventHandler processingCompletedFunction; // Remove and detach from event handlers if (s_statusMessageHandlers.TryRemove(adapter, out statusMessageFunction)) session.StatusMessage -= statusMessageFunction; if (s_processExceptionHandlers.TryRemove(adapter, out processExceptionFunction)) session.ProcessException -= processExceptionFunction; if (s_processingCompletedHandlers.TryRemove(adapter, out processingCompletedFunction)) session.ProcessingComplete -= processingCompletedFunction; session.Dispose(); } session = null; }
/// <summary> /// Returns a new temporal <see cref="IaonSession"/> for a <see cref="IClientSubscription"/>. /// </summary> /// <param name="clientSubscription"><see cref="IClientSubscription"/> instance to create temporal <see cref="IaonSession"/> for.</param> /// <returns>New temporal <see cref="IaonSession"/> for a <see cref="IClientSubscription"/>.</returns> public static IaonSession CreateTemporalSession(this IClientSubscription clientSubscription) { IaonSession session; // Cache the specified input measurement keys requested by the remote subscription // internally since these will only be needed in the private Iaon session MeasurementKey[] inputMeasurementKeys = clientSubscription.InputMeasurementKeys; IMeasurement[] outputMeasurements = clientSubscription.OutputMeasurements; // Since historical data is requested, we "turn off" interaction with the outside real-time world // by removing the client subscription adapter from external routes. To accomplish this we expose // I/O demands for an undefined measurement as assigning to null would mean "broadcast" is desired. clientSubscription.InputMeasurementKeys = new[] { MeasurementKey.Undefined }; clientSubscription.OutputMeasurements = new IMeasurement[] { Measurement.Undefined }; // Create a new Iaon session session = new IaonSession(); session.Name = "<" + clientSubscription.HostName.ToNonNullString("unavailable") + ">@" + clientSubscription.StartTimeConstraint.ToString("yyyy-MM-dd HH:mm:ss"); // Assign requested input measurement keys as a routing restriction session.InputMeasurementKeysRestriction = inputMeasurementKeys; // Setup default bubbling event handlers associated with the client session adapter EventHandler<EventArgs<string, UpdateType>> statusMessageHandler = (sender, e) => { if (e.Argument2 == UpdateType.Information) clientSubscription.OnStatusMessage(e.Argument1); else clientSubscription.OnStatusMessage("0x" + (int)e.Argument2 + e.Argument1); }; EventHandler<EventArgs<Exception>> processExceptionHandler = (sender, e) => clientSubscription.OnProcessException(e.Argument); EventHandler processingCompletedHandler = clientSubscription.OnProcessingCompleted; // Cache dynamic event handlers so they can be detached later s_statusMessageHandlers[clientSubscription] = statusMessageHandler; s_processExceptionHandlers[clientSubscription] = processExceptionHandler; s_processingCompletedHandlers[clientSubscription] = processingCompletedHandler; // Attach handlers to new session - this will proxy all temporal session messages through the client session adapter session.StatusMessage += statusMessageHandler; session.ProcessException += processExceptionHandler; session.ProcessingComplete += processingCompletedHandler; // Send the first message indicating a new temporal session is being established statusMessageHandler(null, new EventArgs<string, UpdateType>( string.Format("Initializing temporal session for host \"{0}\" spanning {1} to {2} processing data {3}...", clientSubscription.HostName.ToNonNullString("unknown"), clientSubscription.StartTimeConstraint.ToString("yyyy-MM-dd HH:mm:ss.fff"), clientSubscription.StopTimeConstraint.ToString("yyyy-MM-dd HH:mm:ss.fff"), clientSubscription.ProcessingInterval == 0 ? "as fast as possible" : clientSubscription.ProcessingInterval == -1 ? "at the default rate" : "at " + clientSubscription.ProcessingInterval + "ms intervals"), UpdateType.Information)); // Duplicate current real-time session configuration for adapters that report temporal support session.DataSource = IaonSession.ExtractTemporalConfiguration(clientSubscription.DataSource); // Initialize temporal session adapters without starting them session.Initialize(false); // Define an in-situ action adapter for the temporal Iaon session used to proxy data back to the client subscription. Note // to enable adapters that are connect-on-demand in the temporal session, we must make sure the proxy adapter is setup to // respect input demands. The proxy adapter produces no points into the temporal session - all received points are simply // internally proxied out to the parent client subscription outside the purview of the Iaon session; from the perspective // of the Iaon session, points seem to dead-end in the proxy adapter. The proxy adapter is an action adapter and action // adapters typically produce measurements, as such, actions default to respecting output demands not input demands. Using // the default settings of not respecting input demands and the proxy adapter not producing any points, the Iaon session // would ignore the adapter's input needs. In this case we want Iaon session to recognize the inputs of the proxy adapter // as important to the connect-on-demand dependency chain, so we request respect for the input demands. TemporalClientSubscriptionProxy proxyAdapter = new TemporalClientSubscriptionProxy { // Assign critical adapter properties ID = 0, Name = "PROXY!SERVICES", ConnectionString = "", DataSource = session.DataSource, RespectInputDemands = true, InputMeasurementKeys = inputMeasurementKeys, OutputMeasurements = outputMeasurements, Parent = clientSubscription, Initialized = true }; // Add new proxy adapter to temporal session action adapter collection - this will start adapter session.ActionAdapters.Add(proxyAdapter); // Load current temporal constraint parameters Dictionary<string, string> settings = clientSubscription.Settings; string startTime, stopTime, parameters; settings.TryGetValue("startTimeConstraint", out startTime); settings.TryGetValue("stopTimeConstraint", out stopTime); settings.TryGetValue("timeConstraintParameters", out parameters); // Assign requested temporal constraints to all private session adapters session.AllAdapters.SetTemporalConstraint(startTime, stopTime, parameters); session.AllAdapters.ProcessingInterval = clientSubscription.ProcessingInterval; // Start temporal session adapters session.AllAdapters.Start(); // Recalculate routing tables to accommodate addition of proxy adapter and handle // input measurement keys restriction session.RecalculateRoutingTables(); return session; }
/// <summary> /// Initializes <see cref="UnsynchronizedClientSubscription"/>. /// </summary> public override void Initialize() { MeasurementKey[] inputMeasurementKeys; string setting; if (Settings.TryGetValue("inputMeasurementKeys", out setting)) { // IMPORTANT: The allowSelect argument of ParseInputMeasurementKeys must be null // in order to prevent SQL injection via the subscription filter expression inputMeasurementKeys = ParseInputMeasurementKeys(DataSource, false, setting); m_requestedInputFilter = setting; // IMPORTANT: We need to remove the setting before calling base.Initialize() // or else we will still be subject to SQL injection Settings.Remove("inputMeasurementKeys"); } else { inputMeasurementKeys = new MeasurementKey[0]; m_requestedInputFilter = null; } base.Initialize(); // Set the InputMeasurementKeys property after calling base.Initialize() // so that the base class does not overwrite our setting InputMeasurementKeys = inputMeasurementKeys; if (Settings.TryGetValue("publishInterval", out setting)) m_publishInterval = int.Parse(setting); else m_publishInterval = -1; if (Settings.TryGetValue("includeTime", out setting)) m_includeTime = setting.ParseBoolean(); else m_includeTime = true; if (Settings.TryGetValue("useMillisecondResolution", out setting)) m_useMillisecondResolution = setting.ParseBoolean(); else m_useMillisecondResolution = false; if (Settings.TryGetValue("requestNaNValueFilter", out setting)) m_isNaNFiltered = m_parent.AllowNaNValueFilter && setting.ParseBoolean(); else m_isNaNFiltered = false; if (Settings.TryGetValue("bufferBlockRetransmissionTimeout", out setting)) m_bufferBlockRetransmissionTimeout = double.Parse(setting); else m_bufferBlockRetransmissionTimeout = 5.0D; if (m_parent.UseBaseTimeOffsets && m_includeTime) { m_baseTimeRotationTimer = Common.TimerScheduler.CreateTimer(m_useMillisecondResolution ? 60000 : 420000); m_baseTimeRotationTimer.AutoReset = true; m_baseTimeRotationTimer.Elapsed += BaseTimeRotationTimer_Elapsed; } m_bufferBlockRetransmissionTimer = Common.TimerScheduler.CreateTimer((int)(m_bufferBlockRetransmissionTimeout * 1000.0D)); m_bufferBlockRetransmissionTimer.AutoReset = false; m_bufferBlockRetransmissionTimer.Elapsed += BufferBlockRetransmissionTimer_Elapsed; // Handle temporal session initialization if (this.TemporalConstraintIsDefined()) m_iaonSession = this.CreateTemporalSession(); }
/// <summary> /// Initializes <see cref="UnsynchronizedClientSubscription"/>. /// </summary> public override void Initialize() { base.Initialize(); string setting; if (Settings.TryGetValue("inputMeasurementKeys", out setting)) m_requestedInputFilter = setting; else m_requestedInputFilter = null; if (Settings.TryGetValue("publishInterval", out setting)) m_publishInterval = int.Parse(setting); else m_publishInterval = -1; if (Settings.TryGetValue("includeTime", out setting)) m_includeTime = setting.ParseBoolean(); else m_includeTime = true; if (Settings.TryGetValue("useMillisecondResolution", out setting)) m_useMillisecondResolution = setting.ParseBoolean(); else m_useMillisecondResolution = false; if (m_parent.UseBaseTimeOffsets && m_includeTime) { m_baseTimeRotationTimer = new System.Timers.Timer(); m_baseTimeRotationTimer.Interval = m_useMillisecondResolution ? 60000 : 420000; m_baseTimeRotationTimer.AutoReset = true; m_baseTimeRotationTimer.Elapsed += BaseTimeRotationTimer_Elapsed; } // Handle temporal session intialization if (this.TemporalConstraintIsDefined()) m_iaonSession = this.CreateTemporalSession(); }
/// <summary> /// Initializes <see cref="UnsynchronizedClientSubscription"/>. /// </summary> public override void Initialize() { base.Initialize(); string setting; if (Settings.TryGetValue("inputMeasurementKeys", out setting)) m_requestedInputFilter = setting; else m_requestedInputFilter = null; if (Settings.TryGetValue("publishInterval", out setting)) m_publishInterval = int.Parse(setting); else m_publishInterval = -1; if (Settings.TryGetValue("includeTime", out setting)) m_includeTime = setting.ParseBoolean(); else m_includeTime = true; if (Settings.TryGetValue("useMillisecondResolution", out setting)) m_useMillisecondResolution = setting.ParseBoolean(); else m_useMillisecondResolution = false; if (Settings.TryGetValue("requestNaNValueFilter", out setting)) m_isNaNFiltered = m_parent.AllowNaNValueFilter && setting.ParseBoolean(); else m_isNaNFiltered = false; if (Settings.TryGetValue("bufferBlockRetransmissionTimeout", out setting)) m_bufferBlockRetransmissionTimeout = double.Parse(setting); else m_bufferBlockRetransmissionTimeout = 5.0D; if (m_parent.UseBaseTimeOffsets && m_includeTime) { m_baseTimeRotationTimer = new Timer(); m_baseTimeRotationTimer.Interval = m_useMillisecondResolution ? 60000 : 420000; m_baseTimeRotationTimer.AutoReset = true; m_baseTimeRotationTimer.Elapsed += BaseTimeRotationTimer_Elapsed; } m_bufferBlockRetransmissionTimer = new Timer(); m_bufferBlockRetransmissionTimer.AutoReset = false; m_bufferBlockRetransmissionTimer.Interval = m_bufferBlockRetransmissionTimeout * 1000.0D; m_bufferBlockRetransmissionTimer.Elapsed += BufferBlockRetransmissionTimer_Elapsed; // Handle temporal session initialization if (this.TemporalConstraintIsDefined()) m_iaonSession = this.CreateTemporalSession(); }