• RSS
  • Add To My MSN
  • Add To Windows Live
  • Add To My Yahoo
  • Add To Google

μPLibrary : componente client per il servizio di Dynamic Dns 

Post di Paolo Patierno sabato 10 dicembre 2011 15:06:00
Valuta questo articolo 0 Voti

La libreria μPLibrary (arrivata alla versione 1.3.0.0) si è arricchita di un nuovo componente software che fornisce la funzionalità di client per il servizio di Dynamic Dns.

Infatti, sappiamo che esistono alcuni service provider (No-IP, DynDns, …) che forniscono il servizio di Dynamic Dns per tutti coloro che hanno un indirizzo IP dinamico ma che vogliono comunque raggiungere il proprio PC dall’esterno utilizzando un nome di dominio (es. pccasa.dyndns.org).

dyndns_thumbno-ip_thumb

Gli stessi service provider forniscono anche un  proprio applicativo che va installato sul proprio PC per garantire la sincronizzazione e corrispondenza tra il proprio indirizzo IP (che cambia continuamente) ed il nome host che abbiamo scelto (es. pccasa.dyndns.org). Con questa soluzione è necessario, però, avere il proprio PC sempre acceso. Esistono, comunque, molti router sul mercato che includono tale funzionalità.

Il componente che ho aggiunto alla mia libreria, invece permette di utilizzare la propria board con il .Net Micro Framework a fungere da client per il servizio di Dynamic Dns. Al suo interno è implementato tutto il protocollo necessario per il check del proprio indirizzo IP e la funzionalità di upload dello stesso presso il service provider. Attualmente, i service provider supportati sono No-IP e DynDns, ma è banale estendere il componente per altri service provider.

Nei prossimi giorni, posterò un articolo più esaustivo sul suo funzionamento e sul suo utilizzo, nel frattempo è già disponibile anche su Nuget !

WCF & .NET Micro FW – A real case 

Post di Mirco Vanini sabato 10 dicembre 2011 14:56:01
Valuta questo articolo 0 Voti

 

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.

Mixer USB con Netduino 

Post di Lorenzo Maiorfi giovedì 8 dicembre 2011 11:01:06
Valuta questo articolo 0 Voti

In questo post vogliamo trattare la realizzazione di una periferica per PC utilizzando la scheda Netduino corredata di uno ‘shield’, ovvero di una scheda che aggiunge componentistica opportuna che si va a collegare alle porte di I/O del microcontrollore o a qualcuna delle sue periferiche integrate. Netduino,  prodotto da Secret Labs (come già la linea FEZ prodotta da GHI), ha infatti il fattore di forma e la disposizione dei connettori predisposti ad ospitare uno shield perfettamente compatibili con le schede Arduino, caratteristica che consente immediatamente l’utilizzo di hardware che era stato già progettato e realizzato per la piattaforma di origine tutta italiana che è stata la prima in questo settore ad essere open-source e che ha goduto e gode di una certa fortuna e diffusione. Chi per Arduino ha pensato al sistema di espansione con gli ‘shield’ ha poi progettato il tutto perché queste schede fossero impilabili: in pratica i connettori presenti sulla scheda a microcontrollore sono in realtà socket femmina e ogni shield presenta i connettori con il lato da agganciare a detta scheda munito di pin maschi e, dall’altro lato, di socket femmina pronti ad accogliere un altro shield. Il sistema, insomma, è reso da un punto di vista hardware facilmente espandibile, salvo poi progettare opportunamente il corretto utilizzo delle risorse del microcontrollore esposte appunto dai connettori evitando concorrenze o sovrapposizioni non possibili.

