Unified · Split
| @@ -103,6 +103,23 @@ namespace PIAdapters · 102 unmodified lines | |||
| 103 | { | 103 | { |
| 104 | #region [ Members ] | 104 | #region [ Members ] |
| 105 | 105 | ||
| 106 | // Constants | ||
| 107 | |||
| 108 | /// <summary> | ||
| 109 | /// Defines the default value for <see cref="EnableTimeReasonabilityCheck"/>. | ||
| 110 | /// </summary> | ||
| 111 | public const bool DefaultEnableTimeReasonabilityCheck = false; | ||
| 112 | |||
| 113 | /// <summary> | ||
| 114 | /// Defines the default value for <see cref="PastTimeReasonabilityLimit"/>. | ||
| 115 | /// </summary> | ||
| 116 | public const double DefaultPastTimeReasonabilityLimit = 43200.0D; | ||
| 117 | |||
| 118 | /// <summary> | ||
| 119 | /// Defines the default value for <see cref="FutureTimeReasonabilityLimit"/>. | ||
| 120 | /// </summary> | ||
| 121 | public const double DefaultFutureTimeReasonabilityLimit = 43200.0D; | ||
| 122 | |||
| 106 | // Fields | 123 | // Fields |
| 107 | 124 | ||
| 108 | // Define cached mapping between GSFSchema measurements and PI points | 125 | // Define cached mapping between GSFSchema measurements and PI points |
| @@ -116,6 +133,8 @@ namespace PIAdapters · 7 unmodified lines | |||
| 116 | private readonly LongSynchronizedOperation m_handleTagRemoval; // Tag removal operation, if any | 133 | private readonly LongSynchronizedOperation m_handleTagRemoval; // Tag removal operation, if any |
| 117 | private Dictionary<Guid, Ticks> m_lastArchiveTimes; // Cache of last point archive times | 134 | private Dictionary<Guid, Ticks> m_lastArchiveTimes; // Cache of last point archive times |
| 118 | private PIConnection m_connection; // PI server connection for meta-data synchronization | 135 | private PIConnection m_connection; // PI server connection for meta-data synchronization |
| 136 | private long m_pastTimeReasonabilityLimit; // Past-timestamp reasonability limit | ||
| 137 | private long m_futureTimeReasonabilityLimit; // Future-timestamp reasonability limit | ||
| 119 | private DateTime m_lastMetadataRefresh; // Tracks time of last meta-data refresh | 138 | private DateTime m_lastMetadataRefresh; // Tracks time of last meta-data refresh |
| 120 | private long m_processedMappings; // Total number of mappings processed so far | 139 | private long m_processedMappings; // Total number of mappings processed so far |
| 121 | private long m_processedMeasurements; // Total number of measurements processed so far | 140 | private long m_processedMeasurements; // Total number of measurements processed so far |
| @@ -288,6 +307,38 @@ namespace PIAdapters · 166 unmodified lines | |||
| 288 | [DefaultValue(0.0D)] | 307 | [DefaultValue(0.0D)] |
| 289 | public double MaximumPointResolution { get; set; } | 308 | public double MaximumPointResolution { get; set; } |
| 290 | 309 | ||
| 310 | /// <summary> | ||
| 311 | /// Gets or sets flag that indicates if incoming timestamps to the historian should be validated for reasonability. | ||
| 312 | /// </summary> | ||
| 313 | [ConnectionStringParameter] | ||
| 314 | [Description("Define the flag that indicates if incoming timestamps to the historian should be validated for reasonability.")] | ||
| 315 | [DefaultValue(DefaultEnableTimeReasonabilityCheck)] | ||
| 316 | public bool EnableTimeReasonabilityCheck { get; set; } | ||
| 317 | |||
| 318 | /// <summary> | ||
| 319 | /// Gets or sets the maximum number of seconds that a past timestamp, as compared to local clock, will be considered valid. | ||
| 320 | /// </summary> | ||
| 321 | [ConnectionStringParameter] | ||
| 322 | [Description("Define the maximum number of seconds that a past timestamp, as compared to local clock, will be considered valid.")] | ||
| 323 | [DefaultValue(DefaultPastTimeReasonabilityLimit)] | ||
| 324 | public double PastTimeReasonabilityLimit | ||
| 325 | { | ||
| 326 | get => new Ticks(m_pastTimeReasonabilityLimit).ToSeconds(); | ||
| 327 | set => m_pastTimeReasonabilityLimit = Ticks.FromSeconds(Math.Abs(value)); | ||
| 328 | } | ||
| 329 | |||
| 330 | /// <summary> | ||
| 331 | /// Gets or sets the maximum number of seconds that a future timestamp, as compared to local clock, will be considered valid. | ||
| 332 | /// </summary> | ||
| 333 | [ConnectionStringParameter] | ||
| 334 | [Description("Define the maximum number of seconds that a future timestamp, as compared to local clock, will be considered valid.")] | ||
| 335 | [DefaultValue(DefaultFutureTimeReasonabilityLimit)] | ||
| 336 | public double FutureTimeReasonabilityLimit | ||
| 337 | { | ||
| 338 | get => new Ticks(m_futureTimeReasonabilityLimit).ToSeconds(); | ||
| 339 | set => m_futureTimeReasonabilityLimit = Ticks.FromSeconds(Math.Abs(value)); | ||
| 340 | } | ||
| 341 | |||
| 291 | /// <summary> | 342 | /// <summary> |
| 292 | /// Returns the detailed status of the data output source. | 343 | /// Returns the detailed status of the data output source. |
| 293 | /// </summary> | 344 | /// </summary> |
| @@ -303,6 +354,14 @@ namespace PIAdapters · 9 unmodified lines | |||
| 303 | status.AppendLine($" Connected to server: {(m_connection?.Connected ?? false ? "Yes" : "No")}"); | 354 | status.AppendLine($" Connected to server: {(m_connection?.Connected ?? false ? "Yes" : "No")}"); |
| 304 | status.AppendLine($" Using compression: {UseCompression}"); | 355 | status.AppendLine($" Using compression: {UseCompression}"); |
| 305 | status.AppendLine($" Maximum point resolution: {MaximumPointResolution:N3} seconds{(MaximumPointResolution <= 0.0D ? " - all data will be archived" : "")}"); | 356 | status.AppendLine($" Maximum point resolution: {MaximumPointResolution:N3} seconds{(MaximumPointResolution <= 0.0D ? " - all data will be archived" : "")}"); |
| 357 | status.AppendLine($" Time reasonability check: {(EnableTimeReasonabilityCheck ? "Enabled" : "Not Enabled")}"); | ||
| 358 | |||
| 359 | if (EnableTimeReasonabilityCheck) | ||
| 360 | { | ||
| 361 | status.AppendLine($" Maximum past time limit: {PastTimeReasonabilityLimit:N4}s, i.e., {new Ticks(m_pastTimeReasonabilityLimit).ToElapsedTimeString(4)}"); | ||
| 362 | status.AppendLine($" Maximum future time limit: {FutureTimeReasonabilityLimit:N4}s, i.e., {new Ticks(m_futureTimeReasonabilityLimit).ToElapsedTimeString(4)}"); | ||
| 363 | } | ||
| 364 | |||
| 306 | status.AppendLine($" Meta-data sync enabled: {RunMetadataSync}"); | 365 | status.AppendLine($" Meta-data sync enabled: {RunMetadataSync}"); |
| 307 | 366 | ||
| 308 | if (RunMetadataSync) | 367 | if (RunMetadataSync) |
| @@ -562,6 +621,15 @@ namespace PIAdapters · 253 unmodified lines | |||
| 562 | if (!Enabled) | 621 | if (!Enabled) |
| 563 | return; | 622 | return; |
| 564 | 623 | ||
| 624 | // Validate timestamp reasonability as compared to local clock, when enabled | ||
| 625 | if (EnableTimeReasonabilityCheck) | ||
| 626 | { | ||
| 627 | long deviation = DateTime.UtcNow.Ticks - measurement.Timestamp.Value; | ||
| 628 | |||
| 629 | if (deviation < -m_futureTimeReasonabilityLimit || deviation > m_pastTimeReasonabilityLimit) | ||
| 630 | continue; | ||
| 631 | } | ||
| 632 | |||
| 565 | // Lookup connection point mapping for this measurement, if it wasn't found - go ahead and exit | 633 | // Lookup connection point mapping for this measurement, if it wasn't found - go ahead and exit |
| 566 | if (!m_mappedPIPoints.TryGetValue(measurement.Key, out PIPoint point)) | 634 | if (!m_mappedPIPoints.TryGetValue(measurement.Key, out PIPoint point)) |
| 567 | continue; | 635 | continue; |