/**@file*/
/*
TelepatiUdp ver 1.5

Copyright (C) 2005 Gilberto Decaro
email: msgid@email.it

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#ifndef TelepatiUdp_h_DEFINED
#define TelepatiUdp_h_DEFINED

#include <OPENR/OObject.h>
#include <OPENR/OSubject.h>
#include <OPENR/OObserver.h>

/*Strutture delle connessioni UDP usate da Telepati*/
#include "UDPConnection.h" 
/*Header dello stack tcp/ip*/
#include <ant.h>  
/*Tipi di Endpoint*/
#include <EndpointTypes.h>  
/*package usati da Telepati i TelepatiPkges*/
#include <Packages.h>

//***
#include <fstream>

#include <vector> 
#include <queue>
#include "def.h"

/*Header della classe che si occupa della stampa delle statistiche di rete*/
#include "EtherStat.h"

/*Header fuzzy*/
#include <FlFVar.h>
#include <FlFRule.h> 
#include <FlFuzzyLib.h>
#include <FlString.h>
#include <FlFRuleBase.h>
#include <FlFSetTrapez.h>    

/**Caratteri di Escape ANSI di inizio. Attiva la colorazione ANSI del
   testo che lo segue. (32 = green)*/
#define TLPT_BEG_COLOR "\e[32m"
/**Caratteri di Escape ANSI di fine. Ristabilisce il colore di default
   dei caratteri che lo seguono.*/
#define TLPT_END_COLOR "\e[0m"
/**Nome dell'oggetto colorato utilizzando i colori ANSI. Utilizzato
   per la stampa dei messaggi sulla console Telnet per permettere un
   piu' facile riconoscimento dell'oggetto che ha stampato il
   messaggio.*/
#define TLPT_NAME TLPT_BEG_COLOR"TelepatiUdp"TLPT_END_COLOR

using namespace std; /**<uso il namespace standard*/

/**file di configurazione della rete di Aibo. E' una lista di
   indirizzi IP, uno per riga, che corrispondo agli idirizzi degli
   Aibo con cui si vuole poter comunicare*/
const char* const AIBONET="/MS/OPEN-R/MW/CONF/AIBONET.CFG";  

/** Oggetto per la comunicazione UDP via Rete Wireless tra Aibo.  Si
    occupa di gestire la comunicazione di agenti cooperanti attraverso
    il protocollo UDP e la rete Wireless. E' un oggetto OPENR pertanto
    e' un processo che lavora in concorrenza con gli altri oggetti
    OPENR presenti nell'Aibo e comunica con loro attraverso lo scambio
    di messaggi.@n All'avvio attiva una connessione UDP in ricezione e
    due connessioni UDP in spedizione: una ti tipo DATA per la
    spedizione di messaggi di dati e una di tipo CTRL per la
    spedizione di messaggi di controllo (HI, HIREPLY, BYE, ACK).@n I
    pacchetti scambiati sono rappresentati dai TelepatiPkg
    (implementati nella librearia libPackages.a e vengono spediti
    attraverso il protocollo UDP, per ogni pacchetto e' possibile
    specificare, all'atto della sua creazione, se e' necessario un
    Acknowledgment o meno come conferma della ricezione.*/
class TelepatiUdp : public OObject {
 public:
    
  /**Costruttore di Default*/
  TelepatiUdp();
  /**Distruttore di default*/
  virtual ~TelepatiUdp();
  
  OSubject*    subject[numOfSubject]; /**< Array dei subject dell'oggetto*/
  OObserver*   observer[numOfObserver]; /**< Array degli observer dell'oggetto*/
   
  /**Entry Point Standard: Inizializza l'oggetto e le strutture per la
     comunicazione inter-object e di Rete.*/
  virtual OStatus DoInit   (const OSystemEvent& event); 
  /**Entry Point Standard: Attiva la comunicazione con gli altri
     Oggetti e via Rete. */
  virtual OStatus DoStart  (const OSystemEvent& event); 
  /**Entry Point Standard: Ferma l'oggetto e la comunicazione*/
  virtual OStatus DoStop   (const OSystemEvent& event); 
  /**Entry Point Standard: Distrugge l'oggetto e le strutture
     inizializzate*/
  virtual OStatus DoDestroy(const OSystemEvent& event); 

