//******************************************************************************************************// SynchronizeLocalClock.cs - Gbtc//// Copyright © 2018, Grid Protection Alliance. All Rights Reserved.//// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See// the NOTICE file distributed with this work for additional information regarding copyright ownership.// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this// file except in compliance with the License. You may obtain a copy of the License at://// http://opensource.org/licenses/MIT//// Unless agreed to in writing, the subject software distributed under the License is distributed on an// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the// License for the specific language governing permissions and limitations.//// Code Modification History:// ----------------------------------------------------------------------------------------------------// 12/20/2018 - J. Ritchie Carroll// Generated original version of source code.////****************************************************************************************************** // ReSharper disable InconsistentNaming// ReSharper disable PossibleMultipleEnumeration// ReSharper disable NotAccessedField.Local// ReSharper disable UnusedMember.Global using System;using System.Collections.Generic;using System.ComponentModel;using System.Runtime.InteropServices;using System.Timers;using GSF;using GSF.Diagnostics;using GSF.TimeSeries;using GSF.TimeSeries.Adapters; namespace TestingAdapters{ /// <summary> /// Represents a class used to synchronize the local system clock time to the time from input measurements. /// </summary> [Description("System Time Sync: Synchronizes local system clock time to the time from input measurements")] public class SynchronizeLocalClock : FacileActionAdapterBase { #region [ Members ] // Nested Types #pragma warning disable 169 #pragma warning disable 414 #pragma warning disable 649 private struct SYSTEMTIME { public ushort wYear; public ushort wMonth; public ushort wDayOfWeek; public ushort wDay; public ushort wHour; public ushort wMinute; public ushort wSecond; public ushort wMilliseconds; } #pragma warning restore 169 #pragma warning restore 414 #pragma warning restore 649 // Constants /// <summary> /// Default value for the <see cref="UpdateFrequency"/> property. /// </summary> public const int DefaultUpdateFrequency = 1000; // Fields private Timer m_updateTimer; private long m_clockUpdates; private bool m_goodSourceTime; private bool m_disposed; #endregion #region [ Properties ] /// <summary> /// Gets or sets the largest possible value generated by the adapter. /// </summary> [ConnectionStringParameter] [DefaultValue(DefaultUpdateFrequency)] [Description("Defines the update frequency, in milliseconds, for setting the local system clock time.")] public int UpdateFrequency { get; set; } = DefaultUpdateFrequency; /// <summary> /// Gets or sets flag that determines whether or not to use the local clock time as real time. /// </summary> /// <remarks> /// Use your local system clock as real time only if the time is locally GPS-synchronized, /// or if the measurement values being sorted were not measured relative to a GPS-synchronized clock. /// Turn this off if the class is intended to process historical data. /// </remarks> [EditorBrowsable(EditorBrowsableState.Never)] // Redeclared to hide property - value should not be changed by user public override bool UseLocalClockAsRealTime { get => base.UseLocalClockAsRealTime; set => base.UseLocalClockAsRealTime = value; } /// <summary> /// Gets or sets flag to start tracking the absolute latest received measurement values. /// </summary> /// <remarks> /// Latest received measurement value will be available via the <see cref="FacileActionAdapterBase.LatestMeasurements"/> property. /// </remarks> [EditorBrowsable(EditorBrowsableState.Never)] // Redeclared to hide property - value should not be changed by user public override bool TrackLatestMeasurements { get => base.TrackLatestMeasurements; set => base.TrackLatestMeasurements = value; } /// <summary> /// Gets the flag indicating if this adapter supports temporal processing. /// </summary> public override bool SupportsTemporalProcessing => false; #endregion #region [ Methods ] /// <summary> /// Releases the unmanaged resources used by the <see cref="SynchronizeLocalClock"/> 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_updateTimer != null) { m_updateTimer.Stop(); m_updateTimer.Elapsed -= m_updateTimer_Elapsed; m_updateTimer.Dispose(); } } } finally { m_disposed = true; // Prevent duplicate dispose. base.Dispose(disposing); // Call base class Dispose(). } } } /// <summary> /// Initializes <see cref="SynchronizeLocalClock"/>. /// </summary> public override void Initialize() { Dictionary<string, string> settings = Settings; // Force facile adapter to use incoming timestamps as time source, i.e., "RealTime" settings[nameof(UseLocalClockAsRealTime)] = false.ToString(); settings[nameof(TrackLatestMeasurements)] = true.ToString(); base.Initialize(); if (settings.TryGetValue(nameof(UpdateFrequency), out string setting)) { if (int.TryParse(setting, out int updateFrequency) && updateFrequency > 0) UpdateFrequency = updateFrequency; } m_updateTimer = new Timer(UpdateFrequency); m_updateTimer.Elapsed += m_updateTimer_Elapsed; m_updateTimer.Start(); } /// <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) => $"Updated clock {m_clockUpdates:N3} times so far...".CenterText(maxLength); /// <summary> /// Queues a collection of measurements for processing. /// </summary> /// <param name="measurements">Measurements to queue for processing.</param> public override void QueueMeasurementsForProcessing(IEnumerable<IMeasurement> measurements) { List<IMeasurement> measurementsWithGoodTime = new List<IMeasurement>(); foreach (IMeasurement measurement in measurements) { if (measurement.TimestampQualityIsGood()) measurementsWithGoodTime.Add(measurement); } if (measurementsWithGoodTime.Count == 0) { base.QueueMeasurementsForProcessing(measurements); m_goodSourceTime = false; } else { base.QueueMeasurementsForProcessing(measurementsWithGoodTime); m_goodSourceTime = true; } } private void m_updateTimer_Elapsed(object sender, ElapsedEventArgs e) { try { Ticks newSystemTime = RealTime; if (newSystemTime <= 0L) return; SetSystemTime(RealTime); m_clockUpdates++; if (!m_goodSourceTime) OnStatusMessage(MessageLevel.Warning, "WARNING: Clock set with measurement that has bad quality -- increase measurement sources."); } catch (Exception ex) { OnProcessException(MessageLevel.Error, ex); } } #endregion #region [ Static ] // Static Methods [DllImport("kernel32.dll", SetLastError = true)] private static extern bool SetSystemTime(ref SYSTEMTIME lpSystemTime); private static void SetSystemTime(DateTime newSystemTime) { SYSTEMTIME systime = new SYSTEMTIME { wYear = (ushort)newSystemTime.Year, wMonth = (ushort)newSystemTime.Month, wDay = (ushort)newSystemTime.Day, wHour = (ushort)newSystemTime.Hour, wMinute = (ushort)newSystemTime.Minute, wSecond = (ushort)newSystemTime.Second, wMilliseconds = (ushort)newSystemTime.Millisecond }; if (!SetSystemTime(ref systime)) throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to set local system time, verify service has SE_SYSTEMTIME_NAME privilege."); } #endregion }}
//******************************************************************************************************
// SynchronizeLocalClock.cs - Gbtc
//
// Copyright © 2018, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
// file except in compliance with the License. You may obtain a copy of the License at:
//
// http://opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 12/20/2018 - J. Ritchie Carroll
// Generated original version of source code.
//
//******************************************************************************************************
// ReSharper disable InconsistentNaming
// ReSharper disable PossibleMultipleEnumeration
// ReSharper disable NotAccessedField.Local
// ReSharper disable UnusedMember.Global
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Timers;
using GSF;
using GSF.Diagnostics;
using GSF.TimeSeries;
using GSF.TimeSeries.Adapters;
namespace TestingAdapters
{
/// <summary>
/// Represents a class used to synchronize the local system clock time to the time from input measurements.
/// </summary>
[Description("System Time Sync: Synchronizes local system clock time to the time from input measurements")]
public class SynchronizeLocalClock : FacileActionAdapterBase
{
#region [ Members ]
// Nested Types
#pragma warning disable 169
#pragma warning disable 414
#pragma warning disable 649
private struct SYSTEMTIME
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
#pragma warning restore 169
#pragma warning restore 414
#pragma warning restore 649
// Constants
/// <summary>
/// Default value for the <see cref="UpdateFrequency"/> property.
/// </summary>
public const int DefaultUpdateFrequency = 1000;
// Fields
private Timer m_updateTimer;
private long m_clockUpdates;
private bool m_goodSourceTime;
private bool m_disposed;
#endregion
#region [ Properties ]
/// <summary>
/// Gets or sets the largest possible value generated by the adapter.
/// </summary>
[ConnectionStringParameter]
[DefaultValue(DefaultUpdateFrequency)]
[Description("Defines the update frequency, in milliseconds, for setting the local system clock time.")]
public int UpdateFrequency { get; set; } = DefaultUpdateFrequency;
/// <summary>
/// Gets or sets flag that determines whether or not to use the local clock time as real time.
/// </summary>
/// <remarks>
/// Use your local system clock as real time only if the time is locally GPS-synchronized,
/// or if the measurement values being sorted were not measured relative to a GPS-synchronized clock.
/// Turn this off if the class is intended to process historical data.
/// </remarks>
[EditorBrowsable(EditorBrowsableState.Never)] // Redeclared to hide property - value should not be changed by user
public override bool UseLocalClockAsRealTime
{
get => base.UseLocalClockAsRealTime;
set => base.UseLocalClockAsRealTime = value;
}
/// <summary>
/// Gets or sets flag to start tracking the absolute latest received measurement values.
/// </summary>
/// <remarks>
/// Latest received measurement value will be available via the <see cref="FacileActionAdapterBase.LatestMeasurements"/> property.
/// </remarks>
[EditorBrowsable(EditorBrowsableState.Never)] // Redeclared to hide property - value should not be changed by user
public override bool TrackLatestMeasurements
{
get => base.TrackLatestMeasurements;
set => base.TrackLatestMeasurements = value;
}
/// <summary>
/// Gets the flag indicating if this adapter supports temporal processing.
/// </summary>
public override bool SupportsTemporalProcessing => false;
#endregion
#region [ Methods ]
/// <summary>
/// Releases the unmanaged resources used by the <see cref="SynchronizeLocalClock"/> 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_updateTimer != null)
{
m_updateTimer.Stop();
m_updateTimer.Elapsed -= m_updateTimer_Elapsed;
m_updateTimer.Dispose();
}
}
}
finally
{
m_disposed = true; // Prevent duplicate dispose.
base.Dispose(disposing); // Call base class Dispose().
}
}
}
/// <summary>
/// Initializes <see cref="SynchronizeLocalClock"/>.
/// </summary>
public override void Initialize()
{
Dictionary<string, string> settings = Settings;
// Force facile adapter to use incoming timestamps as time source, i.e., "RealTime"
settings[nameof(UseLocalClockAsRealTime)] = false.ToString();
settings[nameof(TrackLatestMeasurements)] = true.ToString();
base.Initialize();
if (settings.TryGetValue(nameof(UpdateFrequency), out string setting))
{
if (int.TryParse(setting, out int updateFrequency) && updateFrequency > 0)
UpdateFrequency = updateFrequency;
}
m_updateTimer = new Timer(UpdateFrequency);
m_updateTimer.Elapsed += m_updateTimer_Elapsed;
m_updateTimer.Start();
}
/// <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) => $"Updated clock {m_clockUpdates:N3} times so far...".CenterText(maxLength);
/// <summary>
/// Queues a collection of measurements for processing.
/// </summary>
/// <param name="measurements">Measurements to queue for processing.</param>
public override void QueueMeasurementsForProcessing(IEnumerable<IMeasurement> measurements)
{
List<IMeasurement> measurementsWithGoodTime = new List<IMeasurement>();
foreach (IMeasurement measurement in measurements)
{
if (measurement.TimestampQualityIsGood())
measurementsWithGoodTime.Add(measurement);
}
if (measurementsWithGoodTime.Count == 0)
{
base.QueueMeasurementsForProcessing(measurements);
m_goodSourceTime = false;
}
else
{
base.QueueMeasurementsForProcessing(measurementsWithGoodTime);
m_goodSourceTime = true;
}
}
private void m_updateTimer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
Ticks newSystemTime = RealTime;
if (newSystemTime <= 0L)
return;
SetSystemTime(RealTime);
m_clockUpdates++;
if (!m_goodSourceTime)
OnStatusMessage(MessageLevel.Warning, "WARNING: Clock set with measurement that has bad quality -- increase measurement sources.");
}
catch (Exception ex)
{
OnProcessException(MessageLevel.Error, ex);
}
}
#endregion
#region [ Static ]
// Static Methods
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetSystemTime(ref SYSTEMTIME lpSystemTime);
private static void SetSystemTime(DateTime newSystemTime)
{
SYSTEMTIME systime = new SYSTEMTIME
{
wYear = (ushort)newSystemTime.Year,
wMonth = (ushort)newSystemTime.Month,
wDay = (ushort)newSystemTime.Day,
wHour = (ushort)newSystemTime.Hour,
wMinute = (ushort)newSystemTime.Minute,
wSecond = (ushort)newSystemTime.Second,
wMilliseconds = (ushort)newSystemTime.Millisecond
};
if (!SetSystemTime(ref systime))
throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to set local system time, verify service has SE_SYSTEMTIME_NAME privilege.");
}
#endregion
}
}