martedì 29 novembre 2011

XeDotNet Meeting - Visual Studio Architect Tools

Volevo segnalarvi il prossimo meeting di XeDotNet che si terrà il 2 dicembre 2011

UML con Visual Studio 2010
Speaker: Davide Vernole

Una corretta progettazione è alla base di un applicazione in grado di dare valore al cliente. Quando ci troviamo in questa fase del ciclo di vita di un'applicazione la domanda che spesso ci si pone è: quale notazione utilizzare? Visual Studio 2010 ci propone una possibile risposta indicando in UML la scelta standard da seguire. Sebbene l'implementazione di Visual Studio 2010 non sia UML al 100% la soluzione offerta aiuta a standardizzare l'approccio progettuale di uno o più team di sviluppo. La sessione si pone l'obiettivo di dimostrare queste funzionalità simulando l'intero processo di progettazione.

Layer Diagram con Visual Studio 2010
Speaker: Mirco Vanini

In Visual Studio Ultimate è possibile utilizzare un Layer Diagram per visualizzare l'architettura logica del sistema. Un Layer Diagram organizza gli elementi fisici nel sistema in gruppi logici e astratti chiamati Layer. Questi Layer consentono di identificare, descrivere e differenziare i tipi di attività eseguite dagli elementi. Ogni Layer può contenere anche livelli aggiuntivi o sottolivelli, che descrivono specifiche attività più piccole eseguite dai gruppi discreti di elementi. Utilizzando un Layer Diagram è possibile eseguire le attività seguenti: Comunicare l'architettura logica esistente o desiderata del sistema; Individuare i conflitti tra il codice esistente e l'architettura desiderata; Visualizzare l'impatto delle modifiche sull'architettura desiderata quando si esegue il refactoring, l'aggiornamento o l'evoluzione del sistema; Rinforzare l'architettura desiderata durante lo sviluppo e la manutenzione del codice includendo la convalida con le operazioni di archiviazione e compilazione. Durante questa sessione vedremo come sia possibile utilizzare questa tipologia di diagramma per migliorare la creazione e la manutenzione delle nostre applicazioni.

Venerdì 2/12/2011 ore 19:30
Novotel Castellana
Via Alfredo Ceccherini 21 - Venezia Mestre

per iscrizioni:
clicca qui

domenica 27 novembre 2011

Gestione prenotazioni con .NET Micro FW

Come vi avevo anticipato nei post precedenti vi anticipo qualche contenuto che sarà pubblicato  sul sito TinyCLR.it.

Introduzione

Durante la mia attività di sviluppatore mi sono imbattuto in svariate problematiche che ho risolto utilizzando le varie tecnologie presenti sul mercato. Non sempre queste tecnologie risolvevano in modo “pulito” tutti i punti di queste problematiche ma come si dice di necessità virtù!

Come tutti mi è capitato di riaffrontare le stesse problematiche a distanza di tempo e confrontare e/o rivedere le scelte tecnologiche fatte. In questo caso, che andrò ad illustrare, vi porto la mia esperienza di sviluppo di un sistema di gestione prenotazioni basato su dispositivi con il .NET MF 3.0 e la sua rivisitazione con il .NET FW 4.1 con le nuove funzionalità in esso contenute.

Richieste funzionali

Come in tutti i progetti si parte dalle richieste funzionali che il software deve soddisfare. Di seguito sono esposte le principali, in dettaglio:

Si richiede lo sviluppo di un sistema di gestione prenotazione code per punto vendita. Il sistema deve prevedere:

  • un pulsante di prenotazione
  • la stampa della prenotazione
  • un sistema di verifica della stessa prenotazione
  • un sistema di evasione della prenotazione
  • un sistema di consultazione dello stato coda
  • un sistema di notifica avanzamento coda

La rappresentazione dei moduli principali della soluzione può essere riassunto nel seguente schema:

