{###########################################################################}

procedure ParamToStr;
var myNachkomma:byte;
begin
  myNachkomma:=Nachkomma;
  if Nachkomma>7 then
    myNachkomma:=5;
    if abs(Param)>=10 then
      myNachkomma:=4;
    endif;
    if abs(Param)>=100 then
      myNachkomma:=3;
    endif;
    if abs(Param)>=1000 then
      myNachkomma:=2;
    endif;
  endif;
  if Param<>0 then
    ParamStr:=LongToStr(Param);
  else
    ParamStr:='0.00000';
    setLength(ParamStr,myNachkomma+2);
  endif;
end;

procedure ParamLongToStr;
//mit einer Nachkommastelle in Strin wandeln
begin
  ParamStr:=LongToStr(ParamLong);
end;

procedure ParamLongToHex;
//Longin in Hex-String wandeln
begin
  ParamStr:=LongToHex(ParamLong);
end;

procedure ParamIntToHex;
//Longin in Hex-String wandeln
begin
  ParamStr:=IntToHex(ParamInt);
end;

procedure WriteParamStrSer;
begin
  WriteChPrefix;
  write(SerOut, ParamStr);
  SerCRLF;
end;

procedure WriteParamSer;
begin
  ParamToStr;
  WriteParamStrSer;
end;

procedure WriteParamIntSer;
begin
  ParamStr:= IntToStr(ParamInt);
  WriteParamStrSer;
end;

procedure WriteParamByteSer;
begin
  ParamStr:= ByteToStr(ParamByte);
  WriteParamStrSer;
end;

procedure WriteParamHexSer;    // für verbose-Anfrage
begin
  WriteChPrefix;
  write(SerOut, ParamStr);
  SerOut(#32);
  SerOut('[');
  SerOut('$');
  ParamStr:= LongToHex(ParamLong);
  write(SerOut, ParamStr);
  SerOut(']');
  SerCRLF;
end;

procedure WriteParamLongSer;
begin
  ParamStr:= LongToStr(ParamLong);
  if hexmode then
    WriteParamHexSer;
  else
    WriteParamStrSer;
  endif;
end;

// gerätespezifischer Parser-Teil
procedure ParseGetParam;
var IntInOutFlag:boolean;
    myBranch:boolean;
    myByte:byte;
    myIndex:Byte;

begin
  myIndex:=byte(SubCh mod 10);  // angespr. Register 0..9 errechnen aus SubCh-Rest
  case SubCh of
    0..63:
      FPGAreg:=byte(SubCh);
      SendFPGAreg;
      if (SubCh < 4) then
        FPGAsendLong:=ParamLongArray[SubCh];  // bei SPI-Lesen gesendeter Param retten
      endif;
      SendFPGA32;
      ParamLong:=FPGAreceiveLong;
      WriteParamLongSer;
      |
    80:
      ParamByte:=ord(Modify);
      WriteParamByteSer;
      |
    88:
      if hexmode then
        ParamByte:=1;
      else
        ParamByte:=0;
      endif;
      WriteParamByteSer;
      |
    89:
      ParamByte:=byte(IncRast);
      WriteParamByteSer;
      |
    128..131: // AutoInc-Register
      FPGAreg:=byte(SubCh);
      SendFPGAreg;
      SendFPGA32;
      ParamLong:=FPGAreceiveLong;
      WriteParamLongSer;
      |
    240:
      WriteDirEntry;
      |
    241:
      ClearDirectory;
      GetDirectory;
      FileNumTemp:=FileNum;
      if FileAnzahl>0 then
        for i:= 0 to byte(FileAnzahl)-1 do
          FileNum:= Int8(i);
          WriteDirEntry;
        endfor;
      else
        FileNum:= 0;
        WriteDirEntry;
      endif;
      FileNum:=FileNumTemp;
      |
    242:
      ParamByte:= byte(FileAnzahl);
      WriteParamByteSer;
      |
    243: // DatFileName
      WriteChPrefix;
      write(SerOut,'0 ['+DatFileName+']');
      SerCRLF;
      |
    251: // Fehlerzähler auslesen
      ParamInt:=Errcount;
      WriteParamIntSer;
      |
    253: // SerTest, gibt Input-String komplett und unverändert wieder aus
      write(SerOut, SerInpStr);
      SerCRLF;
      |
    254: // Version
      WriteChPrefix;
      write(SerOut,Vers1Str);
      SerCRLF;
      |
    250,255: // Status
      serprompt(NoErr);
      |
  else
    serprompt(ParamErr);
  endcase;
end;


procedure ParseSetParam;
var myByte:byte;
    myIndex:Byte;
    myBranch:boolean;

begin
  ChangedFlag:=true;
  myIndex:=byte(SubCh mod 10);  // angespr. Register 0..9 errechnen aus SubCh-Rest
  case SubCh of
    0..63:
      if (SubCh < 4) then
        ParamLongArray[SubCh]:=ParamLong;
      endif;
      FPGAreg:=byte(SubCh);
      SendFPGAreg;
      FPGAsendLong:=ParamLong;
      SendFPGA32;
      |
    80: // LCD Anzeige-Wert
      Modify:=tModify(ParamByte);
      |
    88: //Hex-Mode
      hexmode:= (ParamByte<>0);
      if EEunLocked then
        InitHexMode:=Hexmode;
      endif;
      |
    128..131:  // Default-Auto-Increment-Register
      FPGAreg:=byte(SubCh);
      SendFPGAreg;
      FPGAsendLong:=ParamLong;
      SendFPGA32;
      |
    240:  // CFG=
      if ParamAlpha then
        FileLoad(uppercase(ParamStr));
      else
        FileNum:= Int8(ParamByte);
        FileLoad(DirectoryArray[FileNum]);
      endif;
      if FaultFlags<>0 then
        serprompt(FileErr);
        return;
      endif;
      |
    243:  // DatFileName, FNA=
      if ParamAlpha then
        DatFileName:=uppercase(ParamStr);
      else
        FileNum:= Int8(ParamByte);
        DatFileName:=DirectoryArray[FileNum];
      endif;
      if FaultFlags<>0 then
        serprompt(FileErr);
        return;
      endif;
      |
    249:  // FileExists
      if ParamAlpha then
        SerInpStr:=uppercase(ParamStr);
      else
        FileNum:= Int8(ParamByte);
        SerInpStr:=DirectoryArray[FileNum];
      endif;
      if CardOK and F16_FileExist ('\', SerInpStr, faFilesOnly) then
        FaultFlags:=0;
        serprompt(noErr);
        return;
      else
        ReadErr:=true;
        serprompt(FileErr);
        return;
      endif;
      |
    251: //Error-Count
      ErrCount:=ParamInt;
      |
    253: // SerTest, gibt Input-String komplett und unverändert wieder aus
      write(SerOut, SerInpStr);
      SerCRLF;
      return;
      |
    250: // EEPROM Write Enable
      |
    280: //AutoIncrement-Register für DAT-Files
      AutoIncReg:= ParamByte;
      |
    281: //AutoIncrement-Select (AUX-Select auf SubCh <AutoIncReg> für DAT-Files
      AutoIncSel:= ParamByte;
      |
    282:  //  Auto-Increment-Registerbreite im SPI, Anzahl Bytes (1, 2 oder 4)
      AutoIncWidth:=ParamByte;
      |
    285:  //  Auto-Increment-Blockstart zum Speichern auf SD
      AutoIncBlockStart:=ParamLong;
      |
    286:  //  Auto-Increment-Blockende zum Speichern auf SD
      AutoIncBlockEnd:=ParamLong;
      |
   else
    serprompt(ParamErr);
    return;
  endcase;
  EEunLocked:=false;
  if SubCh=250 then  // EEPROM Write Enable
    EEunLocked:=true;
  endif;
  if verbose then
    serprompt(NoErr);
  endif;
end;

{###########################################################################}
// Neuer Alpha-Parser, nimmt in ParamStr auch Strings nach "=" entgegen
// allgemeiner Parser-Teil

function Cmd2Index : byte;
// Umsetzen eines Text-Befehls in Index-Eintrag der Befehlstabelle
var myCmdIndex: byte;
begin
  ParamStr:= uppercase(ParamStr);
  for myCmdIndex := 0 to CmdAnzahl do
    if ParamStr = CmdStrArr[ord(myCmdIndex)] then
      return(myCmdIndex);
    endif;
  endfor;
  return(cmderr);
end;

function ParseExtract(nachGleich:boolean):boolean;
//extrahiert ParamStr oder CmdStr aus SerInpStr,
//liefert true, wenn Parameter, sonst false, wenn Command
//akzeptiert auch alphanumerische Parameter als String nach "="
var myChar: char; myBool:boolean;
begin
  ParamStr:='';
  ParamAlpha:=false;
  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 in ['*'..'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='"' then
        ParamAlpha:=true;
      else
        if (mychar>='A') or ParamAlpha then
          if (myChar in ['!','?','$']) then
            SerInpPtr:=i;
            return(mybool);
          else
            append(mychar,ParamStr);
          endif;
          if nachGleich then
            ParamAlpha:=true;
          endif;
        else // Ziffer oder sonstirgendwas, abbrechen
          SerInpPtr:=i;
          return(mybool);
        endif;
      endif;
    endfor;
  endif;
  return(mybool);
end;

procedure ParamStr2Param;
var DecPointPos:byte;
begin
  Param:=StrToInt(ParamStr);
// 32-Bit-Werte passen nicht in ein Float, deshalb getrennt wandeln
  DecPointPos:= pos('.',ParamStr);
// vor Dezimalpunkt abschneiden
  if DecPointPos>0 then
    SetLength (ParamStr,DecPointPos-1);
  endif;
  ParamLong:=StrToInt(ParamStr);
  ParamInt:=integer(ParamLong);
  ParamByte:=byte(ParamInt);
end;


procedure ParseSubCh;
//Überprüfen, ob Befehl oder Daten eingegangen und für uns, Parser-Vorbehandlung,

var
  GleichPos,
  CheckSpos, myCheckSum,CheckSumIn:byte;
  SubChOffset:integer;

  myChar:char;
  hasMainCh, hasCRC, isResult, isOmni, isRequest: Boolean;

begin
  if SerInpStr='' then
    serprompt(NoErr);
    return;
  endif;
  hasMainCh:=(pos(':',SerInpStr)>0); // Kanaltrenner-':'
  gleichPos:=pos('=',SerInpStr); // Set-'='
  isRequest:=(GleichPos=0); // Abfrage
  myChar:=SerInpStr[1];
  isOmni:=(myChar='*');    // Omni-Flag
  isResult:=(myChar='#');  // Ergebnis-'#'
  SerInpPtr:=1;
  if isResult then
    SerInpPtr:=2;
  endif;
  if hasMainCh then
    ParseExtract(false);
    inc(SerInpPtr);
    if isOmni then // Omni-Befehl
      WriteSerInp; // weiterleiten
    else
      CurrentCh:=StrToInt(ParamStr);
    endif;
  endif;
  if isResult then
    if not INITfileIsOpen then
      WriteSerInp;
    endif;
  else
    if (not isOmni) and (CurrentCh<>MainCh) and hasMainCh then // nicht für uns
      WriteSerInp;
      return;
    endif;
  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);
      return;
    endif;
  endif;
//  SerInpStr:=trim(SerInpStr);

//Parse einzelnen Befehl
  if ParseExtract(false) then
    SubChOffset:=0; // direkter SubCh-Aufruf
  else
    CmdWhich:=Cmd2Index; // Klartext übersetzen
    if CmdWhich = cmderr then
      serprompt(SyntaxErr);
      return;
    endif;
    SubChOffset:=Cmd2SubChArr[ord(CmdWhich)];
    ParseExtract(false); // SubCh-Parameter holen
  endif;
  SubCh:=StrToInt(ParamStr)+SubChOffset; //auf neuen SubCh umrechnen
    if isRequest then
      ParseGetParam;
    else
      SerInpPtr:=gleichPos+1; // Set-'='
      if ParseExtract(true) then
        ParamStr2Param;
      else
        Param:=0;             // evt. auch Alpha-Parameter
        ParamLong:=0;
        ParamInt:=0;
        ParamByte:=0;
      endif;
      ParseSetParam;
    endif;
end;