Guardandoci un po’ intorno alla ricerca di uno shield interessante ci siamo imbattuti nel ‘Danger Shield’ prodotto da Sparkfun (il sito www.sparkfun.com è una meta quasi obbligata per chi si occupa di progettazione e prototipazione di dispositivi embedded): sicuramente una scheda non delle più compatte (con i suoi 8x10,5 cm supera di gran lunga la scheda Netduino che misura circa 5,5x7 cm) ma interessante perché, oltre a diversi sensori, ha a bordo un display led a sette segmenti pilotato da uno shift register, tre bottoni collegati ad altrettanti I/O pin di Netduino e soprattutto tre potenziometri ‘slide’ ovvero i classici potenziometri lineari da mixer audio collegati ad altrettante porte di ingresso analogiche del microcontrollore. Da qui l’idea di progettare un dispositivo che renda ‘reali’ le funzionalità principali del mixer di Windows che, ovviamente è ‘virtuale’. Lo shield viene venduto in kit da assemblare, ovvero con il circuito stampato su scheda preforata e serigrafata e tutti i componenti (inclusi i famosi connettori per agganciare lo shield alla scheda a microcontrollore) da saldare. Nel video potrete seguire le fasi salienti del montaggio della scheda, operazione che riteniamo possibile per chiunque abbia un minimo di confidenza con un saldatore da elettronica, vista la tecnologia ‘through hole’ e le dimensioni generose dei singoli componenti. Una volta completata questa fase di assemblaggio ci siamo potuti dedicare alla realizzazione del software per il nostro dispositivo, in quanto l’hardware si esaurisce proprio con la scheda Netduino collegata al Danger shield, oltre all’immancabile adattatore da USB a seriale che consente di realizzare facilmente una periferica USB che istauri un dialogo con protocollo seriale tra il microcontrollore e il PC.

Il firmware realizzato con il .NET Micro Framework e caricato nel microcontrollore della scheda Netduino consta essenzialmente di un primo livello che realizza una sorta di driver per le funzionalità esposte dal Danger Shield e dal programma vero e proprio eseguito dal microcontrollore che si appoggia su detto driver. Il codice del firmware viene esplorato nel dettaglio nel video contenuto nel DVD allegato alla rivista; a noi qui bastano alcuni cenni che facciano intuire la grande produttività che questo ambiente consente agli sviluppatori per sistemi embedded: il driver per il Danger Shield, ad esempio viene facilmente realizzato partendo da un apposito template di progetto per Visual Studio che si chiama ‘Netduino Application’ che aggiunge in automatico i riferimenti alle librerie ‘SecretLabs.NETMF.Hardware’ e ‘SecretLabs.NETMF.Hardware.Netduino’ che troviamo nel nostro PC dopo aver installato il Netduino SDK. Queste librerie, assieme a quelle di base del .NET Micro Framework, ovvero ‘Microsoft.SPOT.Native’ e ‘Microsoft.SPOT.Hardware’, espongono le classi che ci consentono di istanziare oggetti quali ‘AnalogInput’, ‘InterruptPort’ o ‘OutputPort’ per gestire il comportamento di porte di ingresso e di uscita del microcontrollore. La routine principale del programma, poi, si appoggia anche su una classe ‘Helper’ per le funzionalità di lettura e scrittura su porta seriale e tutto il software fa uso degli eventi, ben noti agli sviluppatori C#: la classe che espone il comportamento della porta seriale, quando questa riceve un comando dal PC, solleva un evento in reazione al quale noi possiamo impostare determinati valori per le uscite del nostro microcontrollore; analogamente le variabili che abbiamo definito per implementare il comportamento delle porte di ingresso del microcontrollore sono in grado di sollevare eventi: abbiamo la notifica di un evento quando viene premuto uno dei tre bottoni presenti sulla scheda Netduino e di un altro evento quando uno dei tre potenziometri viene spostato. In risposta a questi eventi possiamo agire con la opportuna logica della nostra applicazione, ad esempio comunicando al PC sempre tramite porta seriale di alzare o abbassare il volume master del mixer di windows oppure di attivare o disattivare la funzione mute.

Pagina 49 di 49 << < 20 42 43 44 45 46 47 48 49