// Tests the given measurement to determine whether // its value indicates that the signal has been in // the alarming range for the configured delay time. private bool CheckRange(IMeasurement signal) { bool result = GetTestResult(signal); Ticks dist; if (!result) { // Keep track of the last time // the signal was in range m_lastNegative = signal.Timestamp; } else { // Get the amount of time since the // last time the value was in range dist = signal.Timestamp - m_lastNegative; // If the amount of time is larger than // the delay threshold, raise the alarm if (dist >= Ticks.FromSeconds(Delay.Value)) { return(true); } } return(false); }
// Keeps track of the signal's timestamps to determine whether a given // measurement is eligible to raise the alarm based on the delay. private bool CheckDelay(IMeasurement measurement, bool raiseCondition) { Ticks dist; if (!raiseCondition) { // Keep track of the last time // the signal failed the raise test m_lastNegative = measurement.Timestamp; } else { // Get the amount of time since the last // time the signal failed the raise test dist = measurement.Timestamp - m_lastNegative; // If the amount of time is larger than // the delay threshold, raise the alarm if (dist >= Ticks.FromSeconds(m_delay.GetValueOrDefault())) { return(true); } } return(false); }
/// <summary> /// Initializes a new instance of the <see cref="TimestampTest"/> class. /// </summary> public TimestampTest() { m_badTimestampMeasurements = new Dictionary <Ticks, LinkedList <IMeasurement> >(); m_timeToPurge = Ticks.FromSeconds(1.0); m_warnInterval = Ticks.FromSeconds(4.0); m_purgeTimer = new Timer(); m_warningTimer = new Timer(); }
/// <summary> /// Creates a new instance of the <see cref="RangeTest"/> class. /// </summary> public RangeTest() { m_outOfRangeMeasurements = new Dictionary <MeasurementKey, LinkedList <IMeasurement> >(); m_timeToPurge = Ticks.FromSeconds(1.0); m_warnInterval = Ticks.FromSeconds(4.0); m_warningTimer = new Timer(); m_purgeTimer = new Timer(); }
/// <summary> /// Gets a short one-line status of this <see cref="AdapterBase"/>. /// </summary> /// <param name="maxLength">Maximum number of available characters for display.</param> /// <returns>A short one-line summary of the current status of this <see cref="AdapterBase"/>.</returns> public override string GetShortStatus(int maxLength) { if (!Enabled || !IsConnected) { return($"Streaming for \"{Path.GetFileName(WavFileName)}\" is paused...".CenterText(maxLength)); } TimeSpan time = Ticks.FromSeconds(m_dataIndex / (double)m_sampleRate); return($"Streaming \"{Path.GetFileName(WavFileName)}\" at time {time.ToString(@"m\:ss")} / {m_audioLength.ToString(@"m\:ss")} - {time.TotalSeconds / m_audioLength.TotalSeconds:0.00%}.".CenterText(maxLength)); }
/// <summary> /// Creates a new instance of the <see cref="FlatlineTest"/> class. /// </summary> public FlatlineTest() { m_minFlatline = Ticks.FromSeconds(4); m_warnInterval = Ticks.FromSeconds(4); m_emailInterval = Ticks.FromSeconds(3600); m_smtpServer = Mail.DefaultSmtpServer; m_lastChange = new Dictionary <MeasurementKey, IMeasurement>(); m_lastNotified = new Dictionary <MeasurementKey, Ticks>(); m_warningTimer = new Timer(); }
/// <summary> /// Initializes this <see cref="RangeTest"/>. /// </summary> public override void Initialize() { base.Initialize(); string errorMessage = "{0} is missing from Settings - Example: lowRange=59.95; highRange=60.05"; bool rangeSet = false; Dictionary <string, string> settings = Settings; string setting; if (settings.TryGetValue("signalType", out m_signalType)) { rangeSet = TrySetRange(m_signalType); } if (!rangeSet) { // Load required parameters if (!settings.TryGetValue("lowRange", out setting)) { throw new ArgumentException(string.Format(errorMessage, "lowRange")); } m_lowRange = double.Parse(setting); if (!settings.TryGetValue("highRange", out setting)) { throw new ArgumentException(string.Format(errorMessage, "highRange")); } m_highRange = double.Parse(setting); rangeSet = true; } // Load optional parameters if (settings.TryGetValue("timeToPurge", out setting)) { m_timeToPurge = Ticks.FromSeconds(double.Parse(setting)); } if (settings.TryGetValue("warnInterval", out setting)) { m_warnInterval = Ticks.FromSeconds(double.Parse(setting)); } m_warningTimer.Interval = m_warnInterval.ToMilliseconds(); m_warningTimer.Elapsed += m_warningTimer_Elapsed; m_purgeTimer.Interval = m_timeToPurge.ToMilliseconds(); m_purgeTimer.Elapsed += m_purgeTimer_Elapsed; }
static GlobalDeviceStatistics() { CategorizedSettingsElementCollection settings = ConfigurationFile.Current.Settings["systemSettings"]; settings.Add("MedianTimestampDeviation", DefaultMedianTimestampDeviation, "Maximum allowed deviation from median timestamp, in seconds, for consideration in average timestamp calculation."); s_medianTimestampDeviation = new BigInteger(Ticks.FromSeconds(settings["MedianTimestampDeviation"].ValueAs(DefaultMedianTimestampDeviation)).Value); s_latestDeviceTimes = new ConcurrentDictionary <IDevice, LatestDeviceTime>(); s_bigTwo = new BigInteger(2); s_bigMaxLong = new BigInteger(long.MaxValue); StatisticsEngine.SourceRegistered += StatisticsEngine_SourceRegistered; StatisticsEngine.SourceUnregistered += StatisticsEngine_SourceUnregistered; StatisticsEngine.BeforeCalculate += StatisticsEngine_BeforeCalculate; }
// Indicates whether the given measurement has maintained the same // value for at least a number of seconds defined by the delay. private bool RaiseIfFlatline(IMeasurement measurement) { long dist, diff; if (measurement.Value != m_lastValue) { m_lastChanged = measurement.Timestamp; m_lastValue = measurement.Value; } dist = Ticks.FromSeconds(Delay.GetValueOrDefault()); diff = measurement.Timestamp - m_lastChanged; return(diff >= dist); }
// Determines whether the given value has // flatlined over the configured delay interval. private bool CheckFlatline(IMeasurement signal) { long dist, diff; if (signal.Value != m_lastValue) { m_lastChanged = signal.Timestamp; m_lastValue = signal.Value; } dist = Ticks.FromSeconds(Delay.Value); diff = signal.Timestamp - m_lastChanged; return(diff >= dist); }
private void m_activityMonitor_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { if (DateTime.UtcNow.Ticks - m_lastFileChangedTime > Ticks.FromSeconds(60.0D)) { m_activityMonitor.Enabled = false; try { m_process?.Kill(); } catch (Exception ex) { Program.Log("Error while trying to kill no responsive downloader: " + ex.Message); throw; } } }
private int ReadTimestamp(byte[] buffer) { int index = 0; // Read sample index uint sample = LittleEndian.ToUInt32(buffer, index); index += 4; // Get timestamp of this record Timestamp = DateTime.MinValue; // If sample rates are defined, this is the preferred method for timestamp resolution if (InferTimeFromSampleRates && m_schema.SampleRates.Length > 0) { // Find rate for given sample SampleRate sampleRate = m_schema.SampleRates.LastOrDefault(sr => sample <= sr.EndSample); if (sampleRate.Rate > 0.0D) { Timestamp = new DateTime(Ticks.FromSeconds(1.0D / sampleRate.Rate * sample) + m_schema.StartTime.Value); } } // Read microsecond timestamp uint microseconds = LittleEndian.ToUInt32(buffer, index); index += 4; // Fall back on specified microsecond time if (Timestamp == DateTime.MinValue) { Timestamp = new DateTime(Ticks.FromMicroseconds(microseconds * m_schema.TimeFactor) + m_schema.StartTime.Value); } // Apply timestamp offset to restore UTC timezone if (AdjustToUTC) { TimeOffset offset = Schema.TimeCode ?? new TimeOffset(); Timestamp = new DateTime(Timestamp.Ticks + offset.TickOffset, DateTimeKind.Utc); } return(index); }
/// <summary> /// Initializes <see cref="FlatlineTest"/>. /// </summary> public override void Initialize() { base.Initialize(); Dictionary <string, string> settings = Settings; string setting; if (settings.TryGetValue("minFlatline", out setting)) { m_minFlatline = Ticks.FromSeconds(double.Parse(setting)); } if (settings.TryGetValue("warnInterval", out setting)) { m_warnInterval = Ticks.FromSeconds(double.Parse(setting)); } if (settings.TryGetValue("adminEmailAddress", out m_adminEmailAddress)) { // emailInterval is entered in minutes. if (settings.TryGetValue("emailInterval", out setting)) { m_emailInterval = Ticks.FromSeconds(long.Parse(setting) * 60L); } } if (settings.TryGetValue("smtpServer", out setting)) { m_smtpServer = setting; } m_warningTimer.Interval = m_warnInterval.ToMilliseconds(); m_warningTimer.Elapsed += m_warningTimer_Elapsed; m_flatlineService = new FlatlineService(this); m_flatlineService.SettingsCategory = base.Name + m_flatlineService.SettingsCategory; m_flatlineService.ServiceProcessException += m_flatlineService_ServiceProcessException; m_flatlineService.Initialize(); InitializeLastChange(); }
// Static Methods /// <summary> /// Gets proper NTP offset based on <paramref name="seconds"/> value, see RFC-2030. /// </summary> /// <param name="seconds">Seconds value.</param> /// <returns>Proper NTP offset.</returns> protected static long GetBaseDateOffsetTicks(double seconds) { return(GetBaseDateOffsetTicks(Ticks.FromSeconds(seconds))); }
// Internal Functions private void ReadArchive(object state) { try { double timeRange = (m_settings.EndTime - m_settings.StartTime).TotalSeconds; long receivedPoints = 0; long processedDataBlocks = 0; long duplicatePoints = 0; Ticks operationTime; Ticks operationStartTime; DataPoint point = new DataPoint(); DateTime firstTimestamp = new DateTime(0L); DateTime lastTimestamp = new DateTime(0L); using (Algorithm algorithm = new Algorithm()) { algorithm.ShowMessage = ShowUpdateMessage; algorithm.MessageInterval = m_settings.MessageInterval; algorithm.StartTime = m_settings.StartTime; algorithm.EndTime = m_settings.EndTime; algorithm.FrameRate = m_settings.FrameRate; algorithm.TimeRange = timeRange; algorithm.Log = m_log; // Load historian meta-data ShowUpdateMessage(">>> Loading source connection metadata..."); operationStartTime = DateTime.UtcNow.Ticks; algorithm.Metadata = MetadataRecord.Query(m_settings.HostAddress, m_settings.MetadataPort, m_settings.MetadataTimeout); operationTime = DateTime.UtcNow.Ticks - operationStartTime; ShowUpdateMessage("*** Metadata Load Complete ***"); ShowUpdateMessage($"Total metadata load time {operationTime.ToElapsedTimeString(3)}..."); ShowUpdateMessage(">>> Processing filter expression for metadata..."); operationStartTime = DateTime.UtcNow.Ticks; MeasurementKey[] inputKeys = AdapterBase.ParseInputMeasurementKeys(MetadataRecord.Metadata, false, textBoxPointList.Text, "MeasurementDetail"); List <ulong> pointIDList = inputKeys.Select(key => (ulong)key.ID).ToList(); operationTime = DateTime.UtcNow.Ticks - operationStartTime; // Allow algorithm to augment (or even replace) point ID list as provided by user algorithm.AugmentPointIDList(pointIDList); ShowUpdateMessage($">>> Historian read will be for {pointIDList.Count:N0} points based on provided meta-data expression and algorithm augmentation."); // Reduce metadata to filtered point list ShowUpdateMessage($">>> Reducing metadata to the {pointIDList.Count:N0} defined points..."); List <MetadataRecord> records = new List <MetadataRecord>(); foreach (ulong pointID in pointIDList) { MetadataRecord record = algorithm.Metadata.FirstOrDefault(metadata => metadata.PointID == pointID); if ((object)record != null) { records.Add(record); } } algorithm.Metadata = records; ShowUpdateMessage("*** Filter Expression Processing Complete ***"); ShowUpdateMessage($"Total filter expression processing time {operationTime.ToElapsedTimeString(3)}..."); ShowUpdateMessage(">>> Initializing algorithm..."); algorithm.Initialize(); ShowUpdateMessage(">>> Starting archive read..."); // Start historian data read operationStartTime = DateTime.UtcNow.Ticks; using (SnapDBClient historianClient = new SnapDBClient(m_settings.HostAddress, m_settings.DataPort, m_settings.InstanceName, m_settings.StartTime, m_settings.EndTime, m_settings.FrameRate, pointIDList)) { // Scan to first record if (!historianClient.ReadNext(point)) { throw new InvalidOperationException("No data for specified time range in openHistorian connection!"); } ulong currentTimestamp; receivedPoints++; while (!m_formClosing) { int timeComparison; bool readSuccess = true; // Create a new data block for current timestamp and load first/prior point Dictionary <ulong, DataPoint> dataBlock = new Dictionary <ulong, DataPoint> { [point.PointID] = point.Clone() }; currentTimestamp = point.Timestamp; // Load remaining data for current timestamp do { // Scan to next record if (!historianClient.ReadNext(point)) { readSuccess = false; break; } receivedPoints++; timeComparison = DataPoint.CompareTimestamps(point.Timestamp, currentTimestamp, m_settings.FrameRate); if (timeComparison == 0) { // Timestamps are compared based on configured frame rate - if archived data rate is // higher than configured frame rate, then data block will contain only latest values if (dataBlock.ContainsKey(point.PointID)) { duplicatePoints++; } dataBlock[point.PointID] = point.Clone(); } }while (timeComparison == 0); // Finished with data read if (!readSuccess) { ShowUpdateMessage(">>> End of data in range encountered..."); break; } if (++processedDataBlocks % m_settings.MessageInterval == 0) { ShowUpdateMessage($"{Environment.NewLine}{receivedPoints:N0} points{(duplicatePoints > 0 ? $", which included {duplicatePoints:N0} duplicates," : "")} read so far averaging {receivedPoints / (DateTime.UtcNow.Ticks - operationStartTime).ToSeconds():N0} points per second."); UpdateProgressBar((int)((1.0D - new Ticks(m_settings.EndTime.Ticks - (long)point.Timestamp).ToSeconds() / timeRange) * 100.0D)); } try { lastTimestamp = new DateTime((long)currentTimestamp); if (firstTimestamp.Ticks == 0L) { firstTimestamp = lastTimestamp; } // Analyze data block algorithm.Execute(lastTimestamp, dataBlock.Values.ToArray()); } catch (Exception ex) { ShowUpdateMessage($"ERROR: Algorithm exception: {ex.Message}"); m_log.Publish(MessageLevel.Error, "AlgorithmError", "Failed while processing data from the historian", exception: ex); } } operationTime = DateTime.UtcNow.Ticks - operationStartTime; if (m_formClosing) { ShowUpdateMessage("*** Historian Read Canceled ***"); UpdateProgressBar(0); } else { ShowUpdateMessage("*** Historian Read Complete ***"); UpdateProgressBar(100); } algorithm.Complete(); // Show some operational statistics long expectedPoints = (long)(timeRange * m_settings.FrameRate * algorithm.Metadata.Count); double dataCompleteness = Math.Round(receivedPoints / (double)expectedPoints * 100000.0D) / 1000.0D; string overallSummary = $"Total processing time {operationTime.ToElapsedTimeString(3)} at {receivedPoints / operationTime.ToSeconds():N0} points per second.{Environment.NewLine}" + $"{Environment.NewLine}" + $" Meta-data points: {algorithm.Metadata.Count}{Environment.NewLine}" + $" Time-span covered: {timeRange:N0} seconds: {Ticks.FromSeconds(timeRange).ToElapsedTimeString(2)}{Environment.NewLine}" + $" Processed timestamps: {processedDataBlocks:N0}{Environment.NewLine}" + $" Expected points: {expectedPoints:N0} @ {m_settings.FrameRate:N0} samples per second{Environment.NewLine}" + $" Received points: {receivedPoints:N0}{Environment.NewLine}" + $" Duplicate points: {duplicatePoints:N0}{Environment.NewLine}" + $" Data completeness: {dataCompleteness:N3}%{Environment.NewLine}" + $" First timestamp with data: {firstTimestamp:yyyy-MM-dd HH:mm:ss.fff}{Environment.NewLine}" + $" Last timestamp with data: {lastTimestamp:yyyy-MM-dd HH:mm:ss.fff}{Environment.NewLine}"; ShowUpdateMessage(overallSummary); } } } catch (Exception ex) { ShowUpdateMessage($"!!! Failure during historian read: {ex.Message}"); m_log.Publish(MessageLevel.Error, "HistorianDataRead", "Failed while reading data from the historian", exception: ex); } finally { SetGoButtonEnabledState(true); } }
/// <summary> /// Gets a short one-line status of this <see cref="AdapterBase"/>. /// </summary> /// <param name="maxLength">Maximum number of available characters for display.</param> /// <returns>A short one-line summary of the current status of this <see cref="AdapterBase"/>.</returns> public override string GetShortStatus(int maxLength) { TimeSpan time = Ticks.FromSeconds(m_dataIndex / (double)m_sampleRate); return(string.Format("Streaming {0} at time {1} / {2} - {3:0.00%}.", Path.GetFileName(WavFileName), time.ToString(@"m\:ss"), m_audioLength.ToString(@"m\:ss"), time.TotalSeconds / m_audioLength.TotalSeconds)); }
// Handle binary file read private bool ReadNextBinary() { FileStream currentFile = m_fileStreams[m_streamIndex]; int recordLength = m_schema.BinaryRecordLength; byte[] buffer = new byte[recordLength]; // Read next record from file int bytesRead = currentFile.Read(buffer, 0, recordLength); // See if we have reached the end of this file if (bytesRead == 0) { m_streamIndex++; // There is more to read if there is another file return(m_streamIndex < m_fileStreams.Length && ReadNext()); } if (bytesRead == recordLength) { int index = 0; // Read sample index uint sample = LittleEndian.ToUInt32(buffer, index); index += 4; // Get timestamp of this record m_timestamp = DateTime.MinValue; // If sample rates are defined, this is the preferred method for timestamp resolution if (m_inferTimeFromSampleRates && m_schema.SampleRates.Length > 0) { // Find rate for given sample SampleRate sampleRate = m_schema.SampleRates.LastOrDefault(sr => sample <= sr.EndSample); if (sampleRate.Rate > 0.0D) { m_timestamp = new DateTime(Ticks.FromSeconds(1.0D / sampleRate.Rate * sample) + m_schema.StartTime.Value); } } // Read microsecond timestamp uint microseconds = LittleEndian.ToUInt32(buffer, index); index += 4; // Fall back on specified microsecond time if (m_timestamp == DateTime.MinValue) { m_timestamp = new DateTime(Ticks.FromMicroseconds(microseconds * m_schema.TimeFactor) + m_schema.StartTime.Value); } // Parse all analog record values for (int i = 0; i < m_schema.AnalogChannels.Length; i++) { // Read next value m_values[i] = LittleEndian.ToInt16(buffer, index) * m_schema.AnalogChannels[i].Multiplier + m_schema.AnalogChannels[i].Adder; index += 2; } int valueIndex = m_schema.AnalogChannels.Length; int digitalWords = m_schema.DigitalWords; ushort digitalWord; for (int i = 0; i < digitalWords; i++) { // Read next digital word digitalWord = LittleEndian.ToUInt16(buffer, index); index += 2; // Distribute each bit of digital word through next 16 digital values for (int j = 0; j < 16 && valueIndex < m_values.Length; j++, valueIndex++) { m_values[valueIndex] = digitalWord.CheckBits(BitExtensions.BitVal(j)) ? 1.0D : 0.0D; } } } else { throw new InvalidOperationException("Failed to read enough bytes from COMTRADE file for a record as defined by schema - possible schema/data file mismatch or file corruption."); } return(true); }
// Handle ASCII file read private bool ReadNextAscii() { if ((object)m_fileReaders == null) { m_fileReaders = new StreamReader[m_fileStreams.Length]; for (int i = 0; i < m_fileStreams.Length; i++) { m_fileReaders[i] = new StreamReader(m_fileStreams[i]); } } // Read next line of record values StreamReader reader = m_fileReaders[m_streamIndex]; string line = reader.ReadLine(); string[] elems = ((object)line != null) ? line.Split(',') : null; // See if we have reached the end of this file if ((object)elems == null || elems.Length != m_values.Length + 2) { if (reader.EndOfStream) { m_streamIndex++; // There is more to read if there is another file return(m_streamIndex < m_fileStreams.Length && ReadNext()); } throw new InvalidOperationException("COMTRADE schema does not match number of elements found in ASCII data file."); } // Parse row of data uint sample = uint.Parse(elems[0]); // Get timestamp of this record m_timestamp = DateTime.MinValue; // If sample rates are defined, this is the preferred method for timestamp resolution if (m_inferTimeFromSampleRates && m_schema.SampleRates.Length > 0) { // Find rate for given sample SampleRate sampleRate = m_schema.SampleRates.LastOrDefault(sr => sample <= sr.EndSample); if (sampleRate.Rate > 0.0D) { m_timestamp = new DateTime(Ticks.FromSeconds(1.0D / sampleRate.Rate * sample) + m_schema.StartTime.Value); } } // Fall back on specified microsecond time if (m_timestamp == DateTime.MinValue) { m_timestamp = new DateTime(Ticks.FromMicroseconds(uint.Parse(elems[1]) * m_schema.TimeFactor) + m_schema.StartTime.Value); } // Parse all record values for (int i = 0; i < m_values.Length; i++) { m_values[i] = double.Parse(elems[i + 2]); if (i < m_schema.AnalogChannels.Length) { m_values[i] *= m_schema.AnalogChannels[i].Multiplier; m_values[i] += m_schema.AnalogChannels[i].Adder; } } return(true); }
public void Ages() { UT_INIT(); Log.SetVerbosity(new ConsoleLogger(), Verbosity.Verbose, "/"); Log.MapThreadName("UnitTest"); Log.SetDomain("TickWatch", Scope.Method); TickWatch tt = new TickWatch(); // minimum time measuring { tt.Start(); tt.Sample(); tt.Reset(); // we need to do this once before, otherwise C# might be // very slow. Obviously the optimizer... tt.Start(); tt.Sample(); long ttAverageInNanos = tt.GetAverage().InNanos(); long ttAverageInMicros = tt.GetAverage().InMicros(); long ttAverageInMillis = tt.GetAverage().InMillis(); Log.Info("TickWatch minimum measurement nanos: " + ttAverageInNanos); Log.Info("TickWatch minimum measurement micros: " + ttAverageInMicros); Log.Info("TickWatch minimum measurement millis: " + ttAverageInMillis); UT_TRUE(ttAverageInNanos < 5000); UT_TRUE(ttAverageInMicros <= 5); UT_TRUE(ttAverageInMillis == 0); } // minimum sleep time measuring { tt.Reset(); for (int i = 0; i < 100; i++) { ALIB.SleepNanos(1); tt.Sample(); } Ticks avg = tt.GetAverage(); Log.Info("100 probes of 1 ns of sleep leads to average sleep time of " + avg.InNanos() + " ns"); } // sleep two times 20 ms and probe it to an average { tt.Reset(); tt.Start(); ALIB.SleepMillis(20); tt.Sample(); ALIB.SleepMillis(80); tt.Start(); ALIB.SleepMillis(20); tt.Sample(); long cnt = tt.GetSampleCnt(); long avg = tt.GetAverage().InMillis(); double hertz = tt.GetAverage().InHertz(1); Log.Info("TickWatch sum is " + tt.GetCumulated().InMillis() + " after " + cnt + " times 20 ms sleep"); Log.Info(" average is: " + avg + " ms"); Log.Info(" in Hertz: " + hertz); UT_TRUE(hertz < 53); UT_TRUE(hertz > 30); // should work even on heavily loaded machines UT_EQ(2, cnt); UT_TRUE(avg >= 18); UT_TRUE(avg < 45); // should work even on heavily loaded machines } // Ticks Since { Ticks tt1 = new Ticks(); tt1.FromSeconds(1000); Ticks tt2 = new Ticks(); tt2.FromSeconds(1001); UT_TRUE(tt2.Since(tt1).InMillis() == 1000L); UT_TRUE(tt2.Since(tt1).InMicros() == 1000L * 1000L); UT_TRUE(tt2.Since(tt1).InNanos() == 1000L * 1000L * 1000L); } }
private void ProcessMeasurements() { List <IMeasurement> measurements = new List <IMeasurement>((int)(Ticks.ToSeconds(GapThreshold) * m_sampleRate * m_channels * 1.1D)); LittleBinaryValue[] sample; while (Enabled) { try { SpinWait spinner = new SpinWait(); // Determine what time it is now long now = DateTime.UtcNow.Ticks; // Assign a timestamp to the next sample based on its location // in the file relative to the other samples in the file long timestamp = m_startTime + (m_dataIndex * Ticks.PerSecond / m_sampleRate); if (now - timestamp > GapThreshold) { // Reset the start time and delay next transmission in an attempt to catch up m_startTime = now - (m_dataIndex * Ticks.PerSecond / m_sampleRate) + Ticks.FromSeconds(RecoveryDelay); timestamp = now; OnStatusMessage(MessageLevel.Info, "Start time reset."); } // Keep generating measurements until // we catch up to the current time. while (timestamp < now) { sample = m_dataReader.GetNextSample(); // If the sample is null, we've reached the end of the file - close and reopen, // resetting the data index and start time if (sample == null) { m_dataReader.Close(); m_dataReader.Dispose(); m_dataReader = OpenWaveDataReader(); m_dataIndex = 0; m_startTime = timestamp; sample = m_dataReader.GetNextSample(); } // Create new measurements, one for each channel, and add them to the measurements list for (int i = 0; i < m_channels; i++) { measurements.Add(Measurement.Clone(OutputMeasurements[i], sample[i].ConvertToType(TypeCode.Double), timestamp)); } // Update the data index and recalculate the assigned timestamp for the next sample m_dataIndex++; timestamp = m_startTime + (m_dataIndex * Ticks.PerSecond / m_sampleRate); } OnNewMeasurements(measurements); measurements.Clear(); while (DateTime.UtcNow.Ticks - timestamp <= GapThreshold / 100) { // Ahead of schedule -- pause for a moment spinner.SpinOnce(); } } catch (Exception ex) { OnProcessException(MessageLevel.Warning, ex); } } }
/// <summary> /// Queues a collection of measurements for processing. /// </summary> /// <param name="measurements">Collection of measurements to queue for processing.</param> /// <remarks> /// Measurements are filtered against the defined <see cref="InputMeasurementKeys"/> so we override method /// so that dynamic updates to keys will be synchronized with filtering to prevent interference. /// </remarks> public override void QueueMeasurementsForProcessing(IEnumerable <IMeasurement> measurements) { if ((object)measurements == null) { return; } if (!m_startTimeSent && measurements.Any()) { m_startTimeSent = true; IMeasurement measurement = measurements.FirstOrDefault(m => (object)m != null); Ticks timestamp = 0; if ((object)measurement != null) { timestamp = measurement.Timestamp; } m_parent.SendDataStartTime(m_clientID, timestamp); } if (m_isNaNFiltered) { measurements = measurements.Where(measurement => !double.IsNaN(measurement.Value)); } if (!measurements.Any() || !Enabled) { return; } if (TrackLatestMeasurements) { double publishInterval; // Keep track of latest measurements base.QueueMeasurementsForProcessing(measurements); publishInterval = (m_publishInterval > 0) ? m_publishInterval : LagTime; if (DateTime.UtcNow.Ticks > m_lastPublishTime + Ticks.FromSeconds(publishInterval)) { List <IMeasurement> currentMeasurements = new List <IMeasurement>(); Measurement newMeasurement; // Create a new set of measurements that represent the latest known values setting value to NaN if it is old foreach (TemporalMeasurement measurement in LatestMeasurements) { newMeasurement = new Measurement { Key = measurement.Key, Value = measurement.GetValue(RealTime), Adder = measurement.Adder, Multiplier = measurement.Multiplier, Timestamp = measurement.Timestamp, StateFlags = measurement.StateFlags }; currentMeasurements.Add(newMeasurement); } // Order measurements by signal type for better compression when enabled if (m_usePayloadCompression) { ProcessMeasurements(currentMeasurements.OrderBy(measurement => measurement.GetSignalType(DataSource))); } else { ProcessMeasurements(currentMeasurements); } } } else { // Order measurements by signal type for better compression when enabled if (m_usePayloadCompression) { ProcessMeasurements(measurements.OrderBy(measurement => measurement.GetSignalType(DataSource))); } else { ProcessMeasurements(measurements); } } }
/// <summary> /// Creates a new <see cref="CommonFrameHeader"/> from given <paramref name="buffer"/>. /// </summary> /// <param name="parseWordCountFromByte">Defines flag that interprets word count in packet header from a byte instead of a word.</param> /// <param name="usePhasorDataFileFormat">Defines flag that determines if source data is in the Phasor Data File Format (i.e., a DST file).</param> /// <param name="configFrame">Previously parsed configuration frame, if available.</param> /// <param name="buffer">Buffer that contains data to parse.</param> /// <param name="startIndex">Start index into buffer where valid data begins.</param> /// <param name="length">Maximum length of valid data from start index.</param> public CommonFrameHeader(bool parseWordCountFromByte, bool usePhasorDataFileFormat, ConfigurationFrame configFrame, byte[] buffer, int startIndex, int length) { uint secondOfCentury; // Determines if format is for DST file or streaming data m_usePhasorDataFileFormat = usePhasorDataFileFormat; if (m_usePhasorDataFileFormat) { // Handle phasor file format data protocol steps if (buffer[startIndex] == PhasorProtocols.Common.SyncByte && buffer[startIndex + 1] == Common.PhasorFileFormatFlag) { // Bail out and leave frame length zero if there's not enough buffer to parse complete fixed portion of header if (length >= DstHeaderFixedLength) { // Read full DST header m_packetNumber = (byte)BPAPDCstream.FrameType.ConfigurationFrame; m_fileType = (FileType)buffer[startIndex + 2]; m_fileVersion = (FileVersion)buffer[startIndex + 3]; m_sourceID = Encoding.ASCII.GetString(buffer, startIndex + 4, 4); uint headerLength = BigEndian.ToUInt32(buffer, startIndex + 8); secondOfCentury = BigEndian.ToUInt32(buffer, startIndex + 12); switch (m_fileType) { case FileType.PdcNtp: RoughTimestamp = new NtpTimeTag(secondOfCentury, 0).ToDateTime().Ticks; break; case FileType.PdcUnix: RoughTimestamp = new UnixTimeTag(secondOfCentury).ToDateTime().Ticks; break; default: RoughTimestamp = 0; break; } m_startSample = BigEndian.ToUInt32(buffer, startIndex + 16); m_sampleInterval = BigEndian.ToUInt16(buffer, startIndex + 20); m_sampleRate = BigEndian.ToUInt16(buffer, startIndex + 22); m_rowLength = BigEndian.ToUInt32(buffer, startIndex + 24); m_totalRows = BigEndian.ToUInt32(buffer, startIndex + 28); secondOfCentury = BigEndian.ToUInt32(buffer, startIndex + 32); switch (m_fileType) { case FileType.PdcNtp: m_triggerTime = new NtpTimeTag(secondOfCentury, 0).ToDateTime().Ticks; break; case FileType.PdcUnix: m_triggerTime = new UnixTimeTag(secondOfCentury).ToDateTime().Ticks; break; default: m_triggerTime = 0; break; } m_triggerSample = BigEndian.ToUInt32(buffer, startIndex + 36); m_preTriggerRows = BigEndian.ToUInt32(buffer, startIndex + 40); m_triggerPMU = BigEndian.ToUInt16(buffer, startIndex + 44); m_triggerType = BigEndian.ToUInt16(buffer, startIndex + 46); m_userInformation = Encoding.ASCII.GetString(buffer, startIndex + 48, 80).Trim(); m_pmuCount = BigEndian.ToUInt32(buffer, startIndex + 128); FrameLength = unchecked ((ushort)headerLength); } } else { // Must assume this is a data row if there are no sync bytes m_packetNumber = (byte)BPAPDCstream.FrameType.DataFrame; m_rowFlags = BigEndian.ToUInt32(buffer, startIndex); if (configFrame is null) { FrameLength = FixedLength; } else { uint sampleIndex = configFrame.SampleIndex; CommonFrameHeader configFrameHeader = configFrame.CommonHeader; if (configFrameHeader is null) { FrameLength = FixedLength; } else { // Assign row length to make sure parser knows how much data it needs FrameLength = unchecked ((ushort)configFrameHeader.RowLength); // Calculate timestamp as offset plus sample index * frame rate RoughTimestamp = configFrameHeader.RoughTimestamp + Ticks.FromSeconds(sampleIndex * (1.0D / configFrameHeader.FrameRate)); } // Increment sample index for next row configFrame.SampleIndex = sampleIndex + 1; } } } else { // Handle streaming data protocol steps if (buffer[startIndex] != PhasorProtocols.Common.SyncByte) { throw new InvalidOperationException($"Bad data stream, expected sync byte 0xAA as first byte in BPA PDCstream frame, got 0x{buffer[startIndex].ToString("X").PadLeft(2, '0')}"); } // Get packet number m_packetNumber = buffer[startIndex + 1]; // Some older streams have a bad word count (e.g., some data streams have a 0x01 as the third byte // in the stream - this should be a 0x00 to make the word count come out correctly). The following // compensates for this erratic behavior m_wordCount = parseWordCountFromByte ? buffer[startIndex + 3] : BigEndian.ToUInt16(buffer, startIndex + 2); // If this is a data frame get a rough timestamp down to the second (full parse will get accurate timestamp), this way // data frames that don't get fully parsed because configuration hasn't been received will still show a timestamp if (m_packetNumber > 0 && length > 8) { secondOfCentury = BigEndian.ToUInt32(buffer, startIndex + 4); // Until configuration is available, we make a guess at time tag type - this will just be // used for display purposes until a configuration frame arrives. If second of century // is greater than 3155673600 (SOC value for NTP timestamp 1/1/2007), then this is likely // an NTP time stamp (else this is a Unix time tag for the year 2069 - not likely). RoughTimestamp = secondOfCentury > 3155673600 ? new NtpTimeTag(secondOfCentury, 0).ToDateTime().Ticks : new UnixTimeTag(secondOfCentury).ToDateTime().Ticks; } } }
// Handle ASCII file read private bool ReadNextAscii() { // For ASCII files, we wrap file streams with file readers if ((object)m_fileReaders == null) { m_fileReaders = new StreamReader[m_fileStreams.Length]; for (int i = 0; i < m_fileStreams.Length; i++) { m_fileReaders[i] = new StreamReader(m_fileStreams[i]); } } // Read next line of record values StreamReader reader = m_fileReaders[m_streamIndex]; string line = reader.ReadLine(); string[] elems = ((object)line != null) ? line.Split(',') : null; // See if we have reached the end of this file if ((object)elems == null || elems.Length != Values.Length + 2) { if (reader.EndOfStream) { return(ReadNextFile()); } throw new InvalidOperationException("COMTRADE schema does not match number of elements found in ASCII data file."); } // Parse row of data uint sample = uint.Parse(elems[0]); // Get timestamp of this record Timestamp = DateTime.MinValue; // If sample rates are defined, this is the preferred method for timestamp resolution if (InferTimeFromSampleRates && m_schema.SampleRates.Length > 0) { // Find rate for given sample SampleRate sampleRate = m_schema.SampleRates.LastOrDefault(sr => sample <= sr.EndSample); if (sampleRate.Rate > 0.0D) { Timestamp = new DateTime(Ticks.FromSeconds(1.0D / sampleRate.Rate * sample) + m_schema.StartTime.Value); } } // Fall back on specified microsecond time if (Timestamp == DateTime.MinValue) { Timestamp = new DateTime(Ticks.FromMicroseconds(uint.Parse(elems[1]) * m_schema.TimeFactor) + m_schema.StartTime.Value); } // Apply timestamp offset to restore UTC timezone if (AdjustToUTC) { TimeOffset offset = Schema.TimeCode ?? new TimeOffset(); Timestamp = new DateTime(Timestamp.Ticks + offset.TickOffset, DateTimeKind.Utc); } // Parse all record values for (int i = 0; i < Values.Length; i++) { Values[i] = double.Parse(elems[i + 2]); if (i < m_schema.AnalogChannels.Length) { Values[i] = AdjustValue(Values[i], i); } } return(true); }
/// <summary> /// Queues a collection of measurements for processing. /// </summary> /// <param name="measurements">Collection of measurements to queue for processing.</param> /// <remarks> /// Measurements are filtered against the defined <see cref="InputMeasurementKeys"/> so we override method /// so that dyanmic updates to keys will be synchronized with filtering to prevent interference. /// </remarks> public override void QueueMeasurementsForProcessing(IEnumerable <IMeasurement> measurements) { if (!m_startTimeSent && (object)measurements != null && measurements.Any()) { m_startTimeSent = true; IMeasurement measurement = measurements.FirstOrDefault(m => m != null); Ticks timestamp = 0; if (measurement != null) { timestamp = measurement.Timestamp; } m_parent.SendDataStartTime(m_clientID, timestamp); } if (ProcessMeasurementFilter) { List <IMeasurement> filteredMeasurements = new List <IMeasurement>(); lock (this) { foreach (IMeasurement measurement in measurements) { if (IsInputMeasurement(measurement.Key)) { filteredMeasurements.Add(measurement); } } } measurements = filteredMeasurements; } if (measurements.Any() && Enabled) { if (TrackLatestMeasurements) { double publishInterval; // Keep track of latest measurements base.QueueMeasurementsForProcessing(measurements); publishInterval = (m_publishInterval > 0) ? m_publishInterval : LagTime; if (DateTime.UtcNow.Ticks > m_lastPublishTime + Ticks.FromSeconds(publishInterval)) { List <IMeasurement> currentMeasurements = new List <IMeasurement>(); Measurement newMeasurement; // Create a new set of measurements that represent the latest known values setting value to NaN if it is old foreach (TemporalMeasurement measurement in LatestMeasurements) { newMeasurement = new Measurement() { ID = measurement.ID, Key = measurement.Key, Value = measurement.GetValue(RealTime), Adder = measurement.Adder, Multiplier = measurement.Multiplier, Timestamp = measurement.Timestamp, StateFlags = measurement.StateFlags }; currentMeasurements.Add(newMeasurement); } // Publish latest data values... if ((object)m_processQueue != null) { m_processQueue.Enqueue(currentMeasurements); } } } else { // Publish unsynchronized on data receipt otherwise... m_processQueue.Enqueue(measurements); } } }
public void Basics() { UT_INIT(); Log.SetVerbosity(new ConsoleLogger(), Verbosity.Verbose, "/"); Log.MapThreadName("UnitTest"); Log.SetDomain("TickWatch", Scope.Method); Log.Info("\n### TicksBasics ###"); // check times { Ticks t = new Ticks(); t.FromNanos(42); // beyond resolution in C#: UT_EQ( t.InNanos(), 42L); UT_EQ(t.InMicros(), 0L); UT_EQ(t.InMillis(), 0L); UT_EQ(t.InSeconds(), 0L); t.FromMicros(42); UT_EQ(t.InNanos(), 42000L); UT_EQ(t.InMicros(), 42L); UT_EQ(t.InMillis(), 0L); UT_EQ(t.InSeconds(), 0L); t.FromMillis(42); UT_EQ(t.InNanos(), 42000000L); UT_EQ(t.InMicros(), 42000L); UT_EQ(t.InMillis(), 42L); UT_EQ(t.InSeconds(), 0L); t.FromSeconds(42); UT_EQ(t.InNanos(), 42000000000L); UT_EQ(t.InMicros(), 42000000L); UT_EQ(t.InMillis(), 42000L); UT_EQ(t.InSeconds(), 42L); Ticks diff = new Ticks(); diff.FromMillis(100); t.Add(diff); UT_EQ(t.InNanos(), 42100000000L); UT_EQ(t.InMicros(), 42100000L); UT_EQ(t.InMillis(), 42100L); UT_EQ(t.InSeconds(), 42L); t.Sub(diff); UT_EQ(t.InNanos(), 42000000000L); UT_EQ(t.InMicros(), 42000000L); UT_EQ(t.InMillis(), 42000L); UT_EQ(t.InSeconds(), 42L); t.FromMillis(100); UT_EQ(t.InHertz(), 10.0); t.FromMillis(300); UT_EQ(t.InHertz(0), 3.0); UT_EQ(t.InHertz(1), 3.3); UT_EQ(t.InHertz(2), 3.33); UT_EQ(t.InHertz(5), 3.33333); } // check internal frequency { double freq = Ticks.InternalFrequency; Log.Info("TickWatch InternalFrequency: " + freq); UT_TRUE(freq >= 1000000.0); } // check TickWatch creation time { Ticks creationTimeDiff = new Ticks(); creationTimeDiff.Sub(Ticks.CreationTime); Log.Info("TickWatch library creation was: " + creationTimeDiff.InNanos() + "ns ago"); Log.Info("TickWatch library creation was: " + creationTimeDiff.InMicros() + "µs ago"); Log.Info("TickWatch library creation was: " + creationTimeDiff.InMillis() + "ms ago"); Log.Info("TickWatch library creation was: " + creationTimeDiff.InSeconds() + "s ago"); UT_TRUE(creationTimeDiff.InNanos() > 100); // It should really take 100 nanoseconds to get here! UT_TRUE(creationTimeDiff.InSeconds() < 3600); // these test will probably not last an hour } // check if we could sleep for 100ms { Ticks start = new Ticks(); ALIB.SleepMillis(30); Ticks sleepTime = new Ticks(); sleepTime.Sub(start); Log.Info("TickWatch diff after 100ms sleep: " + sleepTime.InMillis() + " ms"); UT_TRUE(sleepTime.InMillis() > 28); UT_TRUE(sleepTime.InMillis() < 150); // should work even on heavily loaded machines } }
// Per partition consumer read handler private void ProcessPartitionMessages(object state) { int partition = (int)state; try { Dictionary <uint, MeasurementKey> idTable = new Dictionary <uint, MeasurementKey>(); ConsumerOptions options = new ConsumerOptions(Topic, m_router); LongSynchronizedOperation cacheLastConsumerOffset = null; OffsetPosition consumerCursor = new OffsetPosition { PartitionId = partition, Offset = 0 }; long lastUpdateTime = 0; long lastMetadataUpdateCount = 0; long lastMeasurementTime = 0; options.PartitionWhitelist.Add(partition); options.Log = new TimeSeriesLogger((message, parameters) => OnStatusMessage($"P[{partition}]: " + message, parameters), OnProcessException); // Handle consumer offset tracking, i.e., adapter will start reading messages where it left off from last run if (TrackConsumerOffset) { // Parse path/filename.ext into constituent parts string[] fileParts = new string[3]; fileParts[0] = FilePath.GetDirectoryName(ConsumerOffsetFileName); // 0: path/ fileParts[1] = FilePath.GetFileNameWithoutExtension(ConsumerOffsetFileName); // 1: filename fileParts[2] = FilePath.GetExtension(ConsumerOffsetFileName); // 2: .ext // Include partition index as part of consumer offset cache file name string fileName = $"{fileParts[0]}{fileParts[1]}-P{partition}{fileParts[2]}"; if (File.Exists(fileName)) { try { // Read last consumer offset consumerCursor.Offset = long.Parse(File.ReadAllText(fileName)); } catch (Exception ex) { OnProcessException(new InvalidOperationException($"Failed to read last consumer offset from \"{fileName}\": {ex.Message}", ex)); } } cacheLastConsumerOffset = new LongSynchronizedOperation(() => { // Do not write file any more often than defined consumer offset cache interval int restTime = (int)(Ticks.FromSeconds(ConsumerOffsetCacheInterval) - (DateTime.UtcNow.Ticks - lastUpdateTime)).ToMilliseconds(); if (restTime > 0) { Thread.Sleep(restTime); } lastUpdateTime = DateTime.UtcNow.Ticks; // Write current consumer offset File.WriteAllText(fileName, consumerCursor.Offset.ToString()); }, ex => OnProcessException(new InvalidOperationException($"Failed to cache current consumer offset to \"{fileName}\": {ex.Message}", ex))) { IsBackground = true }; } using (Consumer consumer = new Consumer(options, new OffsetPosition(partition, consumerCursor.Offset))) { lock (m_consumers) m_consumers.Add(new WeakReference <Consumer>(consumer)); foreach (Message message in consumer.Consume()) { if ((object)m_metadata == null) { continue; } uint id; byte metadataVersion; IMeasurement measurement = message.KafkaDeserialize(out id, out metadataVersion); // Kick-off a refresh for new metadata if message version numbers change if (m_lastMetadataVersion != metadataVersion) { m_lastMetadataVersion = metadataVersion; m_updateMetadata.RunOnceAsync(); } // Clear all undefined items in dictionary when metadata gets updated if (lastMetadataUpdateCount < m_metadataUpdateCount) { lastMetadataUpdateCount = m_metadataUpdateCount; foreach (uint undefinedID in idTable.Where(item => item.Value.SignalID == Guid.Empty).Select(item => item.Key).ToArray()) { idTable.Remove(undefinedID); } } // Get associated measurement key, or look it up in metadata table measurement.Key = idTable.GetOrAdd(id, lookupID => MeasurementKey.LookUpBySignalID(m_metadata?.Records?.FirstOrDefault(record => record.ID == lookupID)?.ParseSignalID() ?? Guid.Empty)); // Only publish measurements with associated metadata and are assigned to this adapter if (measurement.Key != MeasurementKey.Undefined && ((object)m_outputMeasurementKeys == null || m_outputMeasurementKeys.Contains(measurement.Key))) { OnNewMeasurements(new[] { measurement }); } // Cache last consumer offset consumerCursor.Offset = message.Offset; if ((object)cacheLastConsumerOffset != null) { cacheLastConsumerOffset.RunOnceAsync(); } if (ReadDelay > -1) { // As a group of measurements transition from timestamp to another, inject configured read delay if (lastMeasurementTime != measurement.Timestamp) { Thread.Sleep(ReadDelay); } lastMeasurementTime = measurement.Timestamp; } } } } catch (Exception ex) { OnProcessException(new InvalidOperationException($"Exception while reading Kafka messages for topic \"{Topic}\" P[{partition}]: {ex.Message}", ex)); } }
public static void Main(string[] args) { m_definedMeasurements = new ConcurrentDictionary <string, MeasurementMetadata>(); m_definedDevices = new ConcurrentDictionary <ushort, ConfigurationCell>(); if (WriteLogs) { m_exportFile = new StreamWriter(FilePath.GetAbsolutePath("InputTimestamps.csv")); } if (TestConcentrator) { // Create a new concentrator concentrator = new Concentrator(WriteLogs, FilePath.GetAbsolutePath("OutputTimestamps.csv")); concentrator.TimeResolution = 333000; concentrator.FramesPerSecond = 30; concentrator.LagTime = 3.0D; concentrator.LeadTime = 9.0D; concentrator.PerformTimestampReasonabilityCheck = false; concentrator.ProcessByReceivedTimestamp = true; concentrator.Start(); } // Create a new protocol parser parser = new MultiProtocolFrameParser(); parser.AllowedParsingExceptions = 500; parser.ParsingExceptionWindow = 5; // Attach to desired events parser.ConnectionAttempt += parser_ConnectionAttempt; parser.ConnectionEstablished += parser_ConnectionEstablished; parser.ConnectionException += parser_ConnectionException; parser.ParsingException += parser_ParsingException; parser.ReceivedConfigurationFrame += parser_ReceivedConfigurationFrame; parser.ReceivedDataFrame += parser_ReceivedDataFrame; parser.ReceivedFrameBufferImage += parser_ReceivedFrameBufferImage; parser.ConnectionTerminated += parser_ConnectionTerminated; // Define the connection string //parser.ConnectionString = @"phasorProtocol=IeeeC37_118V1; transportProtocol=UDP; localport=5000; server=233.123.123.123:5000; interface=0.0.0.0"; //parser.ConnectionString = @"phasorProtocol=Ieee1344; transportProtocol=File; file=D:\Projects\Applications\openPDC\Synchrophasor\Current Version\Build\Output\Debug\Applications\openPDC\Sample1344.PmuCapture"; //parser.ConnectionString = @"phasorProtocol=Macrodyne; accessID=1; transportProtocol=File; skipDisableRealTimeData = true; file=C:\Users\Ritchie\Desktop\Development\Macrodyne\ING.out; iniFileName=C:\Users\Ritchie\Desktop\Development\Macrodyne\BCH18Aug2011.ini; deviceLabel=ING1; protocolVersion=G"; //parser.ConnectionString = @"phasorProtocol=Iec61850_90_5; accessID=1; transportProtocol=UDP; skipDisableRealTimeData = true; localPort=102; interface=0.0.0.0; commandChannel={transportProtocol=TCP; server=172.21.1.201:4712; interface=0.0.0.0}"; //parser.ConnectionString = @"phasorProtocol=FNET; transportProtocol=TCP; server=172.21.4.100:4001; interface=0.0.0.0; isListener=false"; //parser.ConnectionString = @"phasorProtocol=Macrodyne; transportProtocol=Serial; port=COM6; baudrate=38400; parity=None; stopbits=One; databits=8; dtrenable=False; rtsenable=False;"; //parser.ConnectionString = @"phasorProtocol=SelFastMessage; transportProtocol=Serial; port=COM5; baudrate=57600; parity=None; stopbits=One; databits=8; dtrenable=False; rtsenable=False;"; //parser.ConnectionString = @"phasorProtocol=IEEEC37_118v1; transportProtocol=File; file=C:\Users\Ritchie\Desktop\MTI_Test_3phase.PmuCapture; checkSumValidationFrameTypes=DataFrame,HeaderFrame,CommandFrame"; //parser.ConnectionString = @"phasorProtocol=IEEEC37_118V1; transportProtocol=tcp; accessID=105; server=172.31.105.135:4712; interface=0.0.0.0; isListener=false"; parser.ConnectionString = @"phasorProtocol=IEEEC37_118V1; transportProtocol=Serial; port=COM6; baudRate=115200; dataBits=8; stopBits=One; parity=None; dtrEnable=false; rtsEnable=false; autoStartDataParsingSequence=false; disableRealTimeDataOnStop=false"; Dictionary <string, string> settings = parser.ConnectionString.ParseKeyValuePairs(); string setting; // TODO: These should be optional picked up from connection string inside of MPFP // Apply other settings as needed if (settings.TryGetValue("accessID", out setting)) { parser.DeviceID = ushort.Parse(setting); } if (settings.TryGetValue("simulateTimestamp", out setting)) { parser.InjectSimulatedTimestamp = setting.ParseBoolean(); } if (settings.TryGetValue("allowedParsingExceptions", out setting)) { parser.AllowedParsingExceptions = int.Parse(setting); } if (settings.TryGetValue("parsingExceptionWindow", out setting)) { parser.ParsingExceptionWindow = Ticks.FromSeconds(double.Parse(setting)); } if (settings.TryGetValue("autoStartDataParsingSequence", out setting)) { parser.AutoStartDataParsingSequence = setting.ParseBoolean(); } if (settings.TryGetValue("skipDisableRealTimeData", out setting)) { parser.SkipDisableRealTimeData = setting.ParseBoolean(); } if (settings.TryGetValue("disableRealTimeDataOnStop", out setting)) { parser.DisableRealTimeDataOnStop = setting.ParseBoolean(); } // When connecting to a file based resource you may want to loop the data //parser.AutoRepeatCapturedPlayback = true; Console.WriteLine("ConnectionString: {0}", parser.ConnectionString); parser.Start(); Console.WriteLine("Press <enter> to terminate application..."); Console.ReadLine(); parser.Stop(); // Stop concentrator if (TestConcentrator) { concentrator.Stop(); } if (WriteLogs) { m_exportFile.Close(); } }
/// <summary> /// Queues a collection of measurements for processing. /// </summary> /// <param name="measurements">Collection of measurements to queue for processing.</param> /// <remarks> /// Measurements are filtered against the defined <see cref="InputMeasurementKeys"/> so we override method /// so that dynamic updates to keys will be synchronized with filtering to prevent interference. /// </remarks> // IMPORTANT: TSSC is sensitive to order - always make sure this function gets called sequentially, concurrent // calls to this function can cause TSSC parsing to get out of sequence and fail public override void QueueMeasurementsForProcessing(IEnumerable <IMeasurement> measurements) { if ((object)measurements == null) { return; } if (!m_startTimeSent && measurements.Any()) { m_startTimeSent = true; IMeasurement measurement = measurements.FirstOrDefault(m => (object)m != null); Ticks timestamp = 0; if ((object)measurement != null) { timestamp = measurement.Timestamp; } m_parent.SendDataStartTime(m_clientID, timestamp); } if (m_isNaNFiltered) { measurements = measurements.Where(measurement => !double.IsNaN(measurement.Value)); } if (!measurements.Any() || !Enabled) { return; } if (TrackLatestMeasurements) { double publishInterval; // Keep track of latest measurements base.QueueMeasurementsForProcessing(measurements); publishInterval = m_publishInterval > 0 ? m_publishInterval : LagTime; if (DateTime.UtcNow.Ticks > m_lastPublishTime + Ticks.FromSeconds(publishInterval)) { List <IMeasurement> currentMeasurements = new List <IMeasurement>(); Measurement newMeasurement; // Create a new set of measurements that represent the latest known values setting value to NaN if it is old foreach (TemporalMeasurement measurement in LatestMeasurements) { MeasurementStateFlags timeQuality = measurement.Timestamp.TimeIsValid(RealTime, measurement.LagTime, measurement.LeadTime) ? MeasurementStateFlags.Normal : MeasurementStateFlags.BadTime; newMeasurement = new Measurement { Metadata = measurement.Metadata, Value = measurement.Value, Timestamp = measurement.Timestamp, StateFlags = measurement.StateFlags | timeQuality }; currentMeasurements.Add(newMeasurement); } ProcessMeasurements(currentMeasurements); } } else { ProcessMeasurements(measurements); } }
private void DataInserter_BulkInsertCompleted(object sender, EventArgs <string, int, int> e) //string TableName, int TotalRows, int TotalSeconds { AddMessage("Bulk insert of " + e.Argument2 + " rows into table " + e.Argument1 + " completed in " + Ticks.FromSeconds(e.Argument3).ToElapsedTimeString(2).ToLower()); }
/// <summary> /// Initializes <see cref="TimestampTest"/>. /// </summary> public override void Initialize() { base.Initialize(); string errorMessage = "{0} is missing from Settings - Example: concentratorName=TESTSTREAM"; Dictionary <string, string> settings = Settings; string setting; // Load optional parameters if (settings.TryGetValue("timeToPurge", out setting)) { m_timeToPurge = Ticks.FromSeconds(double.Parse(setting)); } if (settings.TryGetValue("warnInterval", out setting)) { m_warnInterval = Ticks.FromSeconds(double.Parse(setting)); } // Load required parameters string concentratorName; if (!settings.TryGetValue("concentratorName", out concentratorName)) { throw new ArgumentException(string.Format(errorMessage, "concentratorName")); } m_discardingAdapter = null; // Find the adapter whose name matches the specified concentratorName foreach (IAdapter adapter in Parent) { IActionAdapter concentrator = adapter as IActionAdapter; if (concentrator != null && string.Compare(adapter.Name, concentratorName, true) == 0) { m_discardingAdapter = concentrator; break; } } if (m_discardingAdapter == null) { throw new ArgumentException(string.Format("Concentrator {0} not found.", concentratorName)); } // Wait for associated adapter to initialize int timeout = m_discardingAdapter.InitializationTimeout; m_discardingAdapter.WaitForInitialize(timeout); if (!m_discardingAdapter.Initialized) { throw new TimeoutException(string.Format("Timeout waiting for concentrator {0} to initialize.", concentratorName)); } // Attach to adapter's discarding measurements and disposed events m_discardingAdapter.DiscardingMeasurements += m_discardingAdapter_DiscardingMeasurements; m_discardingAdapter.Disposed += m_discardingAdapter_Disposed; m_purgeTimer.Interval = m_timeToPurge.ToMilliseconds(); m_purgeTimer.Elapsed += m_purgeTimer_Elapsed; m_warningTimer.Interval = m_warnInterval.ToMilliseconds(); m_warningTimer.Elapsed += m_warningTimer_Elapsed; m_timestampService = new TimestampService(this); m_timestampService.ServiceProcessException += m_timestampService_ServiceProcessException; m_timestampService.SettingsCategory = base.Name + m_timestampService.SettingsCategory; m_timestampService.Initialize(); }