• Non ci sono risultati.

L’applicazione client desktop permette di accedere e consumare il servizio. Per fare ciò l’applicazione deve ottenere il feed nel quale sono collezionati i link alla varie immagini. Ognuno di questi link va a rappresentare la richiesta al servizio di una singola immagine. La scelta riguardante quali immagini andare effettivamente ad ottenere tra quelle esposte è fatta in base ai limiti imposti dalla velocità di connessione e dalla disponibilità di memoria.

Questa applicazione vuole dare all’utente la possibilità di visualizzare le immagini ottenute dal servizio e di navigare fra esse, vengono infatti mantenute in memoria le cento immagini più recenti ottenute dal servizio tra le quali l’utente può scegliere quale visualizzare. Oppure è possibile attivare la modalità che permette di visualizzare sempre l’ultima immagine ottenuta. Uno degli aspetti che si è voluto curare nella realizzazione del servizio assicurare che l’interfaccia grafica non subisca rallentamenti durante il download di un immagine. Per questo ci si è preoccupati di andare ad utilizzare meccanismi per la gestione dell’esecuzione parallela.

Nella creazione di tale applicazione client sono state individuate alcune funzioni fondamentali che hanno portato alla necessità di creare determinati componenti e di effettuare determinate scelte architetturali:

• ricezione dati;

• gestione interazione con il servizio;

• gestione dei dati ricevuti;

• visualizzazione dei dati;

• gestione log di sistema.

L’applicazione client in questione è stata realizzata utilizzando il linguaggioVisual Basic.NET. È inoltre interessante notare che grazie all’utilizzo di un servizio REST non è strettamente necessario utilizzare WCFper comunicare con esso.

La gestione di tali funzionalità è eseguita tramite alcuni componenti che interagiscono tra loro tramite un preciso schema. I componenti in gioco sono i seguenti:

• Receiver: gestisce le richieste al servizio;

• ServiceManager: decide come e quando eseguire le richieste al servizio;

• DataManager: gestisce la memorizzazione dei dati;

• Interfaccia utente: mostra le immagini e gestisce l’iterazione con l’utente;

• Gestore di log: fornisce metodi per l’inserimento di messaggi nel log dell’appli-cazione.

In Figura 5.6 viene riportato il diagramma delle classi dell’applicazioneImageConsumer.

Luca Pasa 5.3. IL CLIENT DESKTOP: IMAGECONSUMER

#Dispose(in disposing : Boolean) -InitializeComponent()

-FrmClient_Disposed(in sender : Object, in e : EventArgs) -FrmClient_Load(in sender : Object, in e : EventArgs) +HandleNewItem(in sender : Object, in e : EventArgs) -sldImgNav_Scroll(in sender : Object, in e : EventArgs) -changeImage(in img : Image, in dt : Date) -components : IContainer

-ImgItemInverseComparison(in x : ImageItem, in y : ImageItem) : Integer +add(in dt : Date, in img : Image)

+ItemInserted(in sender : Object, in e : EventArgs) : EventHandler +OnAdd(in e : EventArgs)

-getImgAsync(in imgDate : Date, in NTime : Integer = 0) +StartUpdateImgs()

-OnTimedEvent(in source : Object, in e : ElapsedEventArgs) -_lastUpdate : Date = DateTime.MinValue +CompareTo(in obj : Object) : Integer -_img : Image

-_dTime : Date ImageItem +receiveNewImageDataList(in imgAfter : Date) : List

-InverseDateOrder(in x : Date, in y : Date) : Integer +GetImagebyDate(in dtime : Date) : Image

-BASE_SERVICE_URL : String = "https://webcamprototype.servicebus.appfabriclabs.com/imageFabric/"

L'evento che segnala l'inserimento di un oggetto sollevato da DataManager, viene catturato da FrmClient al fine di gestire in maniera efficente la visualizzazione dei dati.

Singleton

Singleton

ComponentModel::Component

IComparable

+imageToStream(in img : Image, in iFormat : ImageFormat) : Stream +streamToImage(in stream : Stream) : Image

ImgCoverter

Figura 5.6: Schema delle classi diImageConsumer.

61 61 di 87

L’interazione tra i componenti segue una precisa logica: ilServiceManagerattende una richiesta da parte dell’interfaccia utente per cominciare l’interazione con il servizio, che avviene tramite l’utilizzo dei metodi messi a disposizione dalReceiver. Al momento dell’arrivo dei dati Servicemanagerva ad immagazzinarli in memoria tramite l’utilizzo delDataManager, il quale si preoccuperà di segnalare all’interfaccia utente la presenza di un nuovo dato da visualizzare. In Figura 5.7 viene riportato lo schema che mostra come interagiscono tra loro questi componenti.

ImageFabric Receiver

ServiceManager

Interfaccia utente

DataManager

Evento D’inserimento Nuovo dato ImageConsumer

Figura 5.7: Schema interazione tra componenti diImageConsumer.

5.3.1 ricezione dati

La ricezione dei dati esposti dal servizio richiede due principali operazioni: la ricezione del feed RSS e la ricezione delle singole immagini esposte. Queste funzioni sono implementate all’interno della classe Receiver.

Per quanto riguarda la prima operazione si va a accedere alla URI a cui è esposto il feed e si va leggere il codiceXMLche lo rappresenta. Tale codice viene codificato tramite l’oggetto XmlReader, ed il risultato utilizzato per creare un oggettoSyndicationFeed. L’utilizzo di un oggetto SyndicationFeed ci permette un facile e veloce accesso ai dati evitando così di eseguire un parsing manuale del XML. Per questo metodo si è inoltre ritenuto necessario dare la possibilità all’utilizzatore di filtrare i dati che si vogliono ottenere. Il filtraggio avviene per data, dando la possibilità di ottenere informazioni strettamente successive ad una data e ora passate come parametro del metodo che permette la ricezione stessa.

Un’altra peculiarità di questo metodo è data dal fatto che il valore restituito è un lista di oggettiDateTimeche rappresentano la data e l’ora di acquisizione delle nuove immagini esposte dal servizio. Questo è possibile grazie al fatto che si è deciso di utilizzare la data e l’ora di acquisizione come identificativi per le immagini. Questa scelta si ripercuote inoltre sul secondo metodo che esegue la ricezione, infatti esso richiede come parametro di input la data e l’ora di acquisizione dell’immagine che si

Luca Pasa 5.3. IL CLIENT DESKTOP: IMAGECONSUMER desidera ottenere. Questo parametro viene utilizzato per andare a creare l’URI che permette di effettuare la richiesta al servizio, l’URI ha la seguente forma:

https://webcamprototype.servicebus.appfabriclabs.com/imageFabric/getImage/[Data e ora dell’immagine richiesta]

Creata tale URI possiamo eseguire un HttpWebRequest per ottenere i dati richiesti che ci saranno inviati comeStream, per farlo è però prima necessario impostare il metodo per la richiesta a Get. Appena ci arriverà la HttpWebResponse possiamo andare a convertire loStream nell’immagine, e per farlo si utilizza le funzionalità messe a disposizione dalla classeImageConverter. Questa classe fornisce due metodi statici che provvedono alla conversione daStreama immagine e viceversa.

In questi metodi possiamo notare come non ci sia traccia dell’utilizzo diWCF, questo è stato possibile grazie all’utilizzo di un servizioRESTFul, rendendo quindi possibile la creazione di client anche non utilizzando tecnologia microsoft. Inoltre ricordiamo che il servizio è esposto tramite l’utilizzo diWindows Azure AppFabric Service Bus, ma come si può vedere nei metodi che si interfacciano con il servizio, non è presente alcuna riga di codice che dipenda dall’utilizzo di questa tecnologia. Questo dimostra la trasparenza di questo servizio offerto dalla piattaformaWindows Azure del quale però si possono apprezzare i benefici: non è infatti stato necessario eseguire alcuna modifica ad impostazione di firewall o NAT per eseguire la connessione al nostro servizioImageFabric.

5.3.2 gestione interazione con il servizio

La gestione dell’interazione con il servizio è affidata alla classeServiceManager. In questa classe è definito quando e come eseguire le richieste al servizio e come gestire i dati ricevuti. Per assicurare che la gestione del servizio avvenga attraverso un unica istanza di questa classe è stato deciso di far in modo che implementi il design pattern Singleton. La classe prevede che essa venga avviata da un richiesta di attivazione dell’interazione con il servizio e poi possa gestire autonomamente tale interazione andando a salvare i dati tramite il DataManager. Alla ricezione di tale richiesta, che avviene tramite il metodoStartUpdateImgs, viene creato unTimer il quale ogni cinque secondi solleva l’eventoElapsed che è gestito dal metodoOnTimedEvent. In questo metodo vengono eseguite due operazioni: l’aggiornamento dei dati e viene calcolato fra quanto tempo verrà eseguito il prossimo aggiornamento. Viene infatti tenuto conto del tempo richiesto per la ricezione dei dati, tempo che viene detratto dall’intervallo dei cinque secondi, in modo da avere la massima regolarità nelle richieste ed avere piene indipendenza da eventuali problemi di velocità nella rete.

Nel poco probabile caso che l’invio dell’immagine richieda più di cinque secondi ci si limiterà ad eseguire l’aggiornamento cinque secondi dopo, andando così a saltare uno o più aggiornamenti previsti, al fine di non accodare inutilmente richieste, che nel lungo periodo potrebbero causare un blocco del sistema.

Per quanto riguarda l’aggiornamento dei dati viene utilizzato il metodo UpdateIm-ageList. All’interno di questo metodo si richiede per prima cosa al servizio (tramite il Receiver) la lista degli ultimi elementi esposti più aggiornati rispetto a quelli di cui si è già in possesso. Ricevuti tali dati si estrae la data e ora più recente, e si esegue la richiesta al servizio di tale immagine. La richiesta di un’immagine può avere un peso importante, e quindi anche un costo a livello computazionale rilevante ai fini dell’esecuzione, inoltre esiste la possibilità di voler eseguire la richiesta di due o più immagini, andando così a rendere molto costosa tale operazione. È per questo stato deciso di implementare questa operazione in maniera asincrona andando ad applicare

63 63 di 87

il concetto del parallel computing. Per farlo è stato deciso di utilizzare la libreria Task Parallella quale, oltre a rendere possibile l’esecuzione di un applicazione in multi-threading, permette l’ottimizzazione dell’esecuzione di task anche su processori multicore. L’applicazione di tale scelta all’esecuzione della richiesta di immagini al servizio ha portato a dover affrontare alcune problematiche. Una di queste è stata la gestione di problemi di connessione o di temporanea impossibilità di ottenere i dati.

In questo caso sì è deciso di tentare per tre volte, e in caso di fallimento di tutti e tre i tentativi, di gestire l’errore. Il codice di richiesta è stato incapsulato all’interno della funzione getImgAsyncnella quale oltre ad eseguire il tentativo di ottenere l’immagine dal servizio va anche, in caso di successo, ad inserirla in memoria. Per farlo si va ad utilizzare le funzioni messe a disposizione dalDataManager.

5.3.3 gestione dei dati ricevuti

La gestione dei dati viene eseguita tramite la classe DataManager. Questa classe si occupa del salvataggio in memoria dei dati, dell’inserimento e della lettura degli stessi.

Viene inoltre qui definito il numero massimo di dati mantenuti contemporaneamente.

Si è deciso di mantenere in memoria non più di cento elementi, onde evitare sovrac-carico di memoria. Gli elementi vengono mantenuti in una struttura dati di tipo ListOf(T). per incapsulare i vari elementi si è creata la classeImageItemLa quale mantiene in memoria le sole informazioni utili: l’immagine e la data e l’ora in cui l’immagine è stata catturata. Questa classe va inoltre ad implementare l’interfaccia IComparable che permette di eseguire confronti tra oggetti da essa definiti. Essendo che i confronti in questo caso vengono utilizzati per ordinare temporalmente i vari elementi, si è deciso di basare l’implementazione di tale interfaccia sulla data e l’ora salvate in modo da permettere un facile ordinamento temporale degli elementi ricevuti dal servizio.

