//Serielle Kommunikation und Parser-Routinen

procedure SerOutFreq(myfreq: Longint);
var
  lastdigit:char;
  l:byte;
begin
  FreqStr:= longtostr(myfreq); // ohne führenden Nullen
  l:=length(FreqStr);
  lastdigit:=FreqStr[l];  //letztes Zeichen
  dec(l);
  SetLength(freqStr,l);
  write(serout, freqStr);
  serout('.');
  serout(lastdigit);
end;

procedure SetSweepUpper;
// obere Frequenz-Grenze für Sweep setzen
begin
  SweepUpper:=longint(gettable(SweepFak,SweepRange));
  SweepUpper:=SweepUpper*Frequenz;
  SweepStarted:=false;
end;

procedure SweepGen;
//Sweep-Generierung per Software, Ausgabe aktueller Frequenz seriell
begin
  if (SweepRange <> 0) then
//    dec(SweepCount);
    if not SweepStarted then
      setSystimer(BurstTimer, SweepTime);
      SetLevelDDS;
//      SweepCount:=0;
      SweepStarted:=true;
    endif;
    if isSysTimerZero(BurstTimer) then
      setSystimer(BurstTimer, SweepTime);
      AktuelleFrequenz:=(AktuelleFrequenz * 101) div 100;
      SetLevelDDS;
      mdelay(word(SweepTime));
      if AktuelleFrequenz>=SweepUpper then
        SweepStarted:=false;
        AktuelleFrequenz:=Frequenz;
      endif;
    endif;
  else
    AktuelleFrequenz:=Frequenz;
  endif;
end;

{###########################################################################}
//allg. Menü-Prozeduren

procedure SerCRLF;
begin
  Serout(#$0D);
  Serout(#$0A);
end;

procedure WriteChPrefix;
begin
  Serout('#');
  Serout(char(SlaveCh+48));
  Serout(':');
  write(Serout, ByteToStr(SubCh));
  Serout('=');
end;

procedure WriteSerInp;
begin
  write(Serout, SerInpStr);   // Befehl weiterreichen
  SerCRLF;
end;

procedure SerPrompt(myErr:tError;myStatus:byte);
//Error-Meldung und Status-Request-Antwort,
//Status Bit 7=Busy, 6=UserSRQ, 5=OverLoad, 4=!!!, 3..0=Fehler-Nummer
begin
  if verbose OR (myERR<>noErr) then
    SubCh:=ErrSubCh; // Fehler-Kanal
    WriteChPrefix;
    write(Serout, ByteToStr(ord(myErr)+myStatus));
    Serout(#32);
    write(serout,ErrStrArr[ord(myErr)]);
    SerCRLF;
  endif;
  if myERR<>noErr then
    inc(ErrCount);
    ErrFlag:=true;
  endif;
end;

procedure WriteParam(onLCD:boolean);
var ParamStr: String[15];
  digits:byte;
begin
  ParamStr:=floatToStr(Param);
  ParamStr:=trim(ParamStr);
  digits:=6;
  if Param<0 then
    inc(digits);
  endif;
  ParamStr:=copy(ParamStr,1,digits); // auf 6 Stellen genau
  write(serout,ParamStr);
  SerCRLF;
end;

{###########################################################################}

procedure ParseGetParam;
begin
  case SubCh of
    0:
      Param:=float(Frequenz)/10.0;
      |
    1:
      Param:=Level;
      |
    3:
      Param:=db;
      |
    4:
      param:=float(wave);
      |
    5:
      param:=float(BurstMode);
      |
    6:
      param:=float(OutputMode);
      |
    10:
      Param:=float(getadc(7));
      |
    11:
      Param:=float(getadc(7))*2.8284;
      |
    12:
      Param:=float(getadc(7));
      Param:= Level2dB(Param+0.1);     // 774.59667 mV
      |
    254: //Wert
      SubCh:=ErrSubCh;
      WriteChPrefix;
      write(serout,Vers1Str);
      SerCRLF;
      return;
      |
  else
    serprompt(ParamErr,status);
    return;
  endcase;
  WriteChPrefix;
  WriteParam(false);
end;

procedure ParseSetParam;
begin
  if Busyflag then
    serprompt(BusyErr,0);
    return;
  endif;
  case SubCh of
    0:
      Frequenz:=longint(Param)*10;
      AktuelleFrequenz:=LongInt(Frequenz);
      |
    1:
      Level:=Param;
      db:=Level2db(Level);
      |
    2:
      Level:=Param/2.8284;
      db:=Level2db(Level);
      |
    3:
      db:=param;
      Level:=dB2Level(db);
      |
    4:
      wave:=ParamByte;
      |
    5:
      BurstMode:=ParamByte;
      |
    6:
      OutputMode:=ParamByte;
      |
    80:
      ShowFrequency;
      |
   else
    serprompt(ParamErr,Status);
    return;
  endcase;
  SetLevelDDS;
  serprompt(NoErr,Status);
end;

// allgemeiner Parser-Teil

function Cmd2Index : tCmdWhich;
// Umsetzen eines Text-Befehls in Index-Eintrag der Befehlstabelle
var myCmdIndex: tCmdWhich;
begin
  ParamStr:= uppercase(ParamStr);
  for myCmdIndex := tError(0) to nop do
    if ParamStr = CmdStrArr[ord(myCmdIndex)] then
      return(myCmdIndex);
    endif;
  endfor;
  return(err);
end;

function ParseExtract:boolean;
//extrahiert ParamStr oder CmdStr aus SerInpStr,
//liefert true, wenn Parameter, sonst false, wenn Command
var myChar: char; myBool:boolean;
begin
  ParamStr:='';
  myBool:=false;
  while SerInpStr[SerInpPtr] =' ' do // Leerzeichen überspringen
    inc(serInpPtr);
  endwhile;
  if SerInpStr[SerInpPtr] in ['*'..'9'] then // Zahlen oder Wildcard, es wird ein Parameter
    myBool:=true;
    for i:=SerInpPtr to length(SerInpStr) do
      mychar:=SerInpStr[i];
      if myChar <='9' then
        append(mychar,ParamStr);
      else // Buchstabe oder sonstirgendwas, abbrechen
        SerInpPtr:=i;
        return(mybool);
      endif;
    endfor;
  else
    for i:=SerInpPtr to length(SerInpStr) do
      mychar:=SerInpStr[i];
      if myChar >= 'A' then
        append(mychar,ParamStr);
      else // Ziffer oder sonstirgendwas, abbrechen
        SerInpPtr:=i;
        return(mybool);
      endif;
    endfor;
  endif;
  return(mybool);
end;

procedure ParseSubCh;
//Überprüfen, ob Befehl oder Daten eingegangen und für uns, Parser-Vorbehandlung,
//wenn Busy => BusyErr
var
  GleichPos,
  CheckSpos, myCheckSum,CheckSumIn:byte;
  SubChOffset:byte;

  myChar:char;
  hasMainCh, hasCRC, isResult, isOmni, isRequest, isParam: Boolean;

begin
  if SerInpStr='' then
    serprompt(NoErr,0);
    return;
  endif;
  hasMainCh:=(pos(':',SerInpStr)>0); // Kanaltrenner-':'
  isRequest:=not (pos('=',SerInpStr)>0); // Abfrage
  myChar:=SerInpStr[1];
  isOmni:=(myChar='*');    // Omni-Flag
  isResult:=(myChar='#');  // Ergebnis-'#'
  if isResult then
    WriteSerInp;
    return;
  endif;
  SerInpPtr:=1;
  if hasMainCh then
    isParam:=ParseExtract;
    inc(SerInpPtr);
    if isOmni then // Omni-Befehl
      WriteSerInp; // weiterleiten
    else
      CurrentCh:=StrToInt(ParamStr);
    endif;
  endif;
  if (not isOmni) and (CurrentCh<>SlaveCh) then // nicht für uns
    WriteSerInp;
    return;
  endif;

// Befehl/Parameter ist für uns, ab hier eigentliche Behandlung
// Wenn vorhanden, XOR-Prüfsumme checken, Präfix-"$" zählt nicht mit!
  verbose:=(pos('!',SerInpStr)>0) OR (pos('?',SerInpStr)>0); // Ausführliche Antwort erwünscht
  CheckSpos:=pos('$',SerInpStr);
  hasCRC:=CheckSpos>0; // CheckSumIn-'$'
  if hasCRC then
    ParamStr:=copy(SerInpStr,CheckSpos+1, 2);
    CheckSumIn:=HexToInt(ParamStr); // erhaltene XOR-Checksumme
    myCheckSum:=0;
    for i:= 1 to CheckSpos-1 do
      myChar:=SerInpStr[i];
      myCheckSum:=myCheckSum xor ord(myChar);
    endfor;
    if myCheckSum <> CheckSumIn then
      serprompt(CheckSumErr,0);
      return;
    endif;
  endif;
//  SerInpStr:=trim(SerInpStr);
  SetSystimer(ActivityTimer, 25);
  LEDactivity:=low;

//Parse einzelnen Befehl

  if ParseExtract then
    SubChOffset:=0; // direkter SubCh-Aufruf
  else
    CmdWhich:=Cmd2Index; // Klartext übersetzen
    if CmdWhich = err then
      serprompt(SyntaxErr,0);
      return;
    endif;
    SubChOffset:=Cmd2SubChArr[ord(CmdWhich)];
    isParam:=ParseExtract; // SubCh-Parameter holen
  endif;
  SubCh:=StrToInt(ParamStr)+SubChOffset; //auf neuen SubCh umrechnen

  if isRequest then
    ParseGetParam;
  else
    SerInpPtr:=pos('=',SerInpStr)+1; // Set-'='
    if (SerInpPtr>1) and ParseExtract then
      Param:=StrToFloat(ParamStr);
      ParamInt:=integer(Param);
      ParamByte:=byte(ParamInt);
    else
      serprompt(ParamErr,0);
      return;
    endif;
    ParseSetParam;
  endif;
end;


{###########################################################################}

