/// <summary> /// Releases the unmanaged resources used by the <see cref="AlarmAdapter"/> object and optionally releases the managed resources. /// </summary> /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> protected override void Dispose(bool disposing) { if (!m_disposed) { try { if (disposing) { if (m_alarmService != null) { m_alarmService.ServiceProcessException -= AlarmService_ServiceProcessException; m_alarmService.Dispose(); } lock (m_alarmLock) { foreach (SignalAlarms signalAlarms in m_alarmLookup.Values) { StatisticsEngine.Unregister(signalAlarms.Statistics); } } } } finally { m_disposed = true; // Prevent duplicate dispose. base.Dispose(disposing); // Call base class Dispose(). } } }
/// <summary> /// If measurement is a statistic, returns the associated Statistic record; otherwise, returns <c>null</c>. /// </summary> /// <param name="metadataRecord">Record of measurement metadata used to lookup Statistic record.</param> /// <returns>Associated Statistic record, if measurement is a statistic; otherwise, returns <c>null</c>.</returns> /// <remarks> /// For best results, this function should be called after all statistic engine sources have been registered. /// <paramref name="metadataRecord"/> object expected to contain a "signalid" and "signalreference" property. /// </remarks> public Statistic GetStatistic(dynamic metadataRecord) { Guid signalID = metadataRecord.signalid; return(s_statisticReferences.GetOrAdd(signalID, _ => { string signalReference = metadataRecord.signalreference; if (string.IsNullOrWhiteSpace(signalReference)) { return null; } if (!StatisticsEngine.TryLookupStatisticSource(signalReference, out string source, out int signalIndex)) { return null; } InitializeStatistics(); foreach (Statistic statistic in s_statistics) { if (statistic.Source.Equals(source, StringComparison.OrdinalIgnoreCase) && statistic.SignalIndex == signalIndex) { return statistic; } } return null; }));
/// <summary> /// Releases the unmanaged resources used by the <see cref="ModbusPoller"/> object and optionally releases the managed resources. /// </summary> /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> protected override void Dispose(bool disposing) { if (!m_disposed) { try { if (disposing) { StatisticsEngine.Unregister(m_deviceProxy); DisposeConnections(); if ((object)m_pollingTimer != null) { m_pollingTimer.Enabled = false; m_pollingTimer.Elapsed -= m_pollingTimer_Elapsed; m_pollingTimer.Dispose(); } } } finally { m_disposed = true; // Prevent duplicate dispose. base.Dispose(disposing); // Call base class Dispose(). } } }
public override void Initialize() { ProcessingInterval = 0; OnStatusMessage(MessageLevel.Info, $"Initialising OPC for device {Name}"); base.Initialize(); // If more data is added to the connection string the it will have to be parsed here (Look at ModbusPoller.cs for example line 456: https://github.com/GridProtectionAlliance/gsf/blob/master/Source/Libraries/Adapters/ModbusAdapters/ModbusPoller.cs) // Statistics StatisticsEngine.Register(this, "OPCUA", "OPC"); OutputMeasurements = ParseOutputMeasurements(DataSource, false, $"FILTER ActiveMeasurements WHERE Device = '{Name}'"); DataTable measurements = DataSource.Tables["ActiveMeasurements"]; items = OutputMeasurements.ToDictionary(measurement => { DataRow[] records = measurements.Select($"ID = '{measurement.Key}'"); var reference = records[0]["SignalReference"].ToNonNullString(); OnStatusMessage(MessageLevel.Info, $"Linking OPC item {reference} with tag {measurement.TagName}"); return(reference); }); // OPC Init ApplicationInstance application = new ApplicationInstance { ApplicationName = "OPC UA Client", ApplicationType = Opc.Ua.ApplicationType.Client, ConfigSectionName = "Opc.Ua.Client" }; ApplicationConfiguration config = application.LoadApplicationConfiguration(false).GetAwaiter().GetResult(); bool haveAppCertificate = application.CheckApplicationInstanceCertificate(false, 0).GetAwaiter().GetResult(); if (!haveAppCertificate) { throw new Exception("Application instance certificate invalid!"); } if (haveAppCertificate) { config.ApplicationUri = Utils.GetApplicationUriFromCertificate(config.SecurityConfiguration.ApplicationCertificate.Certificate); if (config.SecurityConfiguration.AutoAcceptUntrustedCertificates) { autoAccept = true; } config.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation); } else { OnStatusMessage(MessageLevel.Info, $"Missing application certificate, using unsecure connection."); } m_configuration = config; OnStatusMessage(MessageLevel.Info, $"OPC Initialised ({ConnectionString})"); }
private string GenerateStatsTable(string deviceAcronym, string parentAcronym) { string tbl = "ID".PadRight(11) + "Description".PadRight(201) + "Value".PadRight(21) + "Time (UTC)".PadRight(20) + "\r\n"; tbl += string.Concat(Enumerable.Repeat("-", 253)) + "\r\n"; // Grab Stat Measurements IEnumerable <MeasurementDetail> statmeasurements = DataContext.Table <MeasurementDetail>().QueryRecordsWhere("DeviceAcronym IN ({0},{1}) AND SignalAcronym = 'STAT'", deviceAcronym, parentAcronym); // Read Current Values (+ 30 seconds back) ConfigurationFile config = ConfigurationFile.Current; CategorizedSettingsElementCollection settings = config.Settings["statArchiveFile"]; ArchiveReader archiveReader = new ArchiveReader(); archiveReader.Open(settings["FileName"].ValueAsString()); List <MetadataRecord> metadataRecords = archiveReader.MetadataFile.Read() .Where(record => !string.IsNullOrEmpty(record.Name)) .ToList(); Dictionary <long, IEnumerable <IDataPoint> > data = statmeasurements.Select(item => item.PointID).ToDictionary(record => record, record => archiveReader.ReadData((int)record, DateTime.UtcNow.Subtract(new TimeSpan(0, 1, 0)), DateTime.UtcNow, false)); foreach (MeasurementDetail meas in statmeasurements) { tbl += meas.PointTag.Substring(meas.PointTag.LastIndexOf("!") + 1).Trim().PadRight(10) + " " + meas.Description.Trim().PadRight(200) + " "; if (data.ContainsKey(meas.PointID) && data[meas.PointID].Count() > 0) { if (!StatisticsEngine.TryLookupStatisticSource(meas.SignalReference, out string source, out int signalIndex)) { tbl += "N/A".PadRight(20) + " " + "N/A".PadRight(20) + "\r\n"; } else { Statistic stat = DataContext.Table <Statistic>().QueryRecordWhere("Source = {0} AND SignalIndex = {1}", source, signalIndex); Type statType = Type.GetType(stat.DataType); if (statType == typeof(DateTime)) { tbl += String.Format(stat.DisplayFormat, new GSF.UnixTimeTag((decimal)data[meas.PointID].First().Value)).PadRight(20) + " " + data[meas.PointID].First().Time.ToString("HH:mm:ss.fff").PadRight(20) + "\r\n"; } else { tbl += String.Format(stat.DisplayFormat, Convert.ChangeType(data[meas.PointID].First().Value, statType)).PadRight(20) + " " + data[meas.PointID].First().Time.ToString("HH:mm:ss.fff").PadRight(20) + "\r\n"; } } }
// Updates alarm definitions when the data source has been updated. private void UpdateAlarmDefinitions() { DateTime now; DataSet alarmDataSet; Dictionary <int, Alarm> definedAlarmsLookup; Dictionary <Guid, SignalAlarms> newAlarmLookup; //List<IMeasurement> alarmEvents; // Get the current time in case we need // to generate alarm events for cleared alarms now = DateTime.UtcNow; // Get the latest version of the table of defined alarms alarmDataSet = new DataSet(); alarmDataSet.Tables.Add(DataSource.Tables["Alarms"].Copy()); // Compare the latest alarm table with the previous // alarm table to determine if anything needs to be done if (DataSetEqualityComparer.Default.Equals(m_alarmDataSet, alarmDataSet)) { return; } m_alarmDataSet = alarmDataSet; // Get list of alarms defined in the latest version of the alarm table definedAlarmsLookup = alarmDataSet.Tables[0].Rows.Cast <DataRow>() .Where(row => row.ConvertField <bool>("Enabled")) .Select(CreateAlarm) .ToDictionary(alarm => alarm.ID); // Create a list to store alarm events generated by this process //alarmEvents = new List<IMeasurement>(); lock (m_alarmLock) { foreach (Alarm existingAlarm in m_alarmLookup.SelectMany(kvp => kvp.Value.Alarms)) { Alarm definedAlarm; // Attempt to locate the defined alarm corresponding to the existing alarm definedAlarmsLookup.TryGetValue(existingAlarm.ID, out definedAlarm); // Determine if a change to the alarm's // configuration has changed the alarm's behavior if (BehaviorChanged(existingAlarm, definedAlarm)) { // Clone the existing alarm so that changes to alarm // states can be observed later in this process Alarm clone = existingAlarm.Clone(); // Clear the alarm and create an event clone.State = AlarmState.Cleared; //alarmEvents.Add(CreateAlarmEvent(now, clone)); } else if ((object)definedAlarm != null) { // Update functionally irrelevant configuration info existingAlarm.TagName = definedAlarm.TagName; existingAlarm.Description = definedAlarm.Description; // Use the existing alarm since the alarm is functionally the same definedAlarmsLookup[definedAlarm.ID] = existingAlarm; } } // Create the new alarm lookup to replace the old one newAlarmLookup = definedAlarmsLookup.Values .GroupBy(alarm => alarm.SignalID) .ToDictionary(grouping => grouping.Key, grouping => new SignalAlarms() { // Alarms are sorted in order of precedence: // 1) Exemptions (forces alarm state to cleared) // 2) Severity (high severity takes precedence over low severity) // 3) ID (relative order of alarms with same severity doesn't change) Alarms = grouping .OrderByDescending(alarm => alarm.Severity == AlarmSeverity.None) .ThenByDescending(alarm => alarm.Severity) .ThenBy(alarm => alarm.ID) .ToList() }); // Check for changes to alarms that need to go in the alarm log foreach (KeyValuePair <Guid, SignalAlarms> kvp in m_alarmLookup) { SignalAlarms existingAlarms = kvp.Value; SignalAlarms definedAlarms; // Get the active alarms from both before and after the configuration changes were applied Alarm existingActiveAlarm = existingAlarms.Alarms.FirstOrDefault(alarm => alarm.State == AlarmState.Raised); Alarm definedActiveAlarm = null; if (newAlarmLookup.TryGetValue(kvp.Key, out definedAlarms)) { definedAlarms.Statistics = existingAlarms.Statistics; definedActiveAlarm = definedAlarms.Alarms.FirstOrDefault(alarm => alarm.State == AlarmState.Raised); } else { StatisticsEngine.Unregister(existingAlarms.Statistics); } if ((object)definedActiveAlarm != null && (object)existingActiveAlarm != null) { // If the active alarm has changed as a result // of the configuration change, log the change if (definedActiveAlarm.ID != existingActiveAlarm.ID) { LogStateChange(kvp.Key, existingActiveAlarm, definedActiveAlarm, now, double.NaN); } } else if ((object)existingActiveAlarm != null) { // If alarms were raised before the configuration change, // but have all been cleared as a result of the // configuration change, log the change LogStateChange(kvp.Key, existingActiveAlarm, null, now, double.NaN); } } } // Use SignalReference as the name of the signal when creating statistic source foreach (DataRow row in DataSource.Tables["ActiveMeasurements"].Rows) { Guid signalID; SignalAlarms signalAlarms; if (Guid.TryParse(row["SignalID"].ToNonNullString(), out signalID) && newAlarmLookup.TryGetValue(signalID, out signalAlarms)) { signalAlarms.Name = row.Field <string>("SignalReference"); } } // Newly added signals that do not have statistics associated // with them yet need to be registered with the statistics engine foreach (KeyValuePair <Guid, SignalAlarms> kvp in newAlarmLookup) { if ((object)kvp.Value.Statistics == null) { if (string.IsNullOrEmpty(kvp.Value.Name)) { kvp.Value.Name = kvp.Key.ToString().Replace("-", ""); } kvp.Value.Statistics = new AlarmStatistics(); StatisticsEngine.Register(kvp.Value.Statistics, kvp.Value.Name, "Point", "PT", "{0}!{}"); } } lock (m_alarmLock) { m_alarmLookup = newAlarmLookup; // Only automatically update input measurement keys if the setting is not explicitly defined if (m_alarmLookup.Count > 0 && !Settings.ContainsKey("inputMeasurementKeys")) { // Generate filter expression for input measurements string filterExpression = string.Join(";", m_alarmLookup.Select(kvp => kvp.Key.ToString())); // Set input measurement keys for measurement routing InputMeasurementKeys = ParseInputMeasurementKeys(DataSource, true, filterExpression); } } }
private void m_unsynchronizedSubscriber_NewMeasurements(object sender, EventArgs <ICollection <IMeasurement> > e) { if (0 == Interlocked.Exchange(ref m_processingUnsynchronizedMeasurements, 1)) { try { ObservableCollection <StatisticMeasurement> statisticMeasurements; RealTimeMeasurement realTimeMeasurement; HashSet <RealTimeStream> updatedStreams = new HashSet <RealTimeStream>(); HashSet <RealTimeDevice> updatedDevices = new HashSet <RealTimeDevice>(); // If it doesn't already exist, create lookup table to allow quick // lookup of RealTimeMeasurements whose value has changed if ((object)m_realTimeMeasurements == null) { m_realTimeMeasurements = ItemsSource .SelectMany(stream => stream.DeviceList) .SelectMany(device => device.MeasurementList) .GroupBy(measurement => measurement.SignalID) .Select(group => group.First()) .ToDictionary(measurement => measurement.SignalID); } // Update measurements that have changed foreach (IMeasurement newMeasurement in e.Argument) { if (m_realTimeMeasurements.TryGetValue(newMeasurement.ID, out realTimeMeasurement)) { RealTimeDevice parentDevice = realTimeMeasurement.Parent; RealTimeStream parentStream = parentDevice.Parent; if (newMeasurement.Timestamp > realTimeMeasurement.LastUpdated) { realTimeMeasurement.Quality = newMeasurement.ValueQualityIsGood() ? "GOOD" : "BAD"; realTimeMeasurement.TimeTag = newMeasurement.Timestamp.ToString("HH:mm:ss.fff"); realTimeMeasurement.Value = newMeasurement.AdjustedValue.ToString("0.###"); realTimeMeasurement.LastUpdated = newMeasurement.Timestamp; // Maintain collection of updated devices and streams updatedDevices.Add(parentDevice); if ((object)parentStream != null) { updatedStreams.Add(parentStream); } } } } // Update status color of the updated devices // NOTE: Color cannot be red since we just received data from the devices foreach (RealTimeDevice device in updatedDevices) { // By default, determine color based on measurement quality List <RealTimeMeasurement> measurementList = device.MeasurementList.Where(m => m.Value != "--").ToList(); if (measurementList.Count > 0) { if (measurementList.All(m => m.Quality != "BAD")) { device.StatusColor = "Green"; } else { device.StatusColor = "Yellow"; } } if (device.ID != null && device.ID > 0) { // Check to see if device has statistic measurements which define the number of errors reported by the device if (!RealTimeStatistic.InputStreamStatistics.ContainsKey((int)device.ID)) { if (RealTimeStatistic.DevicesWithStatisticMeasurements.TryGetValue((int)device.ID, out statisticMeasurements)) { // If there are any reported errors, force color to yellow foreach (StatisticMeasurement statisticMeasurement in statisticMeasurements) { int value; if (StatisticsEngine.RegexMatch(statisticMeasurement.SignalReference, "PMU") && int.TryParse(statisticMeasurement.Value, out value) && value > 0) { device.StatusColor = "Yellow"; } } } } } } // Update status color of the updated streams // NOTE: Color cannot be red since we just received data from the devices foreach (RealTimeStream stream in updatedStreams) { // Streams with ID of 0 are placeholders // and must remain transparent if (stream.ID > 0) { if (stream.DeviceList.Any(device => device.StatusColor == "Green")) { stream.StatusColor = "Green"; } else { stream.StatusColor = "Yellow"; } } } LastRefresh = "Last Refresh: " + DateTime.UtcNow.ToString("HH:mm:ss.fff"); } finally { Interlocked.Exchange(ref m_processingUnsynchronizedMeasurements, 0); } } }
/// <summary> /// Initializes <see cref="ProcessLauncher"/>. /// </summary> public override void Initialize() { base.Initialize(); Dictionary <string, string> settings = Settings; ProcessWindowStyle windowStyle; int initialInputProcessingDelay, utilizationCalculationInterval; string setting; ProcessStartInfo startInfo = m_process.StartInfo; // Load required parameters if (settings.TryGetValue(nameof(FileName), out setting)) { setting = FilePath.GetAbsolutePath(setting.Trim()); if (File.Exists(setting)) { FileName = setting; startInfo.FileName = FileName; } else { throw new FileNotFoundException($"Cannot launch process: specified executable path and filename \"{setting}\" does not exist."); } } else { throw new ArgumentException($"Cannot launch process: required \"{nameof(FileName)}\" parameter is missing from connection string."); } // Load optional parameters if (settings.TryGetValue(nameof(SupportsTemporalProcessing), out setting)) { m_supportsTemporalProcessing = setting.ParseBoolean(); } else { m_supportsTemporalProcessing = DefaultSupportsTemporalProcessing; } if (settings.TryGetValue(nameof(Arguments), out setting) && setting.Length > 0) { startInfo.Arguments = setting; } if (settings.TryGetValue(nameof(WorkingDirectory), out setting)) { setting = setting.Trim(); if (Directory.Exists(setting)) { WorkingDirectory = setting; startInfo.WorkingDirectory = WorkingDirectory; } else { throw new DirectoryNotFoundException($"Cannot launch process: specified working directory \"{setting}\" does not exist."); } } else { WorkingDirectory = FilePath.GetDirectoryName(FileName); startInfo.WorkingDirectory = WorkingDirectory; } if (settings.TryGetValue(nameof(EnvironmentalVariables), out setting)) { foreach (KeyValuePair <string, string> item in setting.ParseKeyValuePairs()) { startInfo.Environment[item.Key] = item.Value; } } // Note that it's possible that time-series framework is being hosted by an application // running in a Window making many of the following process start properties relevant. // Even when hosted as a service, the user may start the service logging on using the // local system account and select to allow the service interact with the desktop. if (settings.TryGetValue(nameof(CreateNoWindow), out setting)) { startInfo.CreateNoWindow = setting.ParseBoolean(); } else { startInfo.CreateNoWindow = DefaultCreateNoWindow; } if (settings.TryGetValue(nameof(WindowStyle), out setting) && Enum.TryParse(setting, true, out windowStyle)) { startInfo.WindowStyle = windowStyle; } else { startInfo.WindowStyle = (ProcessWindowStyle)Enum.Parse(typeof(ProcessWindowStyle), DefaultWindowStyle); } if (settings.TryGetValue(nameof(ErrorDialog), out setting)) { startInfo.ErrorDialog = setting.ParseBoolean(); } else { startInfo.ErrorDialog = DefaultErrorDialog; } if (settings.TryGetValue(nameof(Domain), out setting) && setting.Length > 0) { startInfo.Domain = setting; } if (settings.TryGetValue(nameof(UserName), out setting) && setting.Length > 0) { startInfo.UserName = setting; } if (settings.TryGetValue(nameof(Password), out setting) && setting.Length > 0) { startInfo.Password = setting.ToSecureString(); } if (settings.TryGetValue(nameof(LoadUserProfile), out setting)) { startInfo.LoadUserProfile = setting.ParseBoolean(); } if (settings.TryGetValue(nameof(InitialInputFileName), out setting)) { setting = FilePath.GetAbsolutePath(setting.Trim()); if (File.Exists(setting)) { InitialInputFileName = setting; } else { throw new FileNotFoundException($"Cannot launch process: specified initial input filename \"{setting}\" does not exist."); } } if (settings.TryGetValue(nameof(InitialInputProcessingDelay), out setting) && int.TryParse(setting, out initialInputProcessingDelay) && initialInputProcessingDelay > -1) { InitialInputProcessingDelay = initialInputProcessingDelay; } if (settings.TryGetValue(nameof(RedirectOutputToHostEnvironment), out setting)) { RedirectOutputToHostEnvironment = setting.ParseBoolean(); } if (settings.TryGetValue(nameof(RedirectErrorToHostEnvironment), out setting)) { RedirectErrorToHostEnvironment = setting.ParseBoolean(); } if (settings.TryGetValue(nameof(UtilizationUpdateInterval), out setting) && int.TryParse(setting, out utilizationCalculationInterval)) { UtilizationUpdateInterval = utilizationCalculationInterval; } startInfo.RedirectStandardOutput = RedirectOutputToHostEnvironment; startInfo.RedirectStandardError = RedirectErrorToHostEnvironment; startInfo.RedirectStandardInput = true; startInfo.UseShellExecute = false; m_process.EnableRaisingEvents = true; m_process.OutputDataReceived += ProcessOutputDataReceived; m_process.ErrorDataReceived += ProcessErrorDataReceived; if (settings.TryGetValue(nameof(ProcessOutputAsLogMessages), out setting)) { ProcessOutputAsLogMessages = setting.ParseBoolean(); } if (ProcessOutputAsLogMessages) { if (settings.TryGetValue(nameof(LogMessageTextExpression), out setting) && setting.Length > 0) { LogMessageTextExpression = setting; } m_logMessageTextExpression = new Regex(LogMessageTextExpression, RegexOptions.Compiled); if (settings.TryGetValue(nameof(LogMessageLevelExpression), out setting) && setting.Length > 0) { LogMessageLevelExpression = setting; } m_logMessageLevelExpression = new Regex(LogMessageLevelExpression, RegexOptions.Compiled); if (settings.TryGetValue(nameof(LogMessageLevelMappings), out setting)) { LogMessageLevelMappings = setting; } foreach (KeyValuePair <string, string> item in LogMessageLevelMappings.ParseKeyValuePairs()) { MessageLevel level; if (Enum.TryParse(item.Value, true, out level)) { m_messageLevelMap[item.Key] = level; } } } if (settings.TryGetValue(nameof(ForceKillOnDispose), out setting)) { ForceKillOnDispose = setting.ParseBoolean(); } if (settings.TryGetValue(nameof(TrackProcessStatistics), out setting)) { TrackProcessStatistics = setting.ParseBoolean(); } m_process.Start(); if (ForceKillOnDispose) { m_childProcessManager?.AddProcess(m_process); } if (RedirectOutputToHostEnvironment) { m_process.BeginOutputReadLine(); } if (RedirectErrorToHostEnvironment) { m_process.BeginErrorReadLine(); } m_processUtilizationCalculator.UpdateInterval = UtilizationUpdateInterval; m_processUtilizationCalculator.Initialize(m_process); // Register launched process with the statistics engine if (TrackProcessStatistics) { StatisticsEngine.Register(this, "Process", "PROC"); } if (string.IsNullOrEmpty(InitialInputFileName)) { return; } // Send any defined initial input to launched application new Action(() => { try { using (StreamReader reader = File.OpenText(InitialInputFileName)) { string line; while ((object)(line = reader.ReadLine()) != null) { Input(line); } } } catch (Exception ex) { OnProcessException(MessageLevel.Warning, new InvalidOperationException($"Failed while sending text from \"{InitialInputFileName}\" to launched process standard input: {ex.Message}", ex)); } }) .DelayAndExecute(InitialInputProcessingDelay); }
/// <summary> /// Gets statistic measurements from the database for current node. /// </summary> /// <param name="database"><see cref="AdoDataConnection"/> to connection to database.</param> /// <returns>Collection of <see cref="StatisticMeasurement"/>.</returns> public static ObservableCollection <StatisticMeasurement> GetStatisticMeasurements(AdoDataConnection database) { bool createdConnection = false; try { createdConnection = CreateConnection(ref database); // Get Statistic Definitions ObservableCollection <Statistic> statisticDefinitions = Statistic.Load(database); // Get statistics measurements. DataTable statisticMeasurements = database.Connection.RetrieveData(database.AdapterType, database.ParameterizedQueryString("SELECT SignalID, ID, DeviceID, PointID, PointTag, SignalReference " + "FROM StatisticMeasurement WHERE NodeID = {0}", "nodeID"), DefaultTimeout, database.CurrentNodeID()); // Assign min and max point IDs as we will need them to request data from web service. MinPointID = int.MaxValue; MaxPointID = int.MinValue; foreach (DataRow row in statisticMeasurements.Rows) { int pointID = Convert.ToInt32(row.Field <object>("PointID")); MinPointID = Math.Min(MinPointID, pointID); MaxPointID = Math.Max(MaxPointID, pointID); } // Takes datarow from statisticMeasurements data table, and associates each row to their statistic source and returns KeyValuePair. Func <DataRow, KeyValuePair <DataRow, string> > mapFunction = measurement => { string signalReference = measurement.Field <string>("SignalReference"); string measurementSource; if (StatisticsEngine.RegexMatch(signalReference, "SYSTEM")) { measurementSource = "System"; } else if (StatisticsEngine.RegexMatch(signalReference, "PMU")) { measurementSource = "Device"; } else if (StatisticsEngine.RegexMatch(signalReference, "OS")) { measurementSource = "OutputStream"; } else if (StatisticsEngine.RegexMatch(signalReference, "IS")) { measurementSource = "InputStream"; } else if (StatisticsEngine.RegexMatch(signalReference, "SUB")) { measurementSource = "Subscriber"; } else if (StatisticsEngine.RegexMatch(signalReference, "PUB")) { measurementSource = "Publisher"; } else { measurementSource = "???"; } return(new KeyValuePair <DataRow, string>(measurement, measurementSource)); }; // Takes KeyValuePair and generates StatisticMeasurement record by maping statistic measurements with statistic definitions. Func <KeyValuePair <DataRow, string>, StatisticMeasurement> selectFunction = keyvaluepair => { DataRow measurement = keyvaluepair.Key; string measurementSource = keyvaluepair.Value; Debug.WriteLine(measurementSource); string signalReference = measurement.Field <string>("SignalReference"); int measurementIndex = Convert.ToInt32(signalReference.Substring(signalReference.LastIndexOf("-ST") + 3)); Statistic statisticDefinition = new Statistic(); foreach (Statistic statistic in statisticDefinitions) { if (statistic.Source == measurementSource && statistic.SignalIndex == measurementIndex) { statisticDefinition = statistic; break; } } return(new StatisticMeasurement() { SignalID = database.Guid(measurement, "SignalID"), ID = measurement.Field <string>("ID"), DeviceID = Convert.ToInt32(measurement.Field <object>("DeviceID") ?? -1), PointID = Convert.ToInt32(measurement.Field <object>("PointID")), PointTag = measurement.Field <string>("PointTag"), SignalReference = signalReference, Source = measurementSource, StatisticName = statisticDefinition.Name, StatisticDescription = statisticDefinition.Description, DataType = statisticDefinition.DataType, DisplayFormat = statisticDefinition.DisplayFormat, ConnectedState = statisticDefinition.IsConnectedState, LoadOrder = statisticDefinition.LoadOrder, TimeTag = "n/a", Quality = "n/a", Value = "--" }); }; return(new ObservableCollection <StatisticMeasurement>(statisticMeasurements.Rows.Cast <DataRow>().Select(mapFunction).OrderBy(pair => pair.Value).Select(selectFunction).OrderBy(s => s.LoadOrder))); } finally { if (createdConnection && database != null) { database.Dispose(); } } }
/// <summary> /// Initializes <see cref="ModbusPoller" />. /// </summary> public override void Initialize() { base.Initialize(); ConnectionStringParser <ConnectionStringParameterAttribute> parser = new ConnectionStringParser <ConnectionStringParameterAttribute>(); parser.ParseConnectionString(ConnectionString, this); // Register downloader with the statistics engine StatisticsEngine.Register(this, "Modbus", "MOD"); StatisticsEngine.Register(m_deviceProxy, Name, "Device", "PMU"); // Attach to output measurements for Modbus device OutputMeasurements = ParseOutputMeasurements(DataSource, false, $"FILTER ActiveMeasurements WHERE Device = '{Name}'"); // Parse derived value expressions from defined signal reference fields m_derivedValues = OutputMeasurements.Select(measurement => measurement.Key).ToDictionary(key => key, key => { DataTable measurements = DataSource.Tables["ActiveMeasurements"]; DerivedValue derivedValue = null; DataRow[] records = measurements.Select($"ID = '{key}'"); if (records.Length > 0) { derivedValue = ParseDerivedValue(records[0]["SignalReference"].ToNonNullString()); } return(derivedValue); }); m_sequences = new List <Sequence>(); Dictionary <string, string> settings = Settings; string setting; int sequenceCount = 0; if (settings.TryGetValue("sequenceCount", out setting)) { int.TryParse(setting, out sequenceCount); } for (int i = 0; i < sequenceCount; i++) { if (settings.TryGetValue($"sequence{i}", out setting)) { Dictionary <string, string> sequenceSettings = setting.ParseKeyValuePairs(); SequenceType sequenceType = SequenceType.Read; if (sequenceSettings.TryGetValue("sequenceType", out setting)) { Enum.TryParse(setting, true, out sequenceType); } Sequence sequence = new Sequence(sequenceType); int groupCount; if (sequenceSettings.TryGetValue("groupCount", out setting) && int.TryParse(setting, out groupCount)) { for (int j = 0; j < groupCount; j++) { Group group = new Group(); if (sequenceSettings.TryGetValue($"groupType{j}", out setting)) { Enum.TryParse(setting, true, out group.Type); } if (sequenceSettings.TryGetValue($"groupStartAddress{j}", out setting)) { ushort.TryParse(setting, out group.StartAddress); } if (sequenceSettings.TryGetValue($"groupPointCount{j}", out setting)) { ushort.TryParse(setting, out group.PointCount); } if (group.StartAddress > 0 && group.PointCount > 0) { // Load any defined write sequence values if (sequence.Type == SequenceType.Write) { group.DataValues = new ushort[group.PointCount]; for (int k = 0; k < group.PointCount; k++) { if (sequenceSettings.TryGetValue($"group{j}DataValue{k}", out setting)) { ushort.TryParse(setting, out group.DataValues[k]); } } } sequence.Groups.Add(group); } } } if (sequence.Groups.Count > 0) { m_sequences.Add(sequence); } } } if (m_sequences.Count == 0) { throw new InvalidOperationException("No sequences defined, cannot start Modbus polling."); } // Define synchronized polling operation m_pollingOperation = new ShortSynchronizedOperation(PollingOperation, exception => OnProcessException(MessageLevel.Warning, exception)); // Define polling timer m_pollingTimer = new Timer(m_pollingRate); m_pollingTimer.AutoReset = true; m_pollingTimer.Elapsed += m_pollingTimer_Elapsed; }
public Dictionary <Player, double> Statistics() { StatisticsEngine statistiqueEngine = new StatisticsEngine(this); return(statistiqueEngine.StatisticsPerPlayer()); }
void Init () { mTickTimer = 0f; CreateResourceStore (); mStatsEngine = new StatisticsEngine (); }