Come si può notare dallo schema precedente la soluzione è suddivisa in varie parti, in dettaglio:

  • Un servizio di back-end che ospita tutta la logica delle prenotazioni, la loro persistenza, la logica delle notifiche, ecc.
  • Un modulo di gestione della richiesta di prenotazione.
  • Un modulo di gestione dell’emissione del ticket di prenotazione.
  • Un modulo di gestione interrogazione e/o verifica dei ticket di prenotazione.
  • Un modulo di gestione esecuzione prenotazione.
  • Un modulo di gestione di esecuzione notifica verso i vari dispositivi informativi.

In tutti i moduli in cui vi è un’iterazione con dispositivi esterni (stampanti, lettori di barcode, display, ecc.) si è scelto di adottare dei dispositivi con .NET FW.

I vari moduli basati su dispositivo MF, sono stati elencati come moduli separati per semplicità, ma nella realtà sono “racchiusi” in un’unica procedura.

Questa soluzione, nella sua prima versione, è stata sviluppata utilizzando VS2008 e .NET MF 3.0. Per la sua rivisitazione è stato poi adottato VS2010 e .NET MF 4.1. Oltre all’ambiente di sviluppo sono state cambiate molte parti della stessa cercando di utilizzare al meglio tutto quello che il nuovo .NET Micro FW metteva a disposizione con la nuova versione.

In questo articolo mi soffermerò soprattutto sul modo con cui i due mondi, servizio di back-end e dispositivi MF, si parlano e come questo sia evoluto con il cambio del MF.

I sorgenti allegati al presente sono divisi in due esempi, sample1 utilizza una comunicazione basata su socket mentre sample2 utilizza WCF per la parte di comunicazione. Per questi esempi è stato utilizzato VS2010 e .NE MF 4.1 in entrambe le sezioni, simulando nel primo caso la soluzione adottata con il FW 3.0.

Questa opzione è stata adottata per non costringere a chi utilizza il codice di avere installato e configurato entrambi gli ambienti.

Codice Allegato

Allegato all’articolo è fornito il codice sorgente di esempio trattato nei vari capitoli seguenti. La solution di VS 2010 è composta dai seguenti moduli:

 

  • Esempi con MF 3.0
    • Xe.Sample1.Server: progetto windows form che racchiude le funzionalità di back-end del servizio di gestione prenotazioni.
    • Xe.Sample1.MicroDevice: progetto device di tipo console che racchiude le funzionalità di consumo del servizio di gestione prenotazioni.
  • Esempi con MF 4.1
    • Xe.Sample1.FakeService: progetto window form per la generazione di un servizio fake.
    • Xe.Sample2.ReservationMarker: progetto device di tipo windows che racchiude le funzionalità di consumo di richiesta prenotazione.
    • Xe.Sample2.ReservationMicroProxy: progetto device i tipo class library che racchiude il proxy verso il servizio di gestione prenotazioni
    • Xe.Sample2.ReservationService: progetto windows form che racchiude le funzionalià del servizio di gestione prenotazioni.
    • Xe.Sample2.ReservationTicket: progetto device di tipo windows che racchiude le funzionalità di stampa della prenotazione.
    • Xe.Sample2.ReservationTicketClient: progetto di tipo windows form che racchiude le funzionalità di richiesta stampa della prenotazione.
    • Xe.Sample2.ReservationTicketProxy: progetto di tipo class library che contiene il proxy verso il servizio di gestione della stampa del ticket prenotazione

P.S.: Per ovvi motivi commerciali e di chiarezza il codice illustrato ed allegato a questo articolo è solo una parte riadattata della soluzione realizzata.

Esempio con MF 3.0

L’esempi che utilizza il MF 3.0 è composto dalle seguenti parti:

  • Reservation Service (.NET WinForm)
    • TcpCommandListener
    • MicroProtocol
    • ThreadPooling
  • Reservation Marker (.NET Micro FW Console)
    • TcpCommandListener
    • DeviceManager
    • MicroProtocol
Reservation Service(Xe.Sample1.Server)

Le funzionalità base del servizio di prenotazioni sono le seguenti:

  • Richiesta di una nuova prenotazione su una particolare coda (AskNewQueueReservation)
  • Richiedi lo stato di una particolare coda (GetCurrentQueueReservation
  • Esegui la prenotazione corrente per una determinata coda (ExecCurrentQueueReservation)

Queste funzionalità del servizio sono esposte ai vari dispositivi utilizzando un protocollo basato su socket nella versione 3.0 del .NET MF. Il protocollo che viene illustrato è semplice ed epurato di tutte le complessità e controlli che un protocollo deve avere. Le richieste verso il servizio di prenotazioni sono gestite tramite un listener socket che ascolta e cattura le richieste provenienti dai vari dispositivi

Le classi principali di questo modulo sono:

  • QueueManager: classe di gestione prenotazioni
  • TcpCommandListener: classe di gestione richieste / risposte tramite socket
  • MicroProtocol: classe di gestione protocollo da e per i device
Reservation Marker(Xe.Sample1.MicroDevice)

Questo modulo è sviluppato utilizzando .NET MF e costituisce il consumatore del servizio di prenotazione precedentemente illustrato. Trami questa modulo è possibile richiedere le seguenti azioni:

  • Eseguire una nuova prenotazione.
  • Richiedere lo stato di una specifica coda.
  • Eseguire la prenotazione corrente.

Il modulo utilizza una comunicazione basata su socket e su un protocollo proprietario. Questo protocollo ovviamente è condiviso con la parte di back-end.

Le classi principali di questo modulo sono:

  • DeviceManager: classe di gestione del device
  • TcpCommandListener: classe di gestione richieste / risposte tramite socket
  • MicroProtocol: classe di gestione protocollo da e per il serivzio di back-end
Considerazioni Sample1

Se si osserva il codice allegato si potrà notare la complessità nella comunicazione tra dispositivo e server di back-end. Tale complessità è insita nella tecnologia utilizzata, i socket sono sempre la strada più performante ma come tutte le cose ha un costo, la relativa complessità di implementazione ma soprattutto la sua manutenzione. Ogni volta che doppiamo modificare e/o estendere il protocollo di comunicazione per nuove funzionalità il lavoro da fare non è poco. Il .NET MF 3.0 offre i DPWS, non certo facili da implementare ma soprattutto non supportati nella versione 3.5 del .NET FW lato desktop. Oltre a questo il server e i dispositivi devono conoscersi, in altre parole il dispositivo deve conoscere l’indirizzo IP e la relativa porta a cui il servizio risponde, viceversa anche il server deve conoscere l’indirizzo IP e la relativa porta del dispositivo a cui il servizio deve rispedire le risposte. Per evitare questo si posso implementare sistemi di identificazione o di auto censimento, ma come dicevo precedentemente, poi bisogna anche mantenerli e/o estenderli.

case MicroProtocol.ProtocolType.AskNewQueueReservation:
 
    AddMessage2Trace("Device -> AskNewRQueueeservation");
 
    ret = MicroProtocol.ReturnOkToken;
 
    ThreadPool.QueueUserWorkItem(state =>
    {
        var tmp = state as MicroProtocol;
 
        var resp = new MicroProtocol(MicroProtocol.ProtocolType.AskNewQueueReservationResponse,
                                     Queues.AskNewQueueReservation(tmp.Param).ToString());
                                    
        CmdListener.SendCommand(DeviceIpToken,
                                DeviceIpPortToken,
                                MicroProtocol.Serialize(resp));                    
    }, cmd);
 
    break;

Con l’uscita del .NET MF 4.1 e soprattutto con l’uscita del .NET FW 4.0 lato desktop le cose sono cambiate. In questa versione il MF supporta la versione 1.1 di DPSW compatibile con la versione 4.0 di WCF. Utilizzando il nuovo binding (ws2007HttpBinding) è possibile consumare servizi WCF direttamente dal dispositivo MF e viceversa, il dispositivo MF può esporre dei servizi consumabili tramite un client WCF.

Quest’ultima affermazione cambia radicalmente il possibile approccio per l’implementazione della comunicazione tra il servizio ed il dispositivo MF. Per questo motivo, e per il naturale evoluzione delle cose, l’applicazione è stata rivista adottando le ultime tecnologie disponibili.

Esempio con MF 4.1

L’esempi che utilizza il MF 4.1 è composto dalle seguenti parti:


  • Reservation Service (.NET WinForm)

    • WCF Service [service discoverable over UDP multicast]

  • Reservation Marker (.NET Micro WinApp)
  • Reservation MicroProxy (.NET Micro Class Library)

    • Discovery [send UDP request to the DPWS multicast address]

  • Reservation Ticket (.NET Micro WinApp)

    • DSPW Service

  • Reservation Ticket Proxy (.NET Micro Class Library)

    • Discovery [client discovery]

  • Reservation Ticket Client (.NET WinForm)

Reservation Service(Xe.Sample2.ReservationService)

Le funzionalità esposte dal servizio sono le stesse del modulo precedente. Quello che cambia in questo caso è come vengono esposte tali funzionalità. Infatti in questo caso le stesse sono erogate tramite un servizio WCF. La peculiarità di questo servizio è la possibilità dello stesso di essere individuato dai device tramite il multicast UDP. La creazione e la configurazione di questa modalità sono espresse nel codice della windows form che funge da host del servizio WCF.



if (Service != null && Service.State == CommunicationState.Opened)
    Service.Close();
 
Uri baseAddress = new Uri("http://" + 
                          "192.168.1.33" + 
                          ":8084/Xe.Sample2.ReservationService/ReservationService/");
 
Service = new ServiceHost(typeof(ReservationService), 
                          baseAddress);
 
ServiceEndpoint wsEndpoint = Service.AddServiceEndpoint(typeof(IReservationService), 
                                                        new WSHttpBinding(SecurityMode.None), 
                                                                          string.Empty);
EndpointDiscoveryBehavior endpointDiscoveryBehavior = new EndpointDiscoveryBehavior();
 
// Add the discovery behavior to the endpoint.
//
wsEndpoint.Behaviors.Add(endpointDiscoveryBehavior);
 
// Make the service discoverable over UDP multicast
//
Service.Description.Behaviors.Add(new ServiceDiscoveryBehavior());
Service.AddServiceEndpoint(new UdpDiscoveryEndpoint(DiscoveryVersion.WSDiscovery11));
 
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.HttpGetUrl = baseAddress;
Service.Description.Behaviors.Add(smb);
 
Service.Open();

In questa modalità i device MF non devono conoscere l’indirizzo IP del servizio ma solamente il nome del contratto e/o il guid dello stesso.


Reservation Proxy(Xe.Sample2.ReservationProxy)

Una volta definite il servizio WCF modifichiamo il modulo MF che richiede le prenotazioni. Per questo è stato creato un nuovo modulo che racchiude il proxy verso il servizio di back-end. Il proxy è racchiuso nell’esempio denominato Xe.Sample2.ReservationMicroProxy. Per la generazione del proxy si usa l’utility MFSvcUtil.exe inclusa nel sdk del .NET MF 4.1. Nel codice ho inserito anche il batch che invoca l’utility che genererà i seguenti file:



  • Reservation.cs
  • ReservationClientProxy.cs

Questi due file rappresentano il data contract serializer per le entità passate attraverso il canale e il proxy autogenerato. La classe di helper che permette di utilizzare il proxy autogenerato è ReservationProxy. Oltre alle funzioni di helper questa classe si incarica anche di esegure il discover del servizio. Questo viene eseguito specificando semplicemente il contratto esposto completo di name space.



private void CreateServiceProxy()
{
    try
    {
        Uri remoteEp = new Uri("http://192.168.1.33:8084/Xe.Sample2.ReservationService/ReservationService/");
        WS2007HttpBinding binding = new WS2007HttpBinding(new HttpTransportBindingConfig(remoteEp));
 
        /// ProtocolVersion11 can be used if the corresponding WCF desktop server application
        /// WcfServer uses wsHttpBinding instead of the custom binding "Soap11AddressingBinding"
        /// 
        _proxy = new ReservationServiceClientProxy(binding, 
                                                   new ProtocolVersion11());
 
        _proxy.IgnoreRequestFromThisIP = false;
 
        if (!Discover())
        {
            Debug.Print("Discovery failed, trying direct address");
            _proxy.EndpointAddress = "http://192.168.1.33:8084/Xe.Sample2.ReservationService/ReservationService/";
        }
    }
    catch (Exception ex)
    {
        Debug.Print(ex.ToString());
    }
}


private bool Discover()
{
    DpwsServiceTypes typeProbes = new DpwsServiceTypes();
    typeProbes.Add(new DpwsServiceType("IReservationService", 
                                       "http://localhost/ReservationService"));
 
    // A Probe is used to discover services on a network. 
    // The Probe method sends a UDP request to the DPWS multicast address, 
    // 239.255.255.250:3702. Any service that implements types specified 
    // in the filters parameter should respond with a ProbeMatches message. 
    // The ProbeMatches message is unicast back to the that client that made 
    // the request. If a null filter is supplied, any DPWS-compliant service 
    // should reply with a ProbeMatches response. 
    // Probe waits ReceiveTimeout for ProbeMatches. 
    //
    DpwsServiceDescriptions descs = _proxy.DiscoveryClient.Probe(typeProbes, 1, 20000);
 
    if (descs.Count > 0)
    {
        _proxy.EndpointAddress = descs[0].XAddrs[0];
        return true;
    }
 
    return false;
}

Il codice sopra esposto esegue il discover del servizio e ne ritorna endpoint address dello stesso. In caso di errore viene assegnato un endpoint address preconfezionato. Una volta eseguito il discover del servizio il proxy è pronto per essere utilizzato.


Reservation Marker(Xe.Sample2.ReservationMarker)

Questo modulo è sviluppato utilizzando .NET MF e costituisce il consumatore del servizio di prenotazione precedentemente illustrato. Trami questa modulo è possibile richiedere le seguenti azioni:



  • Eseguire una nuova prenotazione.
  • Richiedere lo stato di una specifica coda.

Il device utilizzato in questo esempio è dotato di touch screen e tramite questo viene eseguita la richiesta di una nuova prenotazione. Dopo l’invio della richiesta di una nuova prenotazione viene aggiornato lo stato corrente della coda di prenotazioni su cui è stata eseguita la richiesta stessa.



private void ImgTouchDown(object sender, TouchEventArgs e)
{
    try
    {
        _border.BorderBrush = _reverseBorderBrush;
        _border.Invalidate();
 
        Utility.Piezo(1000, 400);
 
        int ret = ReservationProxy.Instance.AskNewQueueReservation(QueueCodeToken);
 
        this.Dispatcher.BeginInvoke(new DispatcherOperationCallback(UpdateValues), 
                                    null);
 
        Debug.Print("AskNewQueueReservation: " + ret.ToString());
    }
    catch (Exception ex)
    {                
        Debug.Print(ex.ToString());
    }
}

Da notare l’utilizzo del dispatcher per la richiesta asincrona di aggiornamento della stato della coda.



private object UpdateValues(object input)
{
    try
    {
        var status = ReservationProxy.Instance.GetQueueStatus(QueueCodeToken);
        if(status == null)
            return null;
 
        _currentText.TextContent = Utils.PadLeft(status.Current.ToString(), 3);
        _nextText.TextContent    = Utils.PadLeft(status.Next.ToString(), 3);
        _currentText.Invalidate();
        _nextText.Invalidate();
 
        return status;
    }
    catch (Exception ex)
    {
        Debug.Print(ex.ToString());
        return null;
    }
}

Altra cosa fondamentale da notare è il valore di ritorno della chiamata GetQueueStatus. Il valore ritornato è un’istanza di una classe QueueStatus definita nel proxy. Tale classe è autogenerata dall’utility MFSvcUtil vista in precedenza. Questo punto è fondamentale. Nell’esempio precedente, ogni modifica al protocollo era costosa in ordine di tempo e di debug. In questo caso se il servizio modifica l’entità ritornata basterà semplicemente rigenerare il proxy.


Reservation Ticket(Xe.Sample2.ReservationTicket)

Nel modulo precedente abbiamo visto come consumare un servizio WCF, ora vediamo come sia possibile rendere disponibile un servizio WCF direttamente su un device con .NET Micro FW. Questo servizio sarà consumato da un applicativo lato desktop. Il modulo mette a disposizione un servizio WCF per la stampa del ticket di prenotazione su una stampante dedicata collegata direttamente sul device tramite porta USB. Per generare il servizio WCF sul device dobbiamo prima creare un servizio fake con cui interagire con l’utility MFSvcUtil. Il servizio fake in questione è il modulo denominato Xe.Sample2.FakeService. Questo modulo è un applicativo winform che contiene un servizio WCF con lo stesso contratto del servizio che vogliamo sia implementato lato device.



[ServiceContract]
public interface IReservationTicketService
{
    [OperationContract]
    void PrintReservationTicket(byte [] buffer);
}

Una volta implementato il servizio facciamo partire l’applicativo e tramite l’utility MFSvcUtil generiamo il proxy. Nel codice di esempio ho incluso il file batch che genera il proxy del servizio fake. Se notiamo l’utility, oltre al codice del proy, genera anche la classe per implementare hosting del servizio lato device con lo stesso contratto del servizio su cui si è eseguito il proxy. In dettaglio la classe generata è contenuta nel file ReservationTicketHostedService.cs. Ora il prossimo passo è quello di implementare la classe del servizio vero e proprio. Il codice è contenuto nel file ReservationTicketImpl.cs.



public PrintReservationTicketResponse PrintReservationTicket(PrintReservationTicket req)
{ 
    try 
    {            
        Program.Instance.InvokeUpdateAction(Resources.GetString(Resources.StringResources.ActionPrintTicket));
        
        Program.Instance.InvokePrintTicket(req.buffer);
 
        System.Threading.Thread.Sleep(500);
 
        var ret = new PrintReservationTicketResponse();
 
        Program.Instance.InvokeUpdateAction(Resources.GetString(Resources.StringResources.ActionWaitPrintTicket));
 
        Debug.Print("PrintReservationTicket");
        
        return ret;        
    }
    catch (Exception ex)
    {
        Debug.Print(ex.ToString());
 
        return new PrintReservationTicketResponse();
    }
}

La classe ReservationTicketImpl, che contiene il metodo sopra riportato, implementa l’interfaccia IIReservationTicketService generata sempre durante la fase di generazione del proxy.

Il servizio sul dispositivo è fatto partire nel main dell’applicativo:



Program.Instance.Dispatcher.BeginInvoke(new DispatcherOperationCallback(Program.Instance.AsyncStartService), null)

La funzione di start vera è propria è la seguente:



private void StartService()
{
    try
    {
        // Initialize the binding
        //
        string guid = "urn:uuid:926A876C-6C6A-4FC5-B665-05B36DDBB932";
 
        // ProtocolVersion10 can be used only if the corresponding client application
        // uses a custom binding with Soap12WSAddressingAugust2004 text message encoding.
        //
        ProtocolVersion version = new ProtocolVersion11();
        Device.Initialize(new WS2007HttpBinding(new HttpTransportBindingConfig(guid, 8085)), 
                          version);
 
        // Set device information
        //
        Device.ThisModel.Manufacturer    = "MyCompany";
        Device.ThisModel.ManufacturerUrl = "http://www.mycompany.com/";
        Device.ThisModel.ModelName       = "Reservation Ticket Device";
        Device.ThisModel.ModelNumber     = "1.0";
        Device.ThisModel.ModelUrl        = "http://www.mycompany.com/";
        Device.ThisModel.PresentationUrl = "http://www.mycompany.com/";
 
        Device.ThisDevice.FriendlyName    = "ReservationTicketService";
        Device.ThisDevice.FirmwareVersion = "alpha";
        Device.ThisDevice.SerialNumber    = "32345678";
 
        // Add a Host service type
        //
        Device.Host = new ReservationTicketService(version);
 
        // Add Dpws hosted service(s) to the device
        //
        Device.HostedServices.Add(new IReservationTicketService(new ReservationTicketImpl()));
 
        // Set this device property if you want to ignore this clients request
        //
        Device.IgnoreLocalClientRequest = false;
 
        // Turn console messages on
        //
        Console.Verbose = true;
 
        Debug.Print("Start DPWS device service with endpoint address: '" + Device.EndpointAddress + "'");
 
        ServerBindingContext ctx = new ServerBindingContext(version);
 
        // Start the device
        //
        Device.Start(ctx); 
 
        _serviceStatus = Resources.GetString(Resources.StringResources.ServiceStartedStatus);
        _actionStatus  = Resources.GetString(Resources.StringResources.ActionWaitPrintTicket);
    }
    catch (Exception ex)
    {
        _serviceStatus = Resources.GetString(Resources.StringResources.ServiceFailedStatus);
        Debug.Print(ex.ToString());
    }
 
    this.Dispatcher.BeginInvoke(new DispatcherOperationCallback(UpdateValues), null);
}

Come si vede la partenza del servizio da prima crea il binding, poi crea l’host del servizio a cui aggiunge l’implementazione concreta del nostro servizio e poi fa partire la parte DPWS. A questo punto il nostro servizio WCF su dispositivo .NET Micro FW è pronto per accettare richieste dall’esterno.

Sempre nello stesso modulo da notare anche l’utilizzo diretto della porta USB, mediante le librerie fornite dal produttore della scheda stessa.



USBHostController.DeviceConnectedEvent += Program.Instance.DeviceConnectedEvent; 
 
private void DeviceConnectedEvent(USBH_Device device)
{
    if (device.TYPE == USBH_DeviceType.Printer)
    {
        Debug.Print("Printer Connected");
 
        Printer = new USBH_Printer(device);
    }
}
 
 
public void InvokePrintTicket(byte[] buffer)
{
    if (Printer != null && Printer.GetStatus() == USBH_PrinterStatus.Selected)
    {
        Printer.SendData(buffer, 0, buffer.Length, 1000);
    }
}

Reservation Ticket Client (Xe.Sample2.ReservationTicketClient)

Questo modulo rappresenta il client, lato desktop, del servizio WCF che abbiamo appena implementato sul device. Per consumare il servizio WCF come sempre creiamo il nostro modulo proxy. Questo modulo è racchiuso nell’esempio Xe.Sample2.ReservationTicketProxy. In questo modulo importiamo il file ReservationTicketService.cs, generato in precedenza durante lo sviluppo del servizio WCF. In questo file vi è la definizione dell’interfaccia IReservationTicketService che sarà utilizzata come criterio di ricerca per eseguire il discover del servizio stesso.



private bool FindService()
{
    try
    {
        DiscoveryClient discoveryClient =
            new DiscoveryClient(new UdpDiscoveryEndpoint(DiscoveryVersion.WSDiscovery11));
 
        Collection<EndpointDiscoveryMetadata> services = discoveryClient.Find(new FindCriteria(typeof(IReservationTicketService))).Endpoints;
 
        discoveryClient.Close();
 
        if (services.Count == 0)
        {
            return false;
        }
        else
        {
            _serviceAddress = services[0].ListenUris[0];
        }
    }
    catch
    {
        return false;
    }
 
    return true;
}

Tramite questo codice la classe di proxy ricerca un servizio che espone il contratto definito nell’interfaccia IReservationTicketService senza conoscerne url. Una volta definito il proxy non ci resta che consumare il servizio sul device.



public void PrintReservationTicket(byte[] buffer)
{
    try
    {
        if (_proxy == null)
        { 
            if(FindService())
                _proxy = new ReservationTicketServiceClient(EndPointConfigurationName, 
                                                            _serviceAddress.AbsoluteUri);
        }
 
        _proxy.PrintReservationTicket(buffer);
    }
    catch (Exception ex)
    {                
        throw;
    }
}

Questi sono i devices che ho utilizzato per la realizzazione degli esempi.



Considerazioni Sample2

Osservando il codice del secondo esempio si può capire come si sia evoluta la parte di comunicazione tra i due FW. L’introduzione del supporto WCF tra i due mondi del .NET FW ha aperto scenari di integrazione molto accattivanti. Il fatto di poter eseguire la ricerca di un servizio senza conoscerne in anticipo l’indirizzo esatto permette una libertà totale sull’architettura definibile. Questo e molto altro ancora, introdotto con il .NET FW 4.1, mi ha spinto a rivedere in questa direzione il progetto in questione. Spero che, sia la spiegazione che il codice allegato, sia sufficiente a generare in chi legge curiosità verso il .NET MF.