  /**Extra Entry Point: spedisce il messaggio. Quando l'oggetto OPENR
     IPStack (che si occua della gestione dello stack TCP/IP) avra'
     copiato tutti i dati nei suoi buffer, e saranno quindi
     riutilizzabili i buffer delle connessioni di spedizione, verra'
     invocata questa funzione. @param msg puntatore al messaggio
     spedito.*/
  void SendCont(ANTENVMSG msg);  
  /** Extra Entry Point: riceve il messaggio. Al ricevimento di un
      messaggio dalla rete IPStack invoca questa funzione che ha il
      compito di gestire la ricezione, se e' un messaggio di dati
      questo viene inoltrato agli oggetti observer. @param msg
      puntatore al messaggio ricevuto.*/
  void ReceiveCont(ANTENVMSG msg);  
  /** Extra Entry Point: chiamato allo scadere del timer periodico di
      Ack. Controlla se e' stato ricevuto l'Ack per il messaggio
      spedito identificato dal suo numero seriale, se non ricevuto
      rispedisce il messaggio.*/
  void TimerEnd(void* msg);


  /** Extra Entry Point: chiamato allo scadere del timer periodico di
      Polling. Controlla lo stato di attivita' degli Aibo
      periodicamente; manda un HiPkg a tutti gli Aibo in rete e
      controlla che sia stata ricevuta risposta all'HiPkg mandato
      precedentemente, se non e' stata ricevuta risposta l'Aibo viene
      posto in stato INACTIVE. @see TELEPATI_BONE_TIME.*/
  void BoneTimerEnd(void* msg);  
  /**Extra Entry Point: chiamato allo scadere del timer periodico di
     Statistiche di Rete. E' attiva nel caso venga definita in
     compilazione OPENR_ETHERSTAT (gcc -DOPENR_ETHERSTAT) e stampa
     sulla console virtuale informazioni sullo stato della rete
     wireless. Viene invocata ogni 5 secondi.*/
  void EtherStat(void* msg);  
  
  /**Entry Point Standard: attua la comunicazione inter-object. Viene
     invocata al ricevimento di un messaggio da parte di un oggetto
     OPEN-R subject e si occupa di spedire il messaggio attraverso la
     rete.*/
  void SendNotify(const ONotifyEvent& event); 
  /** Funzione di Assert Ready. Viene invocata al ricevimento da parte
      dell'observer dell'Assert Ready o del Deassert Ready*/
  void ObserverReady(const OReadyEvent& event); 
  
  /** Porta UDP di ricezione.*/
  static const Port TELEPATI_PORT= 3030;  
  /** Dimensione dei buffer di ricezione/spedizione in byte.*/
  static const unsigned int TELEPATI_BUFFER_SIZE= 65535; 
  /** Numero massimo di messaggi consecutivi all'observer. Rappresenta
      il numero massimo di messaggi che possono essere mandati
      all'observer senza prima ricevere un Asser Ready.*/
  static const int TELEPATI_MAX_MESSAGE_TO_OBSERVER = 20; 
  /** Durata, in msec del timer periodico di Ack. @see TimerEnd.*/
  static const longword TELEPATI_ACK_TIME= 200;
  /** Durata, in msec, del timer ciclico per il rilevamento dello
      stato di attivita' degli Aibo (vedi documentazione). @see
      BoneTimerEnd.*/ 
  static const longword TELEPATI_BONE_TIME= 8; 
  /** Numero massimo di tentativi di rispedizione di un
      messaggio. Numero massimo di volte che un messaggio richiedende
      un Ack viene rispedito al destinatario in caso di mancato
      ricevimento dell'Ack stesso, superato questo numero l'Aibo di
      destinazione viene posto in stato INACTIVE e il messaggio viene
      scartato. @see TimerEnd*/
  static const int TELEPATI_MAX_RESENDING = 20;

  /** Tipi di connessioni di spedizione.*/
  enum {
    /** Connessione Data. Usata per la spedizione dei package di
	dati.*/
    DATA_CONNECTION=0, 
    /** Connessione di Controllo. Usata per la spedizione dei package
	di controllo (ACK, HI, HIREPLY, BYE) */
    CTRL_CONNECTION=1  
  }; 

 private:

  /*--- Variabili per le statistiche di comunicazione ---*/
  unsigned int sendMessage;          /*numero di messaggi spediti*/	    
  unsigned int sendByte;             /*byte spediti*/
  unsigned int totalRecvMessage;     /*numero di messaggi totali ricevuti*/ 
  unsigned int dataRecvMessage;      /*numero di messaggi di dati ricevuti*/
  unsigned int dataRecvByte;         /*byte di dati ricevuti*/
  unsigned int numOfResending;       /*numero di rispedizioni di package*/
  unsigned int numOfAckSended;       /*numero di Ack spediti*/
  int sendSpeed;                     /* velcita' di spedizione in byte/s*/
  /* memorizza il momento in cui e' iniziata la spedizione del pkg
    sulla connesione DATA*/
  SystemTime* startSendTime;       
  

  /*Enumeratori Fuzzy*/
  enum{PICCOLA, PICCOLO, MEDIA, MEDIO, GRANDE, MOLTO_GRANDE}; 
  /* Variabile Fuzzy di input, dimensione del pacchetti (PICCOLA,
     MEDIO, GRANDE, MOLTO_GRANDE)*/
  FlFVar* dimension; 
  /* Variabile Fuzzy di output, intervallo di tempo da attendere prima
     di rispedire il pacchetto (PICCOLO, MEDIO, GRANDE,
     MOLTO_GRANDE)*/
  FlFVar* interval; 
  /* Modello Fuzzy del timerend. Il modello, basato sulle due
     variabili dimension e interval, viene usato per calcolare il
     tempo di attesa che intercorre tra la spedizione di un messaggio
     che richiede un Ack e la sua rispedizione nel caso non venga
     ricevuto l'Ack.*/
  FlFRuleBase* model;

  /* Riferimento allo stack IP (richiesto da OPENR)*/
  antStackRef ipstackRef;       
  /* Connessione di ricezione*/
  UDPRecvConnection recvConnection; 
  /* Array di connessioni di spedizione (CTRL e DATA connection). @see
     enum*/
  UDPSendConnection* sendConnection;

  /* Vettore di indirizzi IP degli AIBO*/
  vector<IPAddress> aiboIp;  
  /* Stato di attivita' degli aibo presenti. Array di aiboState, in
     link con aiboIp.*/
  aiboState* aiboIpActive;          
  /* Numero di aibo presenti nel campo meno 1 (escluso colui su cui
     gira Telepati)*/
  int numaibo;    

  /* Array dei seriali deli Ack attesi da ogni Aibo.*/
  unsigned short* expectedSerial;                    
  /* Numero di rispedizioni dello stesso messaggio da parte di
     TimerEnd*/
  int resending;
  /* Coda di TelepatiPkg. Memorizza i messaggi in attesa di essere
     spediti attraverso la connessione DATA*/
  queue<TelepatiPkg*> sendQueue;  
  /* Coda dei messaggi di controllo in attesa di essere spediti dalla
     connessione CTRL..*/
  queue<TelepatiPkg*> sendCtrlQueue; 
  /* Coda dei messaggi in attesa di essere spediti all'observer.*/
  queue<TelepatiPkg*> interObjectQueue;
  /* Puntatore che memorizza il messaggio in attesa di un Ack. */
  TelepatiPkg* ackWaitingMsg;        
  
  /* Numero di volte che dev'essere invocata la funzione TimerEnd
     prima di rispedire il TelepatiPkg in ackWaitingMsg.*/
  int numOfTimerEnd;
  /* Contatore del numero di volte che viene invocata la funzione
     TimeEnd, il timer ciclico di rilevamento dell'Ack. A causa di una
     gestine non efficiente dei timer in OPENR non e' possibile far
     partire un nuovo timer per ogni messaggio di conseguenza e'
     neessario questo contatore per poter eseguire cicli di attesa di
     lunghezza temporale variabile in funzione della dimensione del
     pacchetto.*/
  int timerEndCont;
  
  /* Buffer per il parsing dei file*/
  static const size_t LINEBUFFERSIZE= 256;   
  
  /* Parser del file di configurazione. Imposta numaibo, aiboIp,
     aiboIpActive e expectedSerial. Fa il parsing del file AIBONET.*/
  OStatus parseIpConfigFile();   
  /* Inizializza le connessioni di ricezione e di spedizione.*/
  OStatus initConnections();     

  /*Spedisce il messaggio attraverso la connessione Ctrl. Imposta
    IPStack mandandogli i dati da spedire, non appena il buffer di
    spedizione di IPStack sara' riempito verra' invocata
    SendCont(). In caso la connessione sia in spedizione accoda il
    messaggio nella coda sendCtrlQueu. @param msg messaggio da
    spedire*/
  void sendViaCtrl(TelepatiPkg* msg); 
  /*Spedisce il messaggio attraverso la connessione Data. Imposta
    IPStack mandandogli i dati da spedire, non appena il buffer di
    spedizione di IPStack sara' riempito verra' invocata
    SendCont(). In caso la connessione sia in spedizione accoda il
    messaggio nella coda sendQueu. @param msg messaggio da spedire*/
  void sendViaData(TelepatiPkg* msg); 
  /* Controlla la presenza di messaggi di dati in attesa di essere
     spediti e, se presenti, li spedisce attraverso ls connessione
     DATA.*/
  void checkQueue();             
  /* Controlla la presenza di messaggi di controllo in attesa di
     essere spediti e, se presenti, li spedisce attraverso la
     connessione CTRL.*/
  void checkCtrlQueue();  
  /* Avvia la ricezione. Imposta IPStack in modo da restare in ascolto
     sulla rete, non appena verra' ricevuto un pacchetto verra'
     invocata la funzione ReceiveCont().*/
  void receive();               
  
  /* Manda il package agli observer se pronti a riceverlo, altrimenti
     lo acconda. @param pkg package da mandare.*/
  void sendToObserver(TelepatiPkg* pkg);
  /* Manda un HiMsg (msg di saluto) all'i-esimo Aibo. @param i aibo a
     cui mandare l'hy message.*/
  inline void sendHiMsg(int i){
    OSYSDEBUG(("Telepati::sendHiMsg(%d)\n",i));
    sendViaCtrl(new HiPkg(i));
#ifdef TELEPATI_POLLING
    aiboIpActive[i]= (aiboIpActive[i] == ACTIVE ? WAITINGREPLY : aiboIpActive[i]);
#endif
  };

  /* Manda un HiReplyMsg (msg di risposta al saluto) all'i-esimo
     Aibo. @param i aibo a cui mandare l'HiReply message*/
  inline void sendHiReplyMsg(int i){
    OSYSDEBUG(("Telepati::sendHiReplyMsg(%d)\n",i));
    sendViaCtrl(new HiReplyPkg(i));
  };
  /* Manda un ByeMsg (msg di arrivederci) all'i-esimo Aibo,
     l'esecuzione di Telepati rimane bloccata fino a completa
     spedizione del package. @param i aibo a cui mandare il byte
     message*/
  void sendByeMsg(int i);
  /* Manda un CounterPkg agli observer di Telepati*/
  void sendCounterMsg();
  /* Manda un AiboInfoMsg agli observer di Telepati*/
  void sendAiboInfoMsg();  

  /* Converte l'IP passato come parametro nell'indice da usare
     nell'array aiboIp. @param ip indirizzo IP dell'Aibo di cui si
     vuole conoscere l'indice*/
  int aiboFromIp(const IPAddress& ip) const; 
         
  /* Recupera l'indirizzo IP dell'Aibo stesso*/
  IPAddress* retrieveMyIp();     
  /* Imposta l'array aiboIpActive degli stati degli Aibo per i-esimo
     Aibo in base al valore passato e se cambia lo stato ne informa
     l'observer. @param aibo aibo di cui modificare lo stato di
     attivita'. @param newVal nuovo stato di attivitą. @return true:
     se ha cambiato stato; false: altrimenti.*/
  bool setAiboIpActive(int aibo, aiboState newVal); 
  /*Cancella il messaggio in attesa di Ack puntato da
    ackWaitingMessage. @param serial seriale del messaggio da
    cancellare. @param aibo aibo di destinazione */
  void eraseFromAckWaitingMsg(unsigned short serial, int aibo);  
  /* Calcola la velocita' di spedizione del package. Se richiede un
     Ack il tempo di spedizione e' calcolato dal momento in cui il
     package viene spedito al momento in cui viene ricevuto il
     relativo Ack.*/
  void computeSpeed();
  /* Effettua alcune impostazioni iniziali per il calcolo della
     velocita'. Fa partire il conteggio del tempo di spedizione e
     salva la dimensione dei dati spediti.*/
  void preComputeSpeed(size_t dataDimension);
  /* Imposta alcuni valori necessari al timer periodico di
     rispedizione del messaggio. In base alla dimensione del pacchetto
     spedito calcola il valore fuzzy del tempo di attesa da attendere
     prima di rispedirlo nel caso non venga ricevuto un Ack.*/
  void setTimerEndValue(size_t dataDimension);
  /* Inizializza il sistema fuzzy per il calcolo della durata del
     tempo di attesa del timer per la rispedizione del pacchetto in
     caso non venga ricevuto l'Ack*/
  void initFuzzySystem();

  //***
  ofstream* debugFile;
  
};
#endif  //TelepatiUdp_h_DEFINED
