-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #577 from WildernessLabs/bug/linux-ntp
handling of Linux NTP and time zone shenanigans
- Loading branch information
Showing
5 changed files
with
152 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
using System; | ||
using System.Net; | ||
using System.Net.Sockets; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using static Meadow.Logging.Logger; | ||
|
||
namespace Meadow; | ||
|
||
/// <summary> | ||
/// base Client for Network Time Protocol | ||
/// </summary> | ||
public abstract class NtpClientBase : INtpClient | ||
{ | ||
private int _ntpLock = 1; | ||
|
||
/// <inheritdoc/> | ||
public event TimeChangedEventHandler TimeChanged = default!; | ||
|
||
/// <inheritdoc/> | ||
public abstract bool Enabled { get; } | ||
/// <inheritdoc/> | ||
public abstract TimeSpan PollPeriod { get; set; } | ||
|
||
/// <summary> | ||
/// Raises the TimeChanged event with a given time | ||
/// </summary> | ||
/// <param name="utcTime">The new time</param> | ||
protected void RaiseTimeChanged(DateTime utcTime) => TimeChanged?.Invoke(utcTime); | ||
|
||
/// <inheritdoc/> | ||
public Task<bool> Synchronize(string? ntpServer = null) | ||
{ | ||
if (ntpServer == null) | ||
{ | ||
if (Resolver.Device.PlatformOS.NtpServers.Length == 0) | ||
{ | ||
ntpServer = "0.pool.ntp.org"; | ||
Resolver.Log.Info($"No configured NTP servers. Defaulting to {ntpServer}", MessageGroup.Core); | ||
} | ||
else | ||
{ | ||
ntpServer = Resolver.Device.PlatformOS.NtpServers[0]; | ||
} | ||
} | ||
|
||
if (Interlocked.Exchange(ref _ntpLock, 0) == 1) | ||
{ | ||
try | ||
{ | ||
var m_ntpPacket = new byte[48]; | ||
//LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode) | ||
m_ntpPacket[0] = 0x1B; | ||
|
||
UdpClient client = new UdpClient(); | ||
client.Connect(ntpServer, 123); | ||
client.Send(m_ntpPacket, m_ntpPacket.Length); | ||
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0); | ||
byte[] data = client.Receive(ref ep); | ||
|
||
// receive date data is at offset 32 | ||
// Data is 64 bits - first 32 is seconds | ||
// it is not in an endian order, so we must rearrange | ||
byte[] endianSeconds = new byte[4]; | ||
endianSeconds[0] = data[32 + 3]; | ||
endianSeconds[1] = data[32 + 2]; | ||
endianSeconds[2] = data[32 + 1]; | ||
endianSeconds[3] = data[32 + 0]; | ||
uint seconds = BitConverter.ToUInt32(endianSeconds, 0); | ||
|
||
// second 32 is fraction of a second | ||
endianSeconds[0] = data[32 + 7]; | ||
endianSeconds[1] = data[32 + 6]; | ||
endianSeconds[2] = data[32 + 5]; | ||
endianSeconds[3] = data[32 + 4]; | ||
|
||
uint fraction = BitConverter.ToUInt32(endianSeconds, 0); | ||
|
||
var s = double.Parse($"{seconds}.{fraction}"); | ||
|
||
var dt = new DateTime(1900, 1, 1).AddSeconds(s); | ||
Resolver.Device.PlatformOS.SetClock(dt); | ||
TimeChanged?.Invoke(dt); | ||
return Task.FromResult(true); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Resolver.Log.Error($"Failed to query NTP Server: '{ex.Message}'.", MessageGroup.Core); | ||
return Task.FromResult(false); | ||
} | ||
finally | ||
{ | ||
Interlocked.Exchange(ref _ntpLock, 1); | ||
} | ||
|
||
} | ||
|
||
return Task.FromResult(false); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,113 +1,31 @@ | ||
using System; | ||
using System.Net; | ||
using System.Net.Sockets; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using static Meadow.Logging.Logger; | ||
|
||
namespace Meadow; | ||
|
||
/// <summary> | ||
/// Client for Network Time Protocol | ||
/// </summary> | ||
public class NtpClient : INtpClient | ||
public class NtpClient : NtpClientBase | ||
{ | ||
/// <summary> | ||
/// Event raised when the device clock is adjusted by NTP | ||
/// </summary> | ||
public event TimeChangedEventHandler TimeChanged = default!; | ||
|
||
/// <summary> | ||
/// Returns <c>true</c> if the NTP Client is enabled | ||
/// </summary> | ||
public bool Enabled => F7PlatformOS.GetBoolean(IPlatformOS.ConfigurationValues.GetTimeAtStartup); | ||
public override bool Enabled => F7PlatformOS.GetBoolean(IPlatformOS.ConfigurationValues.GetTimeAtStartup); | ||
|
||
internal NtpClient() | ||
{ } | ||
|
||
/// <summary> | ||
/// Time period that the NTP client attempts to query the NTP time server(s) | ||
/// </summary> | ||
public TimeSpan PollPeriod | ||
public override TimeSpan PollPeriod | ||
{ | ||
get => TimeSpan.Zero; // currently only happens at startup | ||
set => throw new PlatformNotSupportedException("Changing NTP Poll Frequency not currently supported"); | ||
} | ||
|
||
internal void RaiseTimeChanged() | ||
{ | ||
TimeChanged?.Invoke(DateTime.UtcNow); | ||
} | ||
|
||
private int _ntpLock = 1; | ||
|
||
/// <inheritdoc/> | ||
public Task<bool> Synchronize(string? ntpServer = null) | ||
{ | ||
if (ntpServer == null) | ||
{ | ||
if (Resolver.Device.PlatformOS.NtpServers.Length == 0) | ||
{ | ||
ntpServer = "0.pool.ntp.org"; | ||
Resolver.Log.Info($"No configured NTP servers. Defaulting to {ntpServer}", MessageGroup.Core); | ||
} | ||
else | ||
{ | ||
ntpServer = Resolver.Device.PlatformOS.NtpServers[0]; | ||
} | ||
} | ||
|
||
if (Interlocked.Exchange(ref _ntpLock, 0) == 1) | ||
{ | ||
try | ||
{ | ||
var m_ntpPacket = new byte[48]; | ||
//LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode) | ||
m_ntpPacket[0] = 0x1B; | ||
|
||
UdpClient client = new UdpClient(); | ||
client.Connect(ntpServer, 123); | ||
client.Send(m_ntpPacket, m_ntpPacket.Length); | ||
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0); | ||
byte[] data = client.Receive(ref ep); | ||
|
||
// receive date data is at offset 32 | ||
// Data is 64 bits - first 32 is seconds | ||
// it is not in an endian order, so we must rearrange | ||
byte[] endianSeconds = new byte[4]; | ||
endianSeconds[0] = data[32 + 3]; | ||
endianSeconds[1] = data[32 + 2]; | ||
endianSeconds[2] = data[32 + 1]; | ||
endianSeconds[3] = data[32 + 0]; | ||
uint seconds = BitConverter.ToUInt32(endianSeconds, 0); | ||
|
||
// second 32 is fraction of a second | ||
endianSeconds[0] = data[32 + 7]; | ||
endianSeconds[1] = data[32 + 6]; | ||
endianSeconds[2] = data[32 + 5]; | ||
endianSeconds[3] = data[32 + 4]; | ||
|
||
uint fraction = BitConverter.ToUInt32(endianSeconds, 0); | ||
|
||
var s = double.Parse($"{seconds}.{fraction}"); | ||
|
||
var dt = new DateTime(1900, 1, 1).AddSeconds(s); | ||
Resolver.Device.PlatformOS.SetClock(dt); | ||
TimeChanged?.Invoke(dt); | ||
return Task.FromResult(true); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Resolver.Log.Error($"Failed to query NTP Server: '{ex.Message}'.", MessageGroup.Core); | ||
return Task.FromResult(false); | ||
} | ||
finally | ||
{ | ||
Interlocked.Exchange(ref _ntpLock, 1); | ||
} | ||
|
||
} | ||
|
||
return Task.FromResult(false); | ||
RaiseTimeChanged(DateTime.UtcNow); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 6 additions & 14 deletions
20
Source/implementations/linux/Meadow.Linux/PlatformOS/LinuxNtpClient.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,19 @@ | ||
using System; | ||
using System.Threading.Tasks; | ||
|
||
namespace Meadow; | ||
|
||
/// <summary> | ||
/// Represents an NTP (Network Time Protocol) client for Linux. | ||
/// </summary> | ||
public class LinuxNtpClient : INtpClient | ||
public class LinuxNtpClient : NtpClientBase | ||
{ | ||
/// <inheritdoc/> | ||
public bool Enabled => false; | ||
|
||
/// <inheritdoc/> | ||
public TimeSpan PollPeriod { get; set; } | ||
internal LinuxNtpClient() | ||
{ | ||
} | ||
|
||
/// <inheritdoc/> | ||
public event TimeChangedEventHandler? TimeChanged; | ||
public override bool Enabled => false; | ||
|
||
/// <inheritdoc/> | ||
public Task<bool> Synchronize(string? ntpServer = null) | ||
{ | ||
TimeChanged?.Invoke(DateTime.UtcNow); | ||
|
||
throw new NotImplementedException(); | ||
} | ||
public override TimeSpan PollPeriod { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters