Utilizzare Mayhem con .NET Gadgeteer (Italiano)

By Mike Dodaro, translated by Marco Minerva from the original English version

Mayhem è un’applicazione open source che supporta trigger, eventi e azioni. Il cuore del sistema Mayhem è in esecuzione su un PC, ma gli eventi e le azioni che utilizza possono essere usati praticamente con ogni piattaforma e device. Gli sviluppatori di Mayhem dicono che anche chi non è un programmatore lo può utilizzare per automatizzare qualsiasi cosa! Questo sembra proprio un invito a provare Mayhem con .NET Gadgeteer.

Ecco uno screenshot di Mayhem in esecuzione all’interno di Visual Studio con una  (fare clic sull’immagine per ingrandire).

Mayhem running in Visual Studio Debugger

Mayhem in esecuzione nel Debugger di Visual Studio

Creare eventi ed azioni in esecuzione con Mayhem non è difficile dopo aver dato uno sguardo all’API Reference, che spiega quali sono i metodi di cui effettuare l’override.  L’esempio implementato in questo articolo richiede una classe statica per utilizzare una singola istanza dello XBeeClient di Paul Mineau. XBeeClient è un driver per XBee radios. Esso fornisce un’interfaccia seriale che serve due eventi. Mayhem persiste le impostazioni di stato quando gli eventi sono disattivati oppure Mayhem viene chiuso.

Come estendere le classi  EventBase e ReactionBase di Mayhem

Il sito di Mayhem su CodePlex descrive un semplice esempio di Hello-World che mostra una finestra di messaggio in risposta ad un evento selezionato nella UI. Il modello usa gli attributi dall’assembly System.Runtime.Serialization come mostrato nel codice seguente.  L’implementazione di ReactionBase mostra l’override del metodo Perform.

  [DataContract]
  [MayhemModule("Popup Window", "Creates a popup window")]
  public class Mayhem2 : ReactionBase
    {
        public override void Perform()
        {
            MessageBox.Show("Hello Mayhem World");
        }
    }

Le librerie necessarie per eseguire Mayhem sono gestite tramite NuGetNuget è un add-on open source per Visual Studio. Seguiamo le istruzioni per installare i package in Visual Studio e aggiungiamo le reference richieste.  Quindi, eseguiamo Mayhem nel debugger di Visual Studio per testare Mayhem.

Mayhem running in Visual Studio Debugger

Mayhem in esecuzione del Debugger di Visual Studio

Impostiamo gli eventi e le azioni nell’interfaccia di Mayhem. Il seguente screenshot mostra la finestra di popup creata dal codice ReactionBase che abbiamo visto prima.

Timer Event and Mayhem Pop Up Window

Timer Event and Mayhem Pop Up Window

Automazione di un Device .NET Gadgeteer con Mayhem

I device embedded spesso non dispongono di un sistema operativo, e comunicano con il PC in vari modi. Le classi EventBase e ReactionBase di Mayhem possono automatizzare i device .NET Gadgeteer come nell’esempio che stiamo costruendo, che utilizza una comunicazione radio XBee da e verso il device. Il sistema utilizza i moduli GHI Electronics Light Sensor , Seeed Relay e .NET Gadgeteer XBee adapter per la comunicazione XBee.

Gadgeteer Mayhem Light Sensor and Relays

Light Sensor, Relays e XBee adapter

Il modulo Relay in questo sistema può controllare un circuito elettrico che utilizza fino a 15 Amps/120 Volts oppure 7 Amps/250 Volts. Il sensore di luce è un modulo analogico. L’automazione tramite Mayhem comprende sia eventi che vengono originati dal device, sia eventi che si originano dal PC. Paul Mineau ha scritto il  driver open source XBeeClient , disponibile su CodePlex, per gestire XBee.

Il codice imposta il device così che Mayhem possa automatizzare i suoi moduli.  XBeeClient permette la comunicazione con il PC che ospita Mayhem. Esso gestisce un timer che legge il valore del Light Sensor ogni 10 secondi ed invia una notifica quando riconosce un valore superiore al 92%. In risposta agli eventi di automazione di Mayhem, esso fa scattare il Relay.

Il codice sorgente del device NET Gadgeteer è mostrato di seguito.  L’evento timer_Tick gestisce le notifiche che il sensore di luce invia quando riconosce che la luce ha superato la soglia impostata. E’ possibile impostare la percentuale sul valore desiderato. L’evento xBeeClient_StringReceived fa scattare il relay, che è l’azione scatenata sul device quando esso riceve la notifica da Mayhem.

using Microsoft.SPOT;

using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;
using Gadgeteer.Modules.Seeed;
using GTM.Community;

namespace MakeMayhemGadgeteer
{
    public partial class Program
    {
        GT.Timer timer;

  void ProgramStarted()
        {
            timer = new GT.Timer(10000);
            timer.Tick += new GT.Timer.TickEventHandler(timer_Tick);

            xBeeClient.StringReceived +=
                new GTM.Community.XBee.StringReceivedEventHandler(xBeeClient_StringReceived);

            multicolorLed.TurnGreen();
            button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);
            Debug.Print("Program Started");
        }

        void timer_Tick(GT.Timer timer)
        {
            double percent = lightsensor.ReadLightSensorPercentage();
            if (percent > 92.00)
            {
                xBeeClient.UploadStringAsync("Light sensor high");
                multicolorLed.BlinkOnce(GT.Color.Red);
            }
        }

        void xBeeClient_StringReceived(object sender, GTM.Community.XBee.StringReceivedEventArgs e)
        {
            multicolorLed.BlinkOnce(GT.Color.Red);

            if (e.Message.IndexOf("Start") != -1)
                relays.Relay1 = true;
            else
                relays.Relay1 = false;
        }

        void button_ButtonPressed(Button sender, Button.ButtonState state)
        {
            if (!button.IsLedOn)
            {
                timer.Start();
                button.TurnLEDOn();
            }
            else
            {
                timer.Stop();
                button.TurnLEDOff();
            }
        }
    }
}

Ora diamo uno sguardo al client XBee in esecuzione sul PC.  Esso riceve gli eventi generati dal device .NET Gadgeteer e gli invia notifiche in risposta

Implementazione delle classi EventBase e ReactionBase

Prima di usare le classi EventBase e ReactionBase, abbiamo bisogno di una classe PortStatus per gestire la connessione XBee radio che si occupa delle notifiche.  Una notifica arriva al PC dal sensore di luce, l’altra è inviata dal PC al device per far scattare il relay. Entrambe le notifiche utilizzano XBee sulla porta COM 9, quindi la classe PortStatus contiene una proprietà statica booleana Status che indica se la connessione radio e la porta 9 sono in uso. Sia EventBase sia ReactionBase nel codice di Mayhem leggono tale proprietà nel metodo OnBeforeLoad per impostare le rispettive variabili clientStatus.  Nel costruttore di PortStatus viene istanziata una classe di tipo XBeeClient, così sia EventBase sia ReactionBase possono accedervi.

public static class PortStatus
{
    public static XBeeClient xbeeClient;
    private static bool status;
    public static bool Status
    {
        get
        {
            return status;
        }
        set
        {
            status = value;
        }

    }

    static PortStatus()
    {
        try
        {
            if (!status)
            {
                xbeeClient = new XBeeClient(9);
                status = true;
            }
        }
        catch (Exception ex)
        {
            Logger.WriteLine("Port in use. Delete XBee client and reinititialize. Message: " + ex.Message);

        }

        Logger.WriteLine("Port in use. Delete XBee client and reinititialize.");
    }
}

Il codice ReactionBase è attivato da un evento assegnato nell’interfaccia di Mayhem.  Questo, a sua volta, nel metodo Perform invia una notifica al device per far scattare il relay, così come nell’esempio precedente mostravamo una finestra popup. Ricordiamo che la classe è decorata con gli attributi [DataContract] e [MayhemModule] dell’assembly System.Runtime.Serialization.

Mayhem create i moduli in due modi. Il primo consiste nella creazione di una nuova istanza, che richiama i seguenti metodi, nell’ordine indicato: OnBeforeLoad, OnLoadDefaults, OnAfterLoad. Nell’altro caso, un modulo viene serializzato quando viene spento oppure Mayhem è chiuso.  Mayhem salva lo stato dell’oggetto e lo ricrea quando necessario. In tal caso, sono richiamati i seguenti metodi: OnBeforeLoad, OnLoadFromSaved, OnAfterLoad.  Essi non richiedono un’implementazione in tutte le applicazioni.  Il nostro esempio deve mantenere l’oggetto XBeeClient sulla porta COM 9, quindi sia OnBeforeLoad sia OnLoadFromSaved impostano il valore della proprietà PortStatus.Status.  Questo ci assicura che l’istanza XBeeClient sia disponibile, ma non ci sono tentativi di istanziarla più di una volta.

[DataContract]
[MayhemModule("XBee Radio Relay", "XBee transmission to relay")]
public class XBeeRelay : ReactionBase
{
    private bool clientStatus;
    protected bool relayOn;

    protected override void OnBeforeLoad()
    {
        clientStatus = PortStatus.Status;
        relayOn = false;
    }

    protected override void OnLoadFromSaved()
    {
        clientStatus = PortStatus.Status;
    }

    protected override void OnDeleted()
    { }

    public override void Perform()
    {
        if (!relayOn)
        {
          PortStatus.xbeeClient.UploadStringAsync("Start Relay 1");
            relayOn = true;
        }
        else
        {
            PortStatus.xbeeClient.UploadStringAsync("Stop Relay 1");
            relayOn = false;
        }
    }