La classeDataManager comunica con altre 2 classi: con ilServiceManager che richia-ma i metodi per eseguire l’inserimento degli elementi, e con l’interfaccia utente (FrmClient) tramite un evento che segnala la disponibilità di nuovi dati.

Nel primo caso il metodo che viene esposto allo scopo di inserire nuovi elementi è il metodo add. Questo metodo richiede come parametri d’ingresso l’immagine, la data e l’ora in cui è stata catturata. Con questi dati va a creare un oggetto di tipo ImageItemed inserirlo nella lista. Viene inoltre controllato che non venga superato il numero massimo di elementi che si è deciso di mantenere in memoria, nel caso ci si trovasse in questa situazione il metodo sì occuperebbe di eliminare l’elemento meno recente presente nella lista. Alla fine di queste operazioni viene sollevato l’eventoItemInsertedper segnalare all’interfaccia utente la presenza di nuovi dati da visualizzare. Tale evento viene definito all’interno della classe, insieme al delegate che permette alla classe ricevente di gestirlo.

L’interfaccia grafica necessita anche di conoscere gli oggetti presenti in memoria, per questo è stata creata la funzione getListche ritorna una copia ordinata per data (dalla più recente alla meno recente) della lista di elementi attualmente mantenuti in memoria. La scelta di eseguire l’ordinamento al momento della restituzione dalla lista è data dal fatto di voler minimizzare il numero di operazioni di questo genere. Si ritiene infatti che il numero di inserimenti sia maggiore del numero di letture, anche in proiezione di sviluppi futuri dell’applicazione che magari prevedano l’interazione con più servizi.

Visto l’utilizzo di multithreading e parallel computing da parte delServiceManager, si è ritenuto opportuno proteggere le operazioni eseguite sui dati all’interno di questa classe tramite blocchi SyncLock. Il SyncLockgarantisce che il blocco di istruzioni non venga eseguito da più thread contemporaneamente.

Luca Pasa 5.3. IL CLIENT DESKTOP: IMAGECONSUMER

SyncLockimpedisce infatti a ciascun thread di accedere al blocco durante l’esecuzione di quest’ultimo da parte di altri thread. SyncLockviene in genere utilizzato per evitare che i dati vengano aggiornati da più thread contemporaneamente. Se le istruzioni che modificano i dati devono essere completate senza interruzioni, è opportuno inserirle in un bloccoSyncLock. Il SynckLock basa il suo funzionamento su un riferimento ad un oggetto dettolockobject del quale i vari thread vanno a prendere l’accesso esclusivo, in questo caso tale oggetto è la lista di oggettiImageItemossia la struttura dati su cui vengono effettuate le operazioni.

Si è voluto infine assicurare che la gestione e la memorizzazione dei dati avvenga sempre tramite la stessa istanza di questa classe, a questo scopo si è fatto im modo cheDataManagerimplementi il design pattern Singleton.

5.3.4 visualizzazione dei dati

La classe che gestisce la visualizzazione dei dati e gestisce le operazioni dell’utente è FrmClient. Questa classe assolve il compito di visualizzare i dati all’utente e gli permette una navigazione tra essi. Grazie a questa classe l’utente può inoltre decidere quale modalità di visualizzazione usare: o la visualizzazione che permette di navigare tra le varie immagini, oppure la modalità che va a visualizzare sempre l’immagine più recente.

Il layout grafico è composto da unaPictureBoxnella quale viene visualizzata l’immag-ine scelta, unalabel dove vengono visualizzate le informazioni legate all’immagine, una Combobox che permette di selezionare quale modalità utilizzare e uno slider per navigare all’interno della collezione d’immagini. Il layout grafico e mostrato in Figura 5.8

Figura 5.8: Screen shoot di ImageConsumerin funzione.

All’attivazione dell’applicazione viene subito richiamata la funzione StartUpdateImgs del ServiceManagerche fa iniziare le richieste verso il servizio. Sempre al

momen-65 65 di 87

