= Il modulo event e la gestione degli eventi =
La generazione di eventi da parte di un modulo (A) di Netsukuku e l'ascolto di tali eventi e la loro gestione da parte di un altro modulo (B) sono uno dei meccanismi di comunicazione tra due moduli distinti.

Le funzioni contenute in un modulo, servono di norma a gestire un particolare aspetto del programma. Di norma sono eseguite da un microthread a cui questo aspetto viene demandato. Quando durante questa esecuzione si vogliono inviare messaggi, richiedere servizi ad altri moduli, o comunque segnalare il completamento di particolari azioni, si emettono dei segnali, o eventi.

''Nota'': nel corso di questa trattazione i termini "''emettere segnali''" e "''generare eventi''" sono equivalenti.

I moduli che sono interessati a questi eventi lo hanno già dichiarato '''registrando''' come ''listeners'' di questi eventi alcune loro funzioni.
<<BR>>
In particolare se queste funzioni sono definite come ''microfunc'', come vedremo meglio in seguito, si ottiene che la gestione degli eventi da parte di questi moduli avvenga in distinti microthread, o in un microthread ''dispatcher'' che li gestisce uno alla volta, e così via.

== Il modulo A dichiara di generare certi eventi ==
Di norma gli aspetti di un modulo sono gestiti da una istanza della classe principale definita nel moduolo stesso.
<<BR>>
Nel suo costruttore, questa classe dice quali eventi può generare con una sintassi di questo tipo:
{{{
class A:
    def __init__(self):
        self.events = Event( ['EVENT1', 'EVENT2', 'FOOEVENT'] )
        ...
        self.events.add( ['EVENT3', 'EVENT4'] )
}}}

== Il modulo B dichiara di voler gestire certi eventi generati dal modulo A ==
Anche qui tale dichiarazione avviene nel costruttore della classe principale del modulo, la cui istanza gestisce gli aspetti del programma demandati al modulo.
<<BR>>
Nel costruttore dunque, il modulo B, che deve avere un riferimento alla istanza di A, dichiara di voler gestire i suoi eventi con una sintassi di questo tipo:
{{{
class B:
    def __init__(self, a)
        # a is an instance of the class A
        a.events.listen('EVENT1', self.func_for_ev1)
}}}

B dice che quando A genera eventi di tipo '{{{EVENT1}}}' deve essere eseguito il suo metodo {{{func_for_ev1}}}.
<<BR>>
Il metodo {{{func_for_ev1}}} deve accettare i parametri che sono indicati dal modulo A quando genera eventi di tipo '{{{EVENT1}}}'.
{{{
class B:
    ...

    def func_for_ev1(param1, param2, param3):
        ...
}}}

Se la funzione registrata come ''listener'' è una normale funzione, questa viene direttamente richiamata nel momento in cui A genera l'evento. Questo significa che l'algoritmo svolto da A viene bloccato. Se invece si vuole che la gestione degli eventi da parte del modulo B sia svolta in un diverso microthread per non bloccare l'algoritmo di A, basterà definire la funzione come ''microfunc'' (vedere il [[../ModuloMicro|modulo micro]])
{{{
class B:
    ...

    @microfunc()
    def func_for_ev1(param1, param2, param3):
        ...
}}}

== Il modulo A genera un evento ==
Durante l'esecuzione del programma Netsukuku, il modulo A vuole segnalare al modulo B (e a chi altri fosse interessato) che è avvenuto qualcosa, cioè vuole generare un evento di tipo '{{{EVENT1}}}'.
{{{
class A:
    ...
    def f(self):
        self.events.send('EVENT1', (1, 2, 'terzo'))
}}}

''Nota'': fare attenzione che i parametri sono passati come una tupla. Ricordare che per dichiarare una tupla con un solo elemento va messa una virgola prima di chiudere la parentesi. Ad esempio:
{{{
self.events.send('EVENT1', (1, ))
}}}

== La classe Event ==
La classe {{{Event}}} definita nel modulo '''event''' realizza il meccanismo di dichiarazione/registrazione/generazione di eventi descritto sopra.
<<BR>>
Infatti nel suo costruttore il modulo A crea una istanza di {{{Event}}} e la memorizza, per convenzione, nel suo membro {{{self.events}}}. Il modulo B usa il metodo {{{listen}}} della istanza di {{{Event}}} memorizzata da A in {{{a.events}}} per registrare le sue funzioni ''listeners''. Il modulo A usa il metodo {{{send}}} della istanza di {{{Event}}} memorizzata in {{{self.events}}} per generare gli eventi, e questo fa si che le funzioni ''listeners'' siano richiamate.

== Il decoratore wakeup_on_event ==
Nel modulo event viene definito anche un decoratore di funzioni chiamato '''wakeup_on_event'''.

Con questo decoratore si indica che una funzione può mettersi in attesa, durante la sua esecuzione, di un certo evento.
<<BR>>
Al momento della definizione della funzione occorre indicare di quali eventi si può mettere in attesa la funzione nel suo interno. Ogni evento è definito dalla tupla {{{(EventInstance, EventName)}}} dove {{{EventInstance}}} è una istanza della classe {{{Event}}} (ad esempio {{{neigh.events}}}) e {{{EventName}}} è il nome dell'evento (ad esempio 'NEIGH_NEW')
<<BR>>
Inoltre occorre che la funzione definisca l'argomento con keyword {{{event_wait}}} con un valore default arbitrario.
{{{
    @wakeup_on_event(events=[(wheater_events, 'IT_IS_SUNNY'), (self.neigh.events, 'NEIGH_NEW')])
    def go_out(param1, param2, event_wait=None):
        ...

        msg = event_wait[(wheater_events, 'IT_IS_SUNNY')]() # Block and wait the event 'IT_IS_SUNNY'
        ...

        msg2 = event_wait[(self.neigh.events, 'NEIGH_NEW')]() # Block and wait the event 'NEIGH_NEW'
        ...
}}}

La funzione al suo interno si ritrova valorizzato l'argomento {{{event_wait}}} con un dizionario che associa la tupla {{{(EventInstance, EventName)}}} con una funzione.
<<BR>>
Richiamando questa funzione con la sintassi
{{{
msg = event_wait[(wheater_events, 'IT_IS_SUNNY')]()
}}}
si ottiene che il microthread si blocca (lasciando lo schedulatore agli altri) in attesa dell'evento. Quando l'evento viene generato la funzione restituisce i dati associati all'evento. Quindi avremo nella variabile {{{msg}}} la tupla {{{(param1, param2, param3)}}}.