    protected override void OnEnabling(EnablingEventArgs e)
    {
        clientStatus = PortStatus.Status;
    }

    protected override void OnDisabled(DisabledEventArgs e)
    { }
}

L’implementazione di EventBase è simile a ReactionBase.  Poiché questa classe riceve le notifiche inviate dal device, deve registrarsi sull’evento XBee.StringReceivedEventHandler, cosa che viene fatta nel metodo OnAfterLoad.  Questo assicura che l’oggetto XBeeClient esista prima di assegnare l’handler.

[DataContract]
[MayhemModule("LightSensorHigh", "Event when light sensor is high")]
public class LightSensorEvent : EventBase
{
    private bool clientStatus;

    protected override void OnBeforeLoad()
    {
        clientStatus = PortStatus.Status;
    }

    protected override void OnLoadFromSaved()
    {
        clientStatus = PortStatus.Status;
    }

    protected override void OnAfterLoad()
    {
        if (PortStatus.Status == true)
        {
            PortStatus.xbeeClient.StringReceived +=
                new Gadgeteer.Modules.Community.XBee.StringReceivedEventHandler(xbeeClient_StringReceived);
        }

    }

    protected override void OnDeleted()
    { }

    void xbeeClient_StringReceived(object sender, Gadgeteer.Modules.Community.XBee.StringReceivedEventArgs e)
    {
        if(e.Message.IndexOf("Light sensor high") != -1)
            Trigger();
    }

    protected override void OnEnabling(EnablingEventArgs e)
    {
        clientStatus = PortStatus.Status;
    }

    protected override void OnDisabled(DisabledEventArgs e)
    { }

}

Il codice completo di entrambi i moduli Mayhem

Abbiamo discusso tutti gli elementi dell’implementazione del sistema basato su Mayhem/ .NET Gadgeteer. Si tratta di un esempio molto semplice, ma si può applicare a diversi contesti. La vision di Mayhem è di connettere ogni device con qualunque altro. Il team sta cercando aiuti per lo sviluppo e sponsorizza un contest: ci sono in palio $3,000 dollari per stimolare l’utilizzo della loro piattaforma.

using System;
using MayhemCore;
using System.Runtime.Serialization;
using System.Windows.Forms;
using Gadgeteer.Modules.Community;

namespace Mayhem2
{
    [DataContract]
    [MayhemModule("XBee Radio Relay", "XBee transmission to relay")]
    public class XBeeRelay : ReactionBase
    {
        private bool clientStatus;
        protected bool relayOn;

        protected override void OnBeforeLoad()
        {
            clientStatus = PortStatus.Status;
            relayOn = false;
        }

        protected override void OnLoadFromSaved()
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnDeleted()
        { }

        public override void Perform()
        {
            if (!relayOn)
            {
              PortStatus.xbeeClient.UploadStringAsync("Start Relay 1");
                relayOn = true;
            }
            else
            {
                PortStatus.xbeeClient.UploadStringAsync("Stop Relay 1");
                relayOn = false;
            }
        }

        protected override void OnEnabling(EnablingEventArgs e)
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnDisabled(DisabledEventArgs e)
        { }
    }

    [DataContract]
    [MayhemModule("LightSensorHigh", "Event when light sensor is high")]
    public class LightSensorEvent : EventBase
    {
        private bool clientStatus;

        protected override void OnBeforeLoad()
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnLoadFromSaved()
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnAfterLoad()
        {
            if (PortStatus.Status == true)
            {
                PortStatus.xbeeClient.StringReceived +=
                    new Gadgeteer.Modules.Community.XBee.StringReceivedEventHandler(xbeeClient_StringReceived);
            }

        }

        protected override void OnDeleted()
        { }

        void xbeeClient_StringReceived(object sender, Gadgeteer.Modules.Community.XBee.StringReceivedEventArgs e)
        {
            if(e.Message.IndexOf("Light sensor high") != -1)
                Trigger();
        }

        protected override void OnEnabling(EnablingEventArgs e)
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnDisabled(DisabledEventArgs e)
        { }

    }

    public static class PortStatus
    {
        public static XBeeClient xbeeClient;
        private static bool status;
        public static bool Status
        {
            get
            {
                return status;
            }
            set
            {
                status = value;
            }

        }

        static PortStatus()
        {
            try
            {
                if (!status)
                {
                    xbeeClient = new XBeeClient(9);
                    status = true;
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLine("Port in use. Delete XBee client and reinititialize. Message: " + ex.Message);

            }

            Logger.WriteLine("Port in use. Delete XBee client and reinititialize.");
        }
    }

    [DataContract]
    [MayhemModule("Popup Window", "Creates a popup window")]
    public class Mayhem2 : ReactionBase
    {
        public override void Perform()
        {
            MessageBox.Show("Hello Mayhem World");
        }
    }
}

, , , , , , ,

  1. Leave a comment

Leave a comment