Sincronizzare l’orologio con un server di riferimento su Internet (Italiano)

Praticamente tutte le applicazioni che conosciamo hanno a che fare date e ore: per registrare il momento in cui viene salvato un file, per tenere traccia degli accessi alle risorse, ecc.. Il .NET Micro Framework non fa eccezione: anche su tale piattaforma, ogni applicazione, appena avviata, dovrebbe aggiornare il proprio orologio interno per allinearlo all’orario reale.

A partire dalla versione 4.0, il .NET Micro Framework mette a disposizione un’apposita classe, TimeService, con cui è possibile sincronizzare l’orologio interno della propria scheda con un server di riferimento su Internet (chiamato NTP Server). Vediamo come utilizzarla.

Per il nostro esempio, ci serviremo dei moduli Display_T35 e Ethernet di GHI Electronics. Innanzi tutto, dobbiamo aggiungere al progetto il riferimento all’assembly Microsoft.SPOT.Time, in cui è contenuta la definizione della classe TimeService. Nel metodo ProgramStarted inseriamo le inizializzazione degli oggetti:

private DispatcherTimer timer;
private static bool timeSynchronized;

void ProgramStarted()
{
    ethernet.NetworkUp += 
				new GTM.Module.NetworkModule.NetworkEventHandler(ethernet_NetworkUp);
    ethernet.NetworkDown += 
				new GTM.Module.NetworkModule.NetworkEventHandler(ethernet_NetworkDown);
    ethernet.UseDHCP();

    TimeService.SystemTimeChanged += 
				new SystemTimeChangedEventHandler(TimeService_SystemTimeChanged);
    TimeService.TimeSyncFailed += 
				new TimeSyncFailedEventHandler(TimeService_TimeSyncFailed);

    timer = new DispatcherTimer();
    timer.Interval = TimeSpan.FromTicks(TimeSpan.TicksPerMillisecond * 500);
    timer.Tick += new EventHandler(timer_Tick);

    SetupWindow();           
    Debug.Print("Program Started");
}

Innanzi tutto, ci registriamo sugli eventi dell’interfaccia di rete e abilitiamo l’utilizzo del DHCP. Dopodiché, ci registriamo sugli eventi che l’oggetto TimeService genera, rispettivamente, quando l’ora di sistema viene sincronizzata con un server su Internet e quando si verifica un errore durante la sincronizzazione. Nell’esempio definiamo anche un DispatcherTimer che si occuperà di aggiornare l’orario mostrato a video. Infine, richiamiamo la routine SetupWindow, che definisce una casella di testo che sarà aggiornata con i valori di data e ora:

private void SetupWindow()
{
    Window window = display.WPFWindow;
    Font baseFont = Resources.GetFont(Resources.FontResources.NinaB);

    Canvas canvas = new Canvas();
    window.Child = canvas;

    txtDateTime = new Text(baseFont, "Waiting for time update...");
    canvas.Children.Add(txtDateTime);
    Canvas.SetTop(txtDateTime, 100);
    Canvas.SetLeft(txtDateTime, 90);
}

La sincronizzazione vera e propria viene effettuata al termine dell’inizializzazione dell’interfaccia di rete, ovvero quando viene generato l’evento NetworkUp:

private void ethernet_NetworkUp(GTM.Module.NetworkModule sender, 
								GTM.Module.NetworkModule.NetworkState state)
{
    Debug.Print("Network up!");

    // Configure TimeService settings.
    TimeServiceSettings settings = new TimeServiceSettings();
    settings.ForceSyncAtWakeUp = true;
    settings.RefreshTime = 1800;    // in seconds.

    IPAddress[] address = Dns.GetHostEntry("time-a.nist.gov").AddressList;
    if (address != null && address.Length > 0)
        settings.PrimaryServer = address[0].GetAddressBytes();

    address = Dns.GetHostEntry("pool.ntp.org").AddressList;
    if (address != null && address.Length > 0)
        settings.AlternateServer = address[0].GetAddressBytes();

    TimeService.Settings = settings;

    // Add the time zone offset to the retrieved UTC Time (in this example, it assumes that
    // time zone is GMT+1).
    TimeService.SetTimeZoneOffset(60);
    
    TimeService.Start();
    timer.Start();
}

private static void TimeService_TimeSyncFailed(object sender, TimeSyncFailedEventArgs e)
{
    Debug.Print("Error synchronizing system time with NTP server: " + e.ErrorCode);
}

private static void TimeService_SystemTimeChanged(object sender, SystemTimeChangedEventArgs e)
{
    Debug.Print("Network time received.");
    if (!timeSynchronized)
        timeSynchronized = true;
}

private void timer_Tick(object sender, EventArgs e)
{            
    if (timeSynchronized)
        txtDateTime.TextContent = DateTime.Now.ToString();
}

Definiamo un oggetto di tipo TimeServiceSettings e lo utilizziamo per impostare le proprietà della sincronizzazione.  In particolare, abbiamo la possibilità di specificare due diversi server, nel nostro esempio time-a.nist.gov e pool.ntp.org. Quando si richiede una sincronizzazione dell’orario, TimeService cerca innanzi tutto di collegarsi con il PrimaryServer; se il collegamento fallisce, viene fatto un secondo tentativo verso l’AlternateServer.

L’orario recuperato è in formato UTC. Nel codice supponiamo di essere nel fuso orario GMT+1, quindi richiamando il metodo TimeService.SetTimeZoneOffset diciamo al servizio di aggiungere automaticamente 60 minuti all’orario recuperato.

Infine, con il metodo Start avviamo il recupero vero e proprio: il sistema cercherà di recuperare l’orario collegandosi ai server specificati e lancerà gli eventi associati. L’interrogazione dei server è ripetuta ad intervalli pari al numero di secondi specificati nella proprietà RefreshTime. In ogni momento, è comunque possibile utilizzare il metodo TimeService.UpdateNow per forzare l’aggiornamento immediato. L’ultima routine presentata nell’esempio si occupa di aggiornare l’orario visualizzato sullo schermo.

In conclusione, il file Program.cs completo della nostra applicazione è il seguente:

using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;

using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;
using Microsoft.SPOT.Time;
using System.Net;

namespace TimeServiceExample
{
    public partial class Program
    {
        private DispatcherTimer timer;
        private static bool timeSynchronized;
       
        void ProgramStarted()
        {
            ethernet.NetworkUp += 
				new GTM.Module.NetworkModule.NetworkEventHandler(ethernet_NetworkUp);
            ethernet.NetworkDown += 
				new GTM.Module.NetworkModule.NetworkEventHandler(ethernet_NetworkDown);
            ethernet.UseDHCP();

            TimeService.SystemTimeChanged += 
				new SystemTimeChangedEventHandler(TimeService_SystemTimeChanged);
            TimeService.TimeSyncFailed += 
				new TimeSyncFailedEventHandler(TimeService_TimeSyncFailed);

            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromTicks(TimeSpan.TicksPerMillisecond * 500);
            timer.Tick += new EventHandler(timer_Tick);

            SetupWindow();           
            Debug.Print("Program Started");
        }

        private Text txtDateTime;

        private void SetupWindow()
        {
            Window window = display.WPFWindow;
            Font baseFont = Resources.GetFont(Resources.FontResources.NinaB);

            Canvas canvas = new Canvas();
            window.Child = canvas;

            txtDateTime = new Text(baseFont, "Waiting for time update...");
            canvas.Children.Add(txtDateTime);
            Canvas.SetTop(txtDateTime, 100);
            Canvas.SetLeft(txtDateTime, 90);
        }

        private void ethernet_NetworkDown(GTM.Module.NetworkModule sender, 
									GTM.Module.NetworkModule.NetworkState state)
        {
            Debug.Print("Network down!");
        }

        private void ethernet_NetworkUp(GTM.Module.NetworkModule sender, 
									GTM.Module.NetworkModule.NetworkState state)
        {
            Debug.Print("Network up!");

            // Configure TimeService settings.
            TimeServiceSettings settings = new TimeServiceSettings();
            settings.ForceSyncAtWakeUp = true;
            settings.RefreshTime = 1800;    // in seconds.

            IPAddress[] address = Dns.GetHostEntry("time-a.nist.gov").AddressList;
            if (address != null && address.Length > 0)
                settings.PrimaryServer = address[0].GetAddressBytes();

            address = Dns.GetHostEntry("pool.ntp.org").AddressList;
            if (address != null && address.Length > 0)
                settings.AlternateServer = address[0].GetAddressBytes();

            TimeService.Settings = settings;

            // Add the time zone offset to the retrieved UTC Time (in this example, it assumes that
            // time zone is GMT+1).
            TimeService.SetTimeZoneOffset(60);
            
            TimeService.Start();
            timer.Start();
        }

        private static void TimeService_TimeSyncFailed(object sender, TimeSyncFailedEventArgs e)
        {
            Debug.Print("Error synchronizing system time with NTP server: " + e.ErrorCode);
        }

        private static void TimeService_SystemTimeChanged(object sender, SystemTimeChangedEventArgs e)
        {
            Debug.Print("Network time received.");
            if (!timeSynchronized)
                timeSynchronized = true;
        }

        private void timer_Tick(object sender, EventArgs e)
        {            
            if (timeSynchronized)
                txtDateTime.TextContent = DateTime.Now.ToString();
        }
    }
}

Per completare il codice, dovremo aggiungere anche la gestione dell’ora legale, di cui ci occuperemo in uno dei prossimi post.

L’applicazione di esempio è disponibile per il download.

TimeServiceExample.zip

Advertisements

,

  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: