Technology Experience
.NET World

SignalR e notifiche di allarmi su diversi tipi di client, desktop e mobile

Introduzione
E’ da molto tempo che volevo studiare, almeno superficialmente, SignalR. Sebbene non segua molto le tecnologie Web, ogni tanto nella mia testa suona una specie di campanello di allarme (o di notifica, meglio), che mi dice: “Ok, quella cosa riguarda il Web, ma sento che potrebbe essere utile anche a me“. Anche perchè, parliamoci chiaro, io del Web odio solamente l’HTML ed i browser: la parte più stupida del client, insomma: passatemi questa definizione. SignalR è uno di quei topic che hanno fatto suonare quel campanello, senza mai trovare davvero il tempo e le risorse da dedicarci. Fino alla settimana scorsa, quando mio fratello Omar, improvvisamente, senza alcun input da parte mia, mi manda un bel sample proprio su SignalR. Uno di quei sample perfetti, di quelli che apri con Visual Studio, premi F5, compila e parte tutto, capisci alla perfezione, e poi passi ad analizzare il codice. Ecco, è andata proprio così. E quindi, grazie a quel sample ed all’intervento di mio fratello, è nato questo post.

Di cosa parliamo?
La settimana scorsa mi sono messo ad implementare un piccolo prototipo, che poi ho pubblicato sul mio account GitHub, che implementa la seguente struttura:

WP_20150902_10_26_09_Pro

In parole povere, abbiamo i seguenti componenti:

  • un device fisico, rappresentato a sinistra, cioè un dispositivo di qualche tipo (un sensore di temperatura? un frigorifero? un’auto? una caldaia? oppure…un razzo?). Questo dispositivo, tra le altre cose, è in grado di rilevare i suoi allarmi interni (temperatura alta, fuori traiettoria, perdita liquido refrigerante, motore fuori servizio), e di comunicarli a qualcuno
  • questo “qualcuno” è un dispatcher, rappresentato al centro, che nel mio caso è un server implementato con SignalR, server che ho pubblicato su Azure, quindi raggiungibile in modo globale. Qual è il suo compito? Ogni volta che riceve un allarme, avvisa tutti i client che risultano connessi, inviando loro un messaggio (leggesi: una classe DTO)
  • I client di qualsiasi tipo, rappresentati a destra, ricevono il messaggio ed avvisano l’utente nel modo più consono ed opportuno. Ciascun client all’avvio si collega al server SignalR implementato nel punto precedente di questo elenco

Trovate qui il progetto: https://github.com/VivendoByte/AlarmNotifier.

E’ importante che la cosa sia il più possible in real-time, ed è per questo che ho deciso di studiare e di passare attraverso SignalR.

Qualche dettaglio tecnico in più
Diamo un’occhiata al Solution Explorer che vi trovate nel momento in cui aprite la solution che ho pubblicato. Eccolo qui:

solution_explorer

  • C’è una solution folder denominata Client, al cui interno ho messo 4 tipi diversi di progetto: Console, Universal App per Windows 10, Windows Phone 8.1 e classica applicazione desktop in WPF. Questi progetti rappresentano i tipi diversi di client che possono ricevere la notifica di allarme
  • C’è una solution folder denominata Dispatcher, al cui interno ho messo 2 tipi diversi di progetto: Console e Web. Questi due progetti rappresentano il dispatcher degli allarmi. Uno è console e quindi apre un server SignalR sul vostro PC. L’altro è un progetto di tipo Web, ed è lo stesso che ho pubblicato su Azure
  • Infine, ci sono altri due progetti: uno è VivendoByte.AlarmNotifier.Messages, una class library di tipo Portable, che contiene solo una piccola classe DTO per poter inviare e ricevere il messaggio. L’altro progetto è VivendoByte.AlarmNotifier.RocketDevice, che rappresenta il nostro device fisico capace di generare allarmi: è un po’ una cosa brutta, effettivamente, ma l’ho implementato come applicazione WPF, così è possibile premere un bottone e simulare lo scatenarsi di un allarme

Parte Server – Il Dispatcher
Vediamo per prima cosa la parte server di SignalR. il punto focale è l’Hub, che io ho chiamato NotifierHub e che ho implementato come segue:

[sourcecode language='csharp'  padlinenumbers='true']
public class NotifierHub : Hub
{
	public void RaiseAlarm(AlarmMessage notification)
	{
		Clients.All.SendNotification(notification);
	}
	public override Task OnConnected()
	{
		return base.OnConnected();
	}

	public override Task OnDisconnected(bool stopCalled)
	{
		return base.OnDisconnected(stopCalled);
	}
}
[/sourcecode]

Il metodo RaiseAlarm che vedete implementato qui sopra viene invocato dal device (lo vediamo dopo), nel nostro caso il razzo. Ogni volta che il razzo segnala un allarme, si entra nel metodo RaiseAlarm; l’istanza di AlarmMessage che arriva come parametro contiene tutti i dettagli sull’allarme: data/ora, gravità, descrizione, eccetera. Quello che fa SignalR è rigirare l’allarme a tutti i client che risultano connessi in quel momento, con quella semplice linea di codice. Il dispatcher fa solo questo: riceve un allarme e lo rimanda ai client. Da notare due cose:

  • la proprietà All è di tipo dynamictooltip
  • il metodo SendNotification, che qui chiamiamo internamente al dispatcher, viene poi invocato anche sui client: lo vedremo più tardi

