/// <summary> /// Publish <see cref="IFrame"/> of time-aligned collection of <see cref="IMeasurement"/> values that arrived within the /// concentrator's defined <see cref="ConcentratorBase.LagTime"/>. /// </summary> /// <param name="frame"><see cref="IFrame"/> of measurements with the same timestamp that arrived within <see cref="ConcentratorBase.LagTime"/> that are ready for processing.</param> /// <param name="index">Index of <see cref="IFrame"/> within a second ranging from zero to <c><see cref="ConcentratorBase.FramesPerSecond"/> - 1</c>.</param> protected override void PublishFrame(IFrame frame, int index) { IMeasurement bestMeasurement = GetBestMeasurement(frame); if ((object)bestMeasurement == null) { return; } IMeasurement[] newMeasurements = OutputMeasurements .Select(measurement => Measurement.Clone(measurement)) .ToArray(); newMeasurements[0].Timestamp = frame.Timestamp; newMeasurements[0].StateFlags = bestMeasurement.StateFlags; newMeasurements[0].Value = bestMeasurement.Value; if (newMeasurements.Length > 1) { newMeasurements[1].Timestamp = frame.Timestamp; newMeasurements[1].StateFlags = bestMeasurement.StateFlags; newMeasurements[1].Value = Array.IndexOf(InputMeasurementKeys, bestMeasurement.Key) + 1; } OnNewMeasurements(newMeasurements); m_lastSelectedMeasurement = bestMeasurement.Key; }
/// <summary> /// Initializes this <see cref="HadoopDataLoader"/>. /// </summary> public override void Initialize() { Dictionary <string, string> settings = Settings; // Handle misspelled property so previously configured adapters will still apply proper value if (settings.TryGetValue("UpdateIntervall", out string setting) && int.TryParse(setting, out _)) { settings["UpdateInterval"] = setting; } new ConnectionStringParser <ConnectionStringParameterAttribute>().ParseConnectionString(ConnectionString, this); base.Initialize(); //Generate Query m_query = $"SELECT {ValueField} AS V, {TimeStampField} AS T"; if (!string.IsNullOrEmpty(SubSecondField)) { m_query = m_query + $", {SubSecondField} AS Ticks"; } m_query = m_query + $" FROM {TableName} WHERE {TimeStampField} > '{{0}}' AND {TagQuery}"; if (!string.IsNullOrEmpty(OrderField)) { m_query = $" ORDER BY {OrderField}"; } //Create Mapping Dictionary List <string> pointTags = OutputMeasurements.Select(item => item.Metadata.TagName).ToList(); m_queryParameter = new Dictionary <Guid, List <string> >(); // Read Mapping File using (StreamReader reader = new StreamReader(FilePath.GetAbsolutePath(MappingFile))) { string line; while ((line = reader.ReadLine()) != null) { List <string> entries = line.Split(',').Select(item => item.Trim()).ToList(); string pointTag = entries[0]; int index = pointTags.FindIndex(item => item == pointTag); if (index > -1) { m_queryParameter.Add(OutputMeasurements[index].Key.SignalID, entries.Skip(1).ToList()); } } } //start Timer m_timer = new Timer(); m_timer.Interval = UpdateInterval; m_timer.AutoReset = true; m_timer.Elapsed += m_timer_Elapsed; }
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})"); }
/// <summary> /// Connects to the configured PI server. /// </summary> protected override void AttemptConnection() { m_connection = new PIConnection { ServerName = this.ServerName, UserName = this.UserName, Password = this.Password, ConnectTimeout = this.ConnectTimeout }; m_connection.Disconnected += m_connection_Disconnected; m_connection.Open(); m_dataPipe = new PIDataPipe(AFDataPipeType.Snapshot); //m_dataPipe.Subscribe(m_dataUpdateObserver); if (AutoStart && (object)OutputMeasurements != null && OutputMeasurements.Any()) { SubscribeToPointUpdates(this.OutputMeasurementKeys()); } }
private void Timer_Elapsed(object sender, ElapsedEventArgs elapsedEventArgs) { long now = DateTime.UtcNow.Ticks; long nextPublication = GetNextPublicationTime(m_lastPublication); double delta; while (nextPublication < now) { delta = Ticks.ToSeconds(nextPublication - m_lastPublication); for (int i = 0; i < OutputMeasurements.Length; i++) { Advance(i, delta); } OnNewMeasurements(OutputMeasurements .Select((measurement, index) => Measurement.Clone(measurement, GetWrappedValue(index), m_includeTime ? nextPublication : 0L)) .ToList <IMeasurement>()); m_lastPublication = nextPublication; nextPublication = GetNextPublicationTime(m_lastPublication); } }
private void PollingOperation() { if ((object)m_modbusConnection == null) { return; } try { Ticks timestamp = DateTime.UtcNow.Ticks; Dictionary <MeasurementKey, Measurement> measurements = OutputMeasurements.Select(measurement => Measurement.Clone(measurement, timestamp)).ToDictionary(measurement => measurement.Key, measurement => measurement); Group[] groups = m_sequences.SelectMany(sequence => sequence.Groups).ToArray(); int measurementsReceived = 0; int overallTasksCompleted = 0; int overallTasksCount = m_sequences.Count + 1; OnProgressUpdated(this, new ProgressUpdate(ProgressState.Processing, true, "Starting polling operation...", overallTasksCompleted, overallTasksCount)); OnStatusMessage(MessageLevel.Info, $"Executing poll operation {m_pollOperations + 1}."); // Handle read/write operations for sequence groups try { foreach (Sequence sequence in m_sequences) { foreach (Group group in sequence.Groups) { OnProgressUpdated(this, new ProgressUpdate(ProgressState.Processing, false, $"Executing poll for {sequence.Type.ToString().ToLowerInvariant()} sequence group \"{group.Type}@{group.StartAddress}\"...", 0, group.PointCount)); switch (group.Type) { case RecordType.DI: group.DataValues = ReadDiscreteInputs(group.StartAddress, group.PointCount); break; case RecordType.CO: if (sequence.Type == SequenceType.Read) { group.DataValues = ReadCoils(group.StartAddress, group.PointCount); } else { WriteCoils(group.StartAddress, group.DataValues); } break; case RecordType.IR: group.DataValues = ReadInputRegisters(group.StartAddress, group.PointCount); break; case RecordType.HR: if (sequence.Type == SequenceType.Read) { group.DataValues = ReadHoldingRegisters(group.StartAddress, group.PointCount); } else { WriteHoldingRegisters(group.StartAddress, group.DataValues); } break; } OnProgressUpdated(this, new ProgressUpdate(ProgressState.Processing, false, $"Completed poll for {sequence.Type.ToString().ToLowerInvariant()} sequence group \"{group.Type}@{group.StartAddress}\".", group.PointCount, group.PointCount)); Thread.Sleep(m_interSequenceGroupPollDelay); } OnProgressUpdated(this, new ProgressUpdate(ProgressState.Processing, true, null, ++overallTasksCompleted, overallTasksCount)); } } catch { m_deviceErrors++; throw; } OnProgressUpdated(this, new ProgressUpdate(ProgressState.Processing, false, "Processing derived values...", 0, m_derivedValues.Count)); // Calculate derived values foreach (KeyValuePair <MeasurementKey, DerivedValue> item in m_derivedValues) { DerivedValue derivedValue = item.Value; ushort[] dataValues = derivedValue.GetDataValues(groups); Measurement measurement; if (measurements.TryGetValue(item.Key, out measurement)) { // TODO: Properly interpret measurement types after GSF data type transport update switch (derivedValue.Type) { case DerivedType.String: if (derivedValue.AddressRecords.Count > 0) { m_derivedStrings[item.Key] = DeriveString(dataValues); measurementsReceived++; } else { OnStatusMessage(MessageLevel.Warning, $"No address records defined for derived String value \"{item.Key}\"."); } break; case DerivedType.Single: if (derivedValue.AddressRecords.Count > 1) { measurement.Value = DeriveSingle(dataValues[0], dataValues[1]); measurementsReceived++; } else { OnStatusMessage(MessageLevel.Warning, $"{derivedValue.AddressRecords.Count} address records defined for derived Single value \"{item.Key}\", expected 2."); } break; case DerivedType.Double: if (derivedValue.AddressRecords.Count > 3) { measurement.Value = DeriveDouble(dataValues[0], dataValues[1], dataValues[2], dataValues[3]); measurementsReceived++; } else { OnStatusMessage(MessageLevel.Warning, $"{derivedValue.AddressRecords.Count} address records defined for derived Double value \"{item.Key}\", expected 4."); } break; case DerivedType.UInt16: if (derivedValue.AddressRecords.Count > 0) { measurement.Value = dataValues[0]; measurementsReceived++; } else { OnStatusMessage(MessageLevel.Warning, $"No address records defined for UInt16 value \"{item.Key}\"."); } break; case DerivedType.Int32: if (derivedValue.AddressRecords.Count > 1) { measurement.Value = DeriveInt32(dataValues[0], dataValues[1]); measurementsReceived++; } else { OnStatusMessage(MessageLevel.Warning, $"{derivedValue.AddressRecords.Count} address records defined for derived Int32 value \"{item.Key}\", expected 2."); } break; case DerivedType.UInt32: if (derivedValue.AddressRecords.Count > 1) { measurement.Value = DeriveUInt32(dataValues[0], dataValues[1]); measurementsReceived++; } else { OnStatusMessage(MessageLevel.Warning, $"{derivedValue.AddressRecords.Count} address records defined for derived UInt32 value \"{item.Key}\", expected 2."); } break; case DerivedType.Int64: if (derivedValue.AddressRecords.Count > 3) { measurement.Value = DeriveInt64(dataValues[0], dataValues[1], dataValues[2], dataValues[3]); measurementsReceived++; } else { OnStatusMessage(MessageLevel.Warning, $"{derivedValue.AddressRecords.Count} address records defined for derived Int64 value \"{item.Key}\", expected 4."); } break; case DerivedType.UInt64: if (derivedValue.AddressRecords.Count > 3) { measurement.Value = DeriveUInt64(dataValues[0], dataValues[1], dataValues[2], dataValues[3]); measurementsReceived++; } else { OnStatusMessage(MessageLevel.Warning, $"{derivedValue.AddressRecords.Count} address records defined for derived UInt64 value \"{item.Key}\", expected 4."); } break; } } if (measurementsReceived % 20 == 0) { OnProgressUpdated(this, new ProgressUpdate(ProgressState.Processing, false, null, measurementsReceived, m_derivedValues.Count)); } } OnProgressUpdated(this, new ProgressUpdate(ProgressState.Processing, false, "Completed derived value processing.", m_derivedValues.Count, m_derivedValues.Count)); OnProgressUpdated(this, new ProgressUpdate(ProgressState.Succeeded, true, "Polling operation complete.", overallTasksCount, overallTasksCount)); OnNewMeasurements(measurements.Values.ToArray()); m_measurementsReceived += measurementsReceived; m_pollOperations++; } catch (Exception ex) { OnProgressUpdated(this, new ProgressUpdate(ProgressState.Failed, true, $"Failed during poll operation: {ex.Message}", 0, 1)); // Restart connection cycle when an exception occurs Start(); throw; } finally { m_measurementsExpected += OutputMeasurements.Length; } }
/// <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; }
// Kick start read process for historian private void StartDataReader(object state) { MeasurementKey[] requestedKeys = SupportsTemporalProcessing ? RequestedOutputMeasurementKeys : OutputMeasurements.MeasurementKeys().ToArray(); if (Enabled && (object)m_archiveReader != null && (object)requestedKeys != null && requestedKeys.Length > 0) { m_historianIDs = requestedKeys.Select(key => unchecked ((int)key.ID)).ToArray(); m_publicationTime = 0; // Start data read from historian lock (m_readTimer) { m_startTime = base.StartTimeConstraint <TimeTag.MinValue?TimeTag.MinValue : base.StartTimeConstraint> TimeTag.MaxValue ? TimeTag.MaxValue : new TimeTag(base.StartTimeConstraint); m_stopTime = base.StopTimeConstraint <TimeTag.MinValue?TimeTag.MinValue : base.StopTimeConstraint> TimeTag.MaxValue ? TimeTag.MaxValue : new TimeTag(base.StopTimeConstraint); m_dataReader = m_archiveReader.ReadData(m_historianIDs, m_startTime, m_stopTime).GetEnumerator(); m_readTimer.Enabled = m_dataReader.MoveNext(); if (m_readTimer.Enabled) { OnStatusMessage(MessageLevel.Info, "Starting historical data read..."); } else { OnStatusMessage(MessageLevel.Info, "No historical data was available to read for given timeframe."); OnProcessingComplete(); } } } else { m_readTimer.Enabled = false; OnStatusMessage(MessageLevel.Info, "No measurement keys have been requested for reading, historian reader is idle."); OnProcessingComplete(); } }
/// <summary> /// Initializes <see cref="TimeSeriesProducer"/>. /// </summary> public override void Initialize() { base.Initialize(); Dictionary <string, string> settings = Settings; string setting; int intValue; double doubleValue; // Parse required settings if (!settings.TryGetValue(nameof(Servers), out setting) || string.IsNullOrWhiteSpace(setting)) { throw new ArgumentException($"Required \"{nameof(Servers)}\" setting is missing."); } Servers = setting.Trim(); m_servers = Servers.Split(',').Select(uri => new Uri(uri)).ToArray(); // Parse optional settings if (settings.TryGetValue(nameof(Topic), out setting) && !string.IsNullOrWhiteSpace(setting)) { Topic = setting.Trim(); } else { Topic = TimeSeriesProducer.DefaultTopic; } if (settings.TryGetValue(nameof(Partitions), out setting) && int.TryParse(setting, out intValue)) { Partitions = intValue; } else { Partitions = TimeSeriesProducer.DefaultPartitions; } if (settings.TryGetValue(nameof(TrackConsumerOffset), out setting)) { TrackConsumerOffset = setting.ParseBoolean(); } else { TrackConsumerOffset = DefaultTrackConsumerIndex; } if (!settings.TryGetValue(nameof(ConsumerOffsetFileName), out setting) || string.IsNullOrWhiteSpace(setting)) { setting = Name + ".offset"; } ConsumerOffsetFileName = FilePath.GetAbsolutePath(setting); if (settings.TryGetValue(nameof(ConsumerOffsetCacheInterval), out setting) && double.TryParse(setting, out doubleValue)) { ConsumerOffsetCacheInterval = doubleValue; } else { ConsumerOffsetCacheInterval = DefaultConsumerOffsetCacheInterval; } if (settings.TryGetValue(nameof(ReadDelay), out setting) && int.TryParse(setting, out intValue)) { ReadDelay = intValue; } else { ReadDelay = DefaultReadDelay; } if (settings.TryGetValue(nameof(CacheMetadataLocally), out setting)) { CacheMetadataLocally = setting.ParseBoolean(); } else { CacheMetadataLocally = TimeSeriesProducer.DefaultCacheMetadataLocally; } if (CacheMetadataLocally) { m_cacheMetadataLocally = new LongSynchronizedOperation(() => TimeSeriesMetadata.CacheLocally(m_metadata, MetadataTopic, OnStatusMessage)) { IsBackground = true } } ; if ((object)OutputMeasurements != null && OutputMeasurements.Length > 0) { m_outputMeasurementKeys = new HashSet <MeasurementKey>(OutputMeasurements.Select(m => m.Key)); } }
// Kick start read process for historian private void StartDataReader(object state) { try { if (SupportsTemporalProcessing) { if ((object)RequestedOutputMeasurementKeys != null) { OnStatusMessage(MessageLevel.Info, $"Replaying for requested output keys: {RequestedOutputMeasurementKeys.Length:N0} defined measurements"); } else { OnStatusMessage(MessageLevel.Warning, "No measurements have been requested for playback - make sure \"; connectOnDemand=true\" is defined in the connection string for the reader."); } } MeasurementKey[] requestedKeys = SupportsTemporalProcessing ? RequestedOutputMeasurementKeys : OutputMeasurements.MeasurementKeys().ToArray(); if (Enabled && (object)m_connection != null && (object)requestedKeys != null && requestedKeys.Length > 0) { HashSet <string> tagList = new HashSet <string>(StringComparer.OrdinalIgnoreCase); var query = from row in DataSource.Tables["ActiveMeasurements"].AsEnumerable() from key in requestedKeys where row["ID"].ToString() == key.ToString() select new { Key = key, AlternateTag = row["AlternateTag"].ToString(), PointTag = row["PointTag"].ToString() }; string tagName; m_points = new PIPointList(); PIPoint point; foreach (var result in query) { tagName = result.PointTag; if (!string.IsNullOrWhiteSpace(result.AlternateTag)) { tagName = result.AlternateTag; } if (tagList.Add(tagName) && PIPoint.TryFindPIPoint(m_connection.Server, tagName, out point)) { m_tagKeyMap[point.ID] = result.Key; m_points.Add(point); } } m_publicationTime = 0; // Start data read from historian lock (m_readTimer) { m_startTime = base.StartTimeConstraint <DateTime.MinValue?DateTime.MinValue : base.StartTimeConstraint> DateTime.MaxValue ? DateTime.MaxValue : base.StartTimeConstraint; m_stopTime = base.StopTimeConstraint <DateTime.MinValue?DateTime.MinValue : base.StopTimeConstraint> DateTime.MaxValue ? DateTime.MaxValue : base.StopTimeConstraint; m_dataReader = ReadData(m_startTime, m_stopTime).GetEnumerator(); m_readTimer.Enabled = m_dataReader.MoveNext(); if (m_readTimer.Enabled) { OnStatusMessage(MessageLevel.Info, "Starting historical data read..."); } else { OnStatusMessage(MessageLevel.Info, "No historical data was available to read for given time frame."); OnProcessingComplete(); } } } else { m_readTimer.Enabled = false; OnStatusMessage(MessageLevel.Info, "No measurement keys have been requested for reading, historian reader is idle."); OnProcessingComplete(); } } catch (Exception ex) { OnProcessException(MessageLevel.Warning, new InvalidOperationException($"Could not start historical data read due to exception: {ex.Message}", ex)); } }