to dell’inizializzazione viene configurato il metodo che va a raccogliere l’evento ItemInserted sollevato dal DataManager. Quando viene inserito un nuovo elemen-to nelDataManager viene sollevato l’evento ItemInserted, tale evento viene gestito dal metodo HandleNewItemnel quale trova spazio la gestione delle due modalità di visualizzazione.

La gestione di un evento avviene attraverso un thread diverso dal thread grafico, che è l’unico a poter eseguire modifiche all’interfaccia grafica. Per questo sono state implementate politiche di marshalling in modo da riportare l’esecuzione al thread grafico prima di eseguire modifiche all’interfaccia. Infatti i controlli in Windows Form sono associati a un thread specifico e non sono thread-safe. Pertanto, se si chiama il metodo di un controllo da un thread diverso, è necessario utilizzare uno dei metodi Invoke del controllo per effettuare il marshalling della chiamata al thread adeguato. Per capire se è necessario o meno utilizzare uno dei metodiinvoke si può utilizzare la proprietàControl.InvokeRequiredche restituisce un booleano. Nel caso specifico viene valutata la proprietà Control.InvokeRequiredrelativa al form stesso, e in caso restituisca true viene utilizzato il comando Invoke, il quale permette di eseguire un delegato nel thread proprietario dell’handle del form.

Eseguito ilmarshalling è ora possibile andare ad eseguire le operazioni che porter-anno all’aggiornamento dell’interfaccia grafica. Per prima cosa viene invocato il DataManager richiedendo una copia della lista degli elementi attualmente mantenu-ti in memoria. Ora è necessario differenziare la gesmantenu-tione delle due modalità di visualizzazione dei dati:

• modalità di visualizzazione dell’elemento più recente: in questa modalità il cursore delloSliderpunta sempre all’elemento più recente, ed inoltre l’elemento viene visualizzato nellaPicture Box. Per questo motivo la prima operazione che viene fatta è il controllo se l’immagine più recente presente nella nuova lista è più recente di quella attualmente visualizzata. In caso positivo si va ad aggiornare i dati visualizzati, altrimenti ci si limita ad aggiornare la lista di visualizzazione ed i limiti dimensionali delloSlider nel caso in cui gli elementi inseriti siano di più di quelli precedentemente visualizzati;

• navigazione tra gli elementi attualmente in memoria: in questa modalità al momento dell’inserimento di nuovi elementi il cursore rimane sull’elemento attualmente visualizzato. È quindi importante, in questa modalità, andare a ricalcolare la posizione del cursore delloSlider dopo l’aggiornamento. Per farlo sì è utilizzata la funzione di ricerca all’interno della lista la quale dato un elemento ne restituisce l’indice. È però stato controllato il caso particolare in cui l’elemento non è più presente nella lista perché eliminato in quanto troppo poco recente. In questo caso si è deciso di visualizzare l’elemento temporalmente più vicino a quello precedentemente puntato, ossia il meno recente attualmente presente nella lista. Anche in questo caso vengono aggiornati i limiti dello Slidernel caso in cui il numero di elementi nell’attuale lista risulti maggiore di quelli contenuti nella precedente.

In questa classe è inoltre presente la gestione della navigazione dell’utente all’interno della lista dei dati. L’utente effettua tale operazione utilizzando loSlider, ad ogni movimento del cursore dello Slidercambierà la visualizzazione dei dati andando a modificare il contenuto dellaPicture box e della labelche mostra le informazioni relative all’immagine. Il cambiamento di questi due controlli Windows Form avviene sempre in contemporanea, ed i dati mostrati possono essere incapsulati in un oggetto di tipoImageItem. Sì è per questo deciso di incapsulare tali operazioni di modifica, che vanno a variare l’interfaccia grafica, in un metodo chiamato changeImage che

Luca Pasa 5.3. IL CLIENT DESKTOP: IMAGECONSUMER richiede come parametri di input il valore dei dati da mostrare. In questo modo si è certi di poter mantenere coerenza tra l’immagine mostrata nellaPicture box e i relativi dati mostrati nellaLabel.

5.3.5 Gestione log di sistema

Il sistema utilizzato per implementare la gestione del log è lo stesso utilizzato per l’applicazioneImageFabric

67 67 di 87