Il device fisico – Il Razzo

Adesso vediamo in breve il dispositivo fisico. Questo è il device capace di segnalare gli allarmi. Come dicevo prima, l’ho scritto in WPF, così ha un’interfaccia utente per poter premere un bottone e scatenare un allarme. Il code-behind è piuttosto semplice. Per prima cosa, ho implementato un metodo chiamato Connect:

[sourcecode language='csharp' ]
public MainWindow()
{
    InitializeComponent();
    this.Connect();
}

private async void Connect()
{
    Connection = new HubConnection(ServerURI);
    HubProxy = Connection.CreateHubProxy("NotifierHub");

    try
    {
        await Connection.Start();
    }
    catch (HttpRequestException)
    {
        return;
    }
}
[/sourcecode]

Questo metodo viene eseguito dal costruttore della finestra. In questo modo il nostro razzo si connette immediatamente al server SignalR, e ci mettiamo nella condizione di poter comunicare gli allarmi. Notate che il parametro del metodo CreateHubProxy, di tipo stringa, deve corrispondere al nome della classe Hub definita server-side. Quando nel razzo si scatena un allarme (ripeto: nell’esempio dobbiamo premere manualmente un bottone) accade quanto segue:

[sourcecode language='csharp' ]
private void SendAlarm_Click(object sender, RoutedEventArgs e)
{
    AlarmMessage a = new AlarmMessage();
    HubProxy.Invoke("RaiseAlarm", a);
}
[/sourcecode]

Molto semplice, direi. Costruisco un’istanza di AlarmMessage e la mando al server SignalR di cui abbiamo parlato prima. Il nome del metodo RaiseAlarm è espresso tramite una stringa, ed è quello che abbiamo visto implementato prima nel NotifierHub.

Ovviamente, in produzione un device comunicherebbe un allarme nel momento in cui si scatena realmente, e non c’è alcun utente a premere un bottone.

Parte Client – I diversi tipi di client

La parte client è l’ultima parte di tutto questo giro. Nel progetto su GitHub ne ho implementati quattro tipi diversi, qui parliamo solo della Console Application perchè è la più semplice da trattare. E’ importante dire che il codice per la connessione al server SignalR è lo stesso: diverso semmai è il comportamento che il cliente assume nel momento in cui riceve l’allarme, perchè ogni client ha un’interfaccia diversa e quindi comunica all’utente in modo diverso.

Ecco il codice:

[sourcecode language='csharp' ]
static void Main(string[] args)
{
    Connect();
}
static async void Connect()
{
    Connection = new HubConnection(ServerURI);
    HubProxy = Connection.CreateHubProxy("NotifierHub");

    HubProxy.On<AlarmMessage>("SendNotification", (notification) =>
        {
            System.Console.WriteLine(notification);
        }
    );

    try
    {
        Connection.Start();
    }
    catch (HttpRequestException)
    {
        return;
    }

    System.Console.WriteLine("Console Client started!!!");
    System.Console.ReadKey();
}
[/sourcecode]

Il metodo Connect crea un’istanza di HubProxy, specificando “NotifierHub” come parametro. Si sottoscrive alla ricezione del messaggio AlarmMessage. Il tutto viene gestito in questo caso tramite un anonymous method: quando viene ricevuto un allarme, la Console Application non fa nient’altro che scriverlo sulla console. Nel caso di applicazioni più complesse, come ad esempio un’applicazione WPF, potremmo aggiungere l’oggetto AlarmMessage ad una ListBox, oppure mostrare un balloon di notifica sullo schermo. Il primo parametro del metodo On è “SendNotification”, ed è quello che abbiamo specificato sul server SignalR; il secondo parametro è un Action<T>, dove T nel nostro caso è AlarmMessage.

Conclusioni

SignalR, come recita il sito ufficiale, è una libreria per sviluppatori ASP.NET in grado di implementare con poco sforzo una comunicazione real-time alle nostre applicazioni. E per citare… “What is “real-time web” functionality? It’s the ability to have your server-side code push content to the connected clients as it happens, in real-time”. Direi che è uno scenario perfetto per la comunicazione di allarmi, ma anche per applicazioni di chat, o di videogiochi, o per tutto quello che ci passa per la testa.

Per chiudere, fate clone dal mio repository su GitHub e provate a dargli un’occhiata.

Send to Kindle

Igor Damiani

La sua passione per l'informatica nasce nella prima metà degli anni '80, quando suo padre acquistò un Texas Instruments TI-99. Da allora ha continuato a seguire l'evoluzione sia hardware che software avvenuta nel corso degli anni. E' un utente, un videogiocatore ed uno sviluppatore software a tempo pieno. Igor ha lavorato e lavora anche oggi con le più moderne tecnologie Microsoft per lo sviluppo di applicazioni: .NET Framework, XAML, Universal Windows Platform, su diverse piattaforme, tra cui spiccano Windows 10 piattaforme mobile. Numerose sono le app che Igor ha creato e pubblicato sul marketplace sotto il nome VivendoByte, suo personale marchio di fabbrica. Adora mantenere i contatti attraverso Twitter e soprattutto attraverso gli eventi delle community .NET.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.