// =============================================================================
// Testprogramm für serielle Schnittstellen (eigene Treiber)
// =============================================================================
//

#include "TestSerial.h"

char *StartMsg =
{
    "%@TestSerial "
    TmpVerMsg
};

LoopCheck     lc;
// Eine statische Instanz der Klasse LoopCheck
// Darüber wird das Zeitverhalten gesteuert (Software-Timer) und geprüft

#ifdef DebugTerminal
Monitor       mon(modeEcho | modeNl,0,&lc);
// Eine statische Instanz (mit Konstruktordaten) der Klasse Monitor
// Darüber wird mit (direkten) Terminals (z.B. VT100) kommuniziert
// Unter Linux werden hier GtkTerm (siehe Internet) und
// ArduinoMonTerm (eigene Entwicklung mit grafischen Wertanzeigen) eingesetzt.
// Das in den IDEs integrierte Terminal ist dafür meistens nicht geeignet,
// weil damit keine direkte Kommunikation (getipptes Zeichen sofort gesendet)
// möglich ist.
// ----- Parameter ------------------------------------------------------------
// <mode Echo>  Alle eintreffenden Zeichen werden sofort zurückgesendet
// <mode NL>    Vor der Ausgabe des Prompt (M>) erfolgt CR/LF
// <0>          Für Speicherzugriffe wird von 32 Bit ARM ausgegangen
// <&lc>        Für Zeitüberwachungen und entsprechende statistische Daten
//              greift die Monitor-Instanz auf die LoopCheck-Instanz zu

char *infoThis =
{
    "DHE TestSerial Version 1.0.0\r\n"
    "c0  Abschalten der periodischen Meldung\r\n"
    "c1  Prüfen der Speicheradressen\r\n"
    "c2  Testen der Ringpuffer und seriellen Schnittstellen\r\n"
    //"c3  Steuern/Analysieren der Messwerterfassung\r\n"
    //"c4  Testen von Peripheriezugriffen\r\n"
    //"c5  Temporäre lokale Tests\r\n"
};

#endif

#ifdef DebugTerminal
#define smCycleTime 5
void smInit();  // Vorwärtsreferenz auf die weiter unten definierte Funktion
StateMachine  sm(smInit, NULL, smCycleTime);
// Eine statische Instanz für die Zustandsmaschine, die hier für allgemeine
// Steuerungen, Überwachungen und zum Debugging verwendet wird
// ----- Parameter ------------------------------------------------------------
// <smInit>       Der zuerst aufgerufene Zustand (Funktion). Weitere Zustände
//                werden in den weiteren Zustandsfunktionen eingesetzt.
// <NULL>         Hier kann eine weitere Zustandsfunktion angegeben werden,
//                die dann grundsätzlich vor dem Verzweigen in einen Zustand
//                aufgerufen wird.
// <smCycleTime>  Die Zukluszeit (Takt) der Zustandsmaschine in Millisekunden
#endif


SerParams     tty1Params, tty2Params;
// ----------------------------------------------------------------------------
nRF52840SerE  tty1,tty2;
// ----------------------------------------------------------------------------
// Eine statische Instanz der Klasse nRF52840SerE (UARTE)
// Darüber werden die seriellen Schnittstellen (UARTE0 und UARTE1) des
// nRF52840 bedient.
// Die Parameter werden in einer Struktur <SerParams> über die Funktion
// <begin(...)> gesetzt.

#define       sndBufSize  256
byte          sndBuffer1[sndBufSize];
byte          sndBuffer2[sndBufSize];
#define       recBufSize  256
byte          recBuffer1[recBufSize];
byte          recBuffer2[recBufSize];

// ----------------------------------------------------------------------------
ComRingBuf    crb1,crb2;
// ----------------------------------------------------------------------------
// Eine statische Instanz der Klasse <ComRingBuf>
// Damit wird ein Ringpuffer aufgebaut, der eine serielle Schnittstelle bedient.
// Der Speicher muss extra eingerichtet und mit der Funktion
//  <setWriteBuffer(sndBufSize, sndBuffer)> übergeben werden.
// Die Klasse für die serielle Schnittstelle muss von <IntrfSerial> abgeleitet
// sein, die Instanz wird mit der Funktion <begin(...)> übergeben.


void setup()
{
#if defined(DebugTerminal)
  #if defined(ArduinoMonTerm)
    mon.config(6);    // 6 Anzeigekanäle, die von ArduinoMonTerm aufgebaut werden

    for(int i = 0; i < 6; i++)
    {
      if(i < 3)
        mon.config(i+1,'C',16000,-16000,NULL);    // Kanalnummer, Typ, Maxwert, Minwert
      else
        mon.config(i+1,'C',4000,-4000,NULL);
    }
  #endif
  mon.setInfo(infoThis);
#endif

  // Initialisierung von serieller Schnittstelle 1 und Ringpuffer 1
  // --------------------------------------------------------------------------
  tty1Params.inst    = SerCom1;       // Instanzindex der Schnittstelle (0,1)
  tty1Params.rxdPort = 1;             // Nummer des IO-Port mit RxD-Pin
  tty1Params.rxdPin  = 10;            // Nummer des RxD-Pin am Port
  tty1Params.txdPort = 1;             // Nummer des IO-Port mit TxD-Pin
  tty1Params.txdPin  = 3;             // Nummer des TxD-Pin am Port
  tty1Params.speed   = Baud115200;    // Enumerator für Bitrate
  tty1Params.type    = stStd;         // Standard-RS232

  tty1.begin(&tty1Params, (IntrfBuf *) &crb1);
  // Übergeben von Parametern und Referenz auf Ringpufferverwaltung
  // für die Übergabe empfangener Zeichen

  crb1.setWriteBuffer(sndBufSize, sndBuffer1);
  crb1.setReadBuffer(recBufSize, recBuffer1);
  // Speicher an Ringpufferverwaltung übergeben

  crb1.begin((IntrfSerial *) &tty1);
  // Referenz auf Schnittstelle an Ringpufferverwaltung
  // für die Übergabe zu sendender Zeichen

  tty1.startSend();     // Sendebetrieb aktivieren
  tty1.startRec();      // Empfangsbetrieb aktivieren

  // Initialisierung von serieller Schnittstelle 2 und Ringpuffer 2
  // --------------------------------------------------------------------------
  tty2Params.inst    = SerCom2;       // Instanzindex der Schnittstelle (0,1)
  tty2Params.rxdPort = 1;             // Nummer des IO-Port mit RxD-Pin
  tty2Params.rxdPin  = 11;            // Nummer des RxD-Pin am Port
  tty2Params.txdPort = 1;             // Nummer des IO-Port mit TxD-Pin
  tty2Params.txdPin  = 12;            // Nummer des TxD-Pin am Port
  tty2Params.speed   = Baud115200;    // Enumerator für Bitrate
  tty2Params.type    = stStd;         // Standard-RS232

  tty2.begin(&tty2Params, (IntrfBuf *) &crb2);
  // Übergeben von Parametern und Referenz auf Ringpufferverwaltung
  // für die Übergabe empfangener Zeichen

  crb2.setWriteBuffer(sndBufSize, sndBuffer2);
  crb2.setReadBuffer(recBufSize, recBuffer2);
  // Speicher an Ringpufferverwaltung übergeben

  crb2.begin((IntrfSerial *) &tty2);
  // Referenz auf Schnittstelle an Ringpufferverwaltung
  // für die Übergabe zu sendender Zeichen

  tty2.startSend();     // Sendebetrieb aktivieren
  tty2.startRec();      // Empfangsbetrieb aktivieren
}

#ifdef DebugTerminal
char  runView[4] = {'|','/','-','\\'};
int   runViewIdx = 0;
#endif

void loop()
{
  lc.begin();
  // --------------------------------------------------------------------------

#ifdef DebugTerminal
  // Der Monitor wird ständig aufgerufen und stellt damit eine grundsätzliche
  // Belastung dar. Das ist für die Entwicklung auch vorgesehen.
  // Es entsteht dadurch eine Reserve für den produktiven Einsatz.
  //
  mon.run();
#endif

#ifdef DebugTerminal
  // Alle 5 Millisekunden wird die Zustandsmaschine für
  // betriebliche Abläufe aufgerufen
  //
  if(lc.timerMilli(lcTimer4, smCycleTime, 0))
  {
    sm.run();
  }

  // Jede halbe Sekunde erfolgt die Ausgabe der Version
  //
  if(lc.timerMilli(lcTimer5, 500, 0))
  {
    if(!mon.cFlag[0])
    {
      mon.print(StartMsg);
      mon.cprintcr(runView[runViewIdx]);
      runViewIdx++;
      if(runViewIdx > 3) runViewIdx = 0;
    }
  }
#endif
  // --------------------------------------------------------------------------
  lc.end();
}

#ifdef DebugTerminal
// ****************************************************************************
// Z u s t a n d s m a s c h i n e  (für Monitor-Ergänzung)
// ****************************************************************************
//

dword       debDword;
byte        tmpByteArray[256];
int         tmpInt;


void smInit()
{
  sm.enter(smCheckJobs);
}

// ----------------------------------------------------------------------------
// Abfrage der Monitorschalter
// ----------------------------------------------------------------------------
//
char  tmpOut[256];
char  charOut[2];


void smCheckJobs()
{
  if(mon.cFlag[1] && !mon.busy)
    sm.enter(smCheckMemory);
  else if(mon.cFlag[2] && !mon.busy)
    sm.enter(smCheckRB);
/*  else if(mon.cFlag[3] && !mon.busy)
    sm.enter(sm);
  else if(mon.cFlag[4] && !mon.busy)
    sm.enter(sm);
  else if(mon.cFlag[5] && !mon.busy)
    sm.enter(sm);
  */
}

// ----------------------------------------------------------------------------
// Testen der Speicherzugriffe (Datenstruktur)
// ----------------------------------------------------------------------------
//

void smCheckMemory()
{
  if(sm.firstEnter())
  {
    mon.print((char *) "Speichertest ");
    mon.lastKeyIn = ':';
  }

  if(mon.lastKeyIn == ':') return;

  charOut[0] = mon.lastKeyIn;
  charOut[1] = '\0';
  mon.println(charOut);

  if(mon.lastKeyIn == '0' || mon.lastKeyIn == ' ')
  {
    mon.cFlag[1] = false;
    mon.print((char *) "-- Schleifenabbruch - drücke Enter");
    sm.enter(smCheckJobs);
  }
  else if(mon.lastKeyIn == 'S' || mon.lastKeyIn == 's')
  {
    mon.println();
    sm.enter(smTestSerMem);
  }
  mon.lastKeyIn = ':';
  sm.resetEnter();
}


void smTestSerMem()
{
  nrfSerPtr perMem = NULL;

  dword res1 = (dword) &perMem->EVENTS_ENDRX;
  dword res2 = (dword) &perMem->INTEN;
  dword res3 = (dword) &perMem->EVENTS_ERROR;
  dword res4 = (dword) &perMem->ENABLE;
  dword res5 = (dword) &perMem->BAUDRATE;
  dword res6 = (dword) &perMem->RXD_AMOUNT;
  dword res7 = (dword) &perMem->CONFIG;

  sprintf(tmpOut,"%X %X %X %X %X %X %X",res1,res2,res3,res4,res5,res6,res7);
  mon.println(tmpOut);
  sm.enter(smCheckMemory);
}


// ----------------------------------------------------------------------------
// Testen der Ringpuffer
// ----------------------------------------------------------------------------
//

char *smCheckRBHelp =
{
    "A   Zeichen in P1 eingeben\r\n"
    "B   Zeichen in P2 eingeben\r\n"
    "I   Anzahl der Interrupts anzeigen\r\n"
    "R   Inhalte der Empfangspuffer auslesen\r\n"
    "Sx  String in Px eingeben\r\n"
    "Z   Zeiger/Index der Ringpuffer anzeigen\r\n"
};

void smCheckRB()
{
  if(sm.firstEnter())
  {
    mon.print((char *) "Testen der Ringpuffer und seriellen Schnittstellen ");
    mon.lastKeyIn = ':';
  }

  if(mon.lastKeyIn == ':') return;

  charOut[0] = mon.lastKeyIn;
  charOut[1] = '\0';
  mon.println(charOut);

  if(mon.lastKeyIn == 'A' || mon.lastKeyIn == 'a')
  {
    sm.userVar = 1;
    sm.enter(smPutCharRB);
  }
  else if(mon.lastKeyIn == 'B' || mon.lastKeyIn == 'b')
  {
    sm.userVar = 2;
    sm.enter(smPutCharRB);
  }
  else if(mon.lastKeyIn == 'I' || mon.lastKeyIn == 'i')
  {
    sm.userVar = 1;
    sm.enter(smGetCntIntSer);
  }
  else if(mon.lastKeyIn == 'R' || mon.lastKeyIn == 'r')
  {
    sm.userVar = 1;
    sm.enter(smReadAllRB);
  }
  else if(mon.lastKeyIn == 'S' || mon.lastKeyIn == 's')
  {
    sm.userVar = 0;
    sm.enter(smWriteStrRB);
  }
  else if(mon.lastKeyIn == 'Z' || mon.lastKeyIn == 'z')
  {
    sm.userVar = 1;
    sm.enter(smReadPtrRB);
  }
  else if(mon.lastKeyIn == '0' || mon.lastKeyIn == ' ')
  {
    mon.cFlag[2] = false;
    mon.print((char *) "-- Schleifenabbruch - drücke Enter");
    sm.enter(smCheckJobs);
  }
  else if(mon.lastKeyIn == 'H' || mon.lastKeyIn == 'h' || mon.lastKeyIn == '?')
  {
    mon.print(smCheckRBHelp);
  }
  mon.lastKeyIn = ':';
  sm.resetEnter();
}

void smPutCharRB()
{
  int retv;

  if(sm.firstEnter())
  {
    mon.print((char *) "Zeichen eingeben für Puffer ");
    mon.print(sm.userVar);
    mon.print((char *) " ");
    mon.lastKeyIn = ':';
  }
  if(mon.lastKeyIn == ':') return;

  mon.cprint(mon.lastKeyIn);
  if(sm.userVar == 1)
    retv = crb1.putChr(mon.lastKeyIn);
  else
    retv = crb2.putChr(mon.lastKeyIn);
  mon.println(retv);
  mon.lastKeyIn = ':';
  sm.enter(smCheckRB);
}

void smReadPtrRB()
{
  ComRingBuf::DbBufValues bv;

  mon.print((char *) "Instanz [");
  mon.print(sm.userVar);
  mon.print((char *) "]: Indizes RbIn=");
  if(sm.userVar == 1)
    crb1.dbGetBufValues(&bv);
  else
    crb2.dbGetBufValues(&bv);
  mon.print(bv.rbRdIdx);
  mon.print((char *) " RbOut=");
  mon.print(bv.rbWrIdx);

  mon.print((char *) " TbIn=");
  mon.print(bv.tbRdIdx);

  mon.print((char *) " TbOut=");
  mon.println(bv.tbWrIdx);

  if(sm.userVar == 2)
  {
    mon.lastKeyIn = ':';
    sm.enter(smCheckRB);
  }
  else
    sm.userVar++;
}

void smGetCntIntSer()
{
  int cnt1, cnt2;

  cnt1 = tty1.getIrqCount();
  cnt2 = tty2.getIrqCount();
  mon.print((char *) "Interrupts Ser1=");
  mon.print(cnt1);
  mon.print((char *) " Ser2=");
  mon.println(cnt2);
  mon.lastKeyIn = ':';
  sm.enter(smCheckRB);
}

unsigned char tmpCharBuf[32];

void smReadAllRB()
{
  int nrIn;

  if(sm.userVar == 1)
  {
    nrIn = crb1.getAll(tmpCharBuf);
    mon.print((char *) "In(1)={");
  }
  else
  {
    nrIn = crb2.getAll(tmpCharBuf);
    mon.print((char *) "In(2)={");
  }
  if(nrIn > 32) nrIn = 32;
  mon.print(tmpCharBuf,nrIn,',');
  mon.cprintln('}');
  if(sm.userVar == 2)
  {
    mon.lastKeyIn = ':';
    sm.enter(smCheckRB);
  }
  else
    sm.userVar++;
}

char smWrStNr;

void smWriteStrRB()
{
  if(sm.firstEnter())
  {
    mon.print((char *) "String (Endezeichen TAB) eingeben für Puffer ");
  }

  if(mon.lastKeyIn == ':') return;

  if(sm.userVar < 1)
  {
    if(mon.lastKeyIn < '1' || mon.lastKeyIn > '2') return;
    smWrStNr = mon.lastKeyIn;
    mon.cprint(smWrStNr);
    mon.cprint(':');
    mon.cprint(' ');
    sm.userVar++;
    mon.lastKeyIn = ':';
    return;
  }

  if(mon.lastKeyIn != '\t')
  {
    tmpCharBuf[sm.userVar - 1] = mon.lastKeyIn;
    mon.cprint(mon.lastKeyIn);
    sm.userVar++;
    mon.lastKeyIn = ':';
    return;
  }

  tmpCharBuf[sm.userVar - 1] = '\0';
  if(smWrStNr == '1')
    crb1.putStr((char *) tmpCharBuf);
  else if(smWrStNr == '2')
    crb2.putStr((char *) tmpCharBuf);
  mon.println();
  mon.lastKeyIn = ':';
  sm.enter(smCheckRB);
}


#endif // DebugTerminal