/*@!Encoding:1252*/

includes
{
  #include "AL_Output.cin"
}


variables
{
  const word wChannelsPerCard = 4;       // 4 measurement channels per card
  const word wCardCntMax      = 16;      // max. supported cards
  const word wRelayMsgCntMax  = 4;       // 4 cards per message
  const word wCardCanMask     = 0xFFF0;  // use 4 low bits for card addressing
  const word wMeasChannels    = 10;      // max. number of measurement channels
  const word wTimeStampRange  = 4096;    // use 12 bit time stamp
  const word wBufferSize      = 10000;   // measurement buffer size
  const word wBufferOffset    = 5000;    // start output if buffer is filled 50%
  const long lCanTimeout      = 100;     // 100ms timeout for incoming CAN messages
  const long lMeasDelay       = 100;     // 100ms delay between measurement and display
  const long lMsgCycle        = 20;      // 20ms CAN message cycle time

  const word wAddMsgBusNone  = 0;       // bus used for additional message
  const word wAddMsgBusCan   = 2;
//const word wAddMsgBusLin   = 2;

  Timer   timerMeasTick;     // 100µs tick
  msTimer timerMeasStart;    // delay between measurement and display
  msTimer timerTimeoutTick;  // timeout calculation for CAN messages
  word timerTimeoutTick_Idx;
  word timerTimeoutTick_wChannel;
  msTimer timerMsgTimer;     // used for transmission of CAN messages

  message MsgCommon       CommonMessage;
  message MsgRelayCtrl_00 RelayMessage;
  message mDiag_Req_00    DiagReqMessage;
  message mDiag_Res_00    DiagResMessage;

  char cSwVersion[11] = "B4Al32----"; // e.g. 'B4Al32y07m' 'B4Al32z06m'

  int     nTestStart[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };

  int     nMeasValue   [wMeasChannels][wBufferSize];              // input buffer: measurement values
  dword   dwTimeStamp  [wMeasChannels][wBufferSize];              // input buffer: time stamps

  word    wPtrIn       [wMeasChannels] = { 0,0,0,0,0,0,0,0,0,0 }; // write pointer for input buffer
  word    wPtrOut      [wMeasChannels] = { 0,0,0,0,0,0,0,0,0,0 }; // read pointer for input buffer
  word    wTimeStampMem[wMeasChannels] = { 0,0,0,0,0,0,0,0,0,0 }; // 12 bit time stamp of last CAN message
  dword   dwTimeOffset [wMeasChannels] = { 0,0,0,0,0,0,0,0,0,0 }; // accumulated time stamp
  float   fDispOffset  [wMeasChannels] = { 0,0,0,0,0,0,0,0,0,0 }; // for output calibration

  dword   dwMeasTick;    // 100µs tick

  byte    byRelayMsgActive[wRelayMsgCntMax] = { 0,0,0,0 }; // 1 if corresponding message is activated
  word    wRelayCtrlWord  [wCardCntMax]     = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; // 1 word per card

  byte    byMeasStart   = 0;
  byte    byMeasActive  = 0;
  byte    byMeasStopped = 0;
  
  int gPrepareStepperMeasurement;
  msTimer prepareStepperMeasurement;
}


on start
{
  CommonMessage.SigCommon_RelayActive = 1;
  CommonMessage.SigCommon_CardDetect  = 1;
  
  ScalingProfileRead();

  SysSetVariableString(sysvar::sys_Control::sSwVersion, cSwVersion);

  setTimer(timerMsgTimer, lMsgCycle);
}


on preStop
{
  ScalingProfileWrite();
}


on timer timerMeasTick
{
  word wCh;

  dwMeasTick += 1;

  for(wCh=0; wCh<wMeasChannels; wCh++)
  {
    if(wPtrIn[wCh] != wPtrOut[wCh])            // new data available?
    {
      if(wCh == @sys_Measure::nBufferMonitor)  // monitor on this channel active?
      {
        if(wPtrIn[wCh] >= wPtrOut[wCh])
        {
          @sys_Measure::nBufferUsage = wPtrIn[wCh] - wPtrOut[wCh];
        }
        else
        {
          @sys_Measure::nBufferUsage = wBufferSize + wPtrIn[wCh] - wPtrOut[wCh];
        }
      }

      if(dwMeasTick >= dwTimeStamp[wCh][wPtrOut[wCh]])
      {
        @sys_Measure::afOutput[wCh] = (float)  nMeasValue[wCh][wPtrOut[wCh]]
                                             / @sys_Measure::anDivisor[wCh]
                                             + fDispOffset            [wCh];

        wPtrOut[wCh] = IncBufferPtr(wPtrOut[wCh]);

        if(wPtrIn[wCh] == wPtrOut[wCh])
        {
          byMeasStopped = 1;
        }
      }
    }
  }

  if(byMeasStopped == 0)
  {
    // continue masurement cycle at 100µs rate
    setTimer(timerMeasTick, 0, 100000);
  }
}


on timer timerTimeoutTick
{
  // Programmed interrupt of the ongoing measurement.
  // In offline mode this function interrupts but does not end the measurement.
  // In offline mode the measurement can only be ended with <ESC>.
  
  write("******** on timer timerTimeoutTick (wChannel='%d')(%l)  ->  STOP  *************", timerTimeoutTick_wChannel);
  Output("*** Error - STOP?!?! ***");

  // stop();
}


on message *
{
  word wCard;
  
  switch(this.ID & wCardCanMask)
  {
    case MsgCardDetect_00.ID:
      wCard = this.ID - MsgCardDetect_00.ID;

      @sys_Control::anAvailableCard[wCard] = 1;
      @sys_Control::anCurrentRange [wCard] = this.byte(0);  // TBD

      if(this.char(4) != 0)
      {
        cSwVersion[6]  = this.char(4);
        cSwVersion[7]  = this.char(5);
        cSwVersion[8]  = this.char(6);
        cSwVersion[9]  = this.char(7);
        cSwVersion[10] = '\0';

        SysSetVariableString(sysvar::sys_Control::sSwVersion, cSwVersion);
      }

      if(nTestStart[wCard])
      {
        msgBeep(0);
      }
      else
      {
        nTestStart[wCard] = 1;
      }
      break;

    case mDiag_Res_00.ID:
      DiagResMessage.qword(0) = this.qword(0);
      EvaluateDiagResponse(this.ID - mDiag_Res_00.ID);
      break;

    default:
      if((@sysAddMsg::nBusType == wAddMsgBusCan) && (this.ID == @sysAddMsg::nID))
      {
        StoreAdditionalMeasurement(this.qword(0));
      }
      break;
  }
}


StartOutput(void)
{
  if((byMeasActive == 0) && (byMeasStopped == 0))
  {
    byMeasActive = 1;
 
    // initiate measurement tick event at once
    setTimer(timerMeasTick, 0, 0);
  }
}


word IncBufferPtr(word p)
{
  p++;

  if(p >= wBufferSize)
  {
    p = 0;
  }

  return p;
}


on timer timerMeasStart
{
  StartOutput();
}


// obsolet: on linMessage *
// on linFrame *
// {
//   if(@sysAddMsg::nBusType == wAddMsgBusLin)
//   {
//     if(this.ID == @sysAddMsg::nID)
//     {
//       StoreAdditionalMeasurement(this.qword(0));
//     }
//   }
// }


on timer timerMsgTimer
{
  word w;

  output(CommonMessage);

  for(w=0; w<wRelayMsgCntMax; w++)
  {
    if(byRelayMsgActive[w] != 0)
    {
      RelayMessage.SigRelayCtrl_00 = wRelayCtrlWord[4 * w    ];
      RelayMessage.SigRelayCtrl_01 = wRelayCtrlWord[4 * w + 1];
      RelayMessage.SigRelayCtrl_02 = wRelayCtrlWord[4 * w + 2];
      RelayMessage.SigRelayCtrl_03 = wRelayCtrlWord[4 * w + 3];
      RelayMessage.ID = MsgRelayCtrl_00.ID + w;
      output(RelayMessage);
    }
  }

  setTimer(timerMsgTimer, lMsgCycle);
}


void ScalingProfileRead()
{
  @sys_Measure::anDivisor[0] = getProfileInt("SCALING", "Divisor0", 1, "Measure.ini");
  @sys_Measure::anDivisor[1] = getProfileInt("SCALING", "Divisor1", 1, "Measure.ini");
  @sys_Measure::anDivisor[2] = getProfileInt("SCALING", "Divisor2", 1, "Measure.ini");
  @sys_Measure::anDivisor[3] = getProfileInt("SCALING", "Divisor3", 1, "Measure.ini");
  @sys_Measure::anDivisor[4] = getProfileInt("SCALING", "Divisor4", 1, "Measure.ini");
  @sys_Measure::anDivisor[5] = getProfileInt("SCALING", "Divisor5", 1, "Measure.ini");
  @sys_Measure::anDivisor[6] = getProfileInt("SCALING", "Divisor6", 1, "Measure.ini");
  @sys_Measure::anDivisor[7] = getProfileInt("SCALING", "Divisor7", 1, "Measure.ini");
  @sys_Measure::anDivisor[8] = getProfileInt("SCALING", "Divisor8", 1, "Measure.ini");
  @sys_Measure::anDivisor[9] = getProfileInt("SCALING", "Divisor9", 1, "Measure.ini");
}


on sysvar_update sysvar::sys_Control::anAvailableCard
{
  int i;

  for(i=0; i<wCardCntMax; i++)
  {
    if(@sys_Control::anAvailableCard[i] != 0)
    {
      byRelayMsgActive[i/4] = 1;
    }
  }
}


on sysvar_update sysvar::sys_Control::nMeasEnable
{
  CommonMessage.SigCommon_MeasEnable  = @sys_Control::nMeasEnable;
}


void StoreMeasurementWithTimeStamp (word wChannel, int nData0, int nData1, word wTimeStamp, word wTimeOffset)
{
  dword dwTimeStampAbs;
  
  if(wTimeStamp < wTimeStampMem[wChannel])  // overflow of 12 bit time stamp
  {
    dwTimeOffset[wChannel] = dwTimeOffset[wChannel] + wTimeStampRange;
  }

  wTimeStampMem[wChannel] = wTimeStamp;

  dwTimeStampAbs = dwTimeOffset[wChannel] + wTimeStamp;
  // now dwTimeStampAbs contains the absolute time stamp since measurement start

  nMeasValue [wChannel][wPtrIn[wChannel]] = nData0;
  dwTimeStamp[wChannel][wPtrIn[wChannel]] = dwTimeStampAbs;
  wPtrIn[wChannel] = IncBufferPtr(wPtrIn[wChannel]);

  nMeasValue [wChannel][wPtrIn[wChannel]] = nData1;
  dwTimeStamp[wChannel][wPtrIn[wChannel]] = dwTimeStampAbs + wTimeOffset;
  wPtrIn[wChannel] = IncBufferPtr(wPtrIn[wChannel]);

  // STOP measurement if timeout ... !!!
  cancelTimer(timerTimeoutTick);
  setTimer(timerTimeoutTick, lCanTimeout);
  {
  int idx;
  if(idx < 10) { idx++; write("(on message MsgRtMeasure0...9  -> ) StoreMeasurementWithTimeStamp(wChannel='%d') '%d'", wChannel, timerTimeoutTick_Idx++); timerTimeoutTick_wChannel = wChannel; }
  }

  if(byMeasStart == 0)
  {
    byMeasStart = 1;
    setTimer(timerMeasStart, lMeasDelay);
  }

  {
  int idx;
  if(idx < 10) { idx++; write("StartOutput: wChannel=%d, wPtrIn[wChannel]=%d, wBufferOffset=%d", wChannel, wPtrIn[wChannel], wBufferOffset); }
  }
  if(wPtrIn[wChannel] >= wBufferOffset)
  {
    {
    int idx;
    if(idx < 10) { idx++; write("StartOutput"); }
    }
    StartOutput();
  }
}


on message MsgRtMeasure0
{
  // incl.: STOP measurement if timeout ... !!!
  StoreMeasurementWithTimeStamp( 0, $SigRtMeasure0_Data0,     $SigRtMeasure0_Data1,
                                    $SigRtMeasure0_TimeStamp, $SigRtMeasure0_TimeOffset);
}

on message MsgRtMeasure1
{
  StoreMeasurementWithTimeStamp( 1, $SigRtMeasure1_Data0,     $SigRtMeasure1_Data1,
                                    $SigRtMeasure1_TimeStamp, $SigRtMeasure1_TimeOffset);
}

on message MsgRtMeasure2
{
  StoreMeasurementWithTimeStamp( 2, $SigRtMeasure2_Data0,     $SigRtMeasure2_Data1,
                                    $SigRtMeasure2_TimeStamp, $SigRtMeasure2_TimeOffset);
}

on message MsgRtMeasure3
{
  StoreMeasurementWithTimeStamp( 3, $SigRtMeasure3_Data0,     $SigRtMeasure3_Data1,
                                    $SigRtMeasure3_TimeStamp, $SigRtMeasure3_TimeOffset);
}

on message MsgRtMeasure4
{
  StoreMeasurementWithTimeStamp( 4, $SigRtMeasure4_Data0,     $SigRtMeasure4_Data1,
                                    $SigRtMeasure4_TimeStamp, $SigRtMeasure4_TimeOffset);
}

on message MsgRtMeasure5
{
  StoreMeasurementWithTimeStamp( 5, $SigRtMeasure5_Data0,     $SigRtMeasure5_Data1,
                                    $SigRtMeasure5_TimeStamp, $SigRtMeasure5_TimeOffset);
}

on message MsgRtMeasure6
{
  StoreMeasurementWithTimeStamp( 6, $SigRtMeasure6_Data0,     $SigRtMeasure6_Data1,
                                    $SigRtMeasure6_TimeStamp, $SigRtMeasure6_TimeOffset);
}

on message MsgRtMeasure7
{
  StoreMeasurementWithTimeStamp( 7, $SigRtMeasure7_Data0,     $SigRtMeasure7_Data1,
                                    $SigRtMeasure7_TimeStamp, $SigRtMeasure7_TimeOffset);
}

on message MsgRtMeasure8
{
  StoreMeasurementWithTimeStamp( 8, $SigRtMeasure8_Data0,     $SigRtMeasure8_Data1,
                                    $SigRtMeasure8_TimeStamp, $SigRtMeasure8_TimeOffset);
}

on message MsgRtMeasure9
{
  StoreMeasurementWithTimeStamp( 9, $SigRtMeasure9_Data0,     $SigRtMeasure9_Data1,
                                    $SigRtMeasure9_TimeStamp, $SigRtMeasure9_TimeOffset);
}


word CreateRelayCtrlWord (word wCard)
{
  word wRelayCtrlWord;
  word w;

  wRelayCtrlWord = 0;

  for(w=0; w<wChannelsPerCard; w++)
  {
    wRelayCtrlWord = wRelayCtrlWord | @sys_Control::anRelay_OL [4 * wCard + w] << (4 * w    );
    wRelayCtrlWord = wRelayCtrlWord | @sys_Control::anRelay_SC1[4 * wCard + w] << (4 * w + 1);
    wRelayCtrlWord = wRelayCtrlWord | @sys_Control::anRelay_SC2[4 * wCard + w] << (4 * w + 2);
    wRelayCtrlWord = wRelayCtrlWord | @sys_Control::anRelay_SC3[4 * wCard + w] << (4 * w + 3);
  }

  return wRelayCtrlWord;
}


on sysvar sys_User::OlRelayOnOff
{
  word wOnOff = 0;
  word w;

  if(@this)
  {
    wOnOff = !wOnOff;

    for(w=0; w<wCardCntMax; w++)
    {
      if((wOnOff == 0) || (@sys_Control::anAvailableCard[w] != 0))
      {
        @sys_Control::anRelay_OL[4 * w    ] = wOnOff;
        @sys_Control::anRelay_OL[4 * w + 1] = wOnOff;
        @sys_Control::anRelay_OL[4 * w + 2] = wOnOff;
        @sys_Control::anRelay_OL[4 * w + 3] = wOnOff;
      }
    }
  }
}


on sysvar sys_User::StartMeasure
{
  if(@sys_User::StartMeasure)
  {
    @this = 0; // reset
    @sys_Control::nMeasEnable = 1;
  }
}


on sysvar sys_User::StopMeasure
{
  if(@sys_User::StopMeasure)
  {
    @this = 0; // reset
    @sys_Control::nMeasEnable = 0;
  }
}


on sysvar sys_Control::anActiveCard
{
  int i;

  for(i=0; i<wCardCntMax; i++)
  {
    if(@sys_Control::anAvailableCard[i] != 0)  // check only if card present
    {
      if(@sys_Control::anActiveCard[i] != 0)   // card selected
      {
        if(@sys_Control::nActiveCard != i)     // not previously selected
        {
          @sys_Control::nActiveCard = i;       // set new selection
          break;
        }
      }
    }
  }

  for(i=0; i<wCardCntMax; i++)
  {
    if(i == @sys_Control::nActiveCard)
    {
      @sys_Control::anActiveCard[i] = 1;  // set new selection
    }
    else
    {
      @sys_Control::anActiveCard[i] = 0;  // delete invalid selections
    }
  }
}


on sysvar ( sysvar::sys_Control::anRelay_OL 
          | sysvar::sys_Control::anRelay_SC1
          | sysvar::sys_Control::anRelay_SC2
          | sysvar::sys_Control::anRelay_SC3 )
{
  word w;

  for(w=0; w<wCardCntMax; w++)
  {
    if(@sys_Control::anAvailableCard[w] != 0)
    {
      wRelayCtrlWord[w] = CreateRelayCtrlWord(w);
    }
    else
    {
      ClearRelaySysvar(w);
    }
  }
}


ClearRelaySysvar (word wCard)
{
  word w;

  for(w=0; w<wChannelsPerCard; w++)
  {
    @sys_Control::anRelay_OL [4 * wCard + w] = 0;
    @sys_Control::anRelay_SC1[4 * wCard + w] = 0;
    @sys_Control::anRelay_SC2[4 * wCard + w] = 0;
    @sys_Control::anRelay_SC3[4 * wCard + w] = 0;
  }
}


StoreAdditionalMeasurement (qword qwMessage)
{
  word  wChannel;
  dword dwTimeStampAbs;

  wChannel = @sysAddMsg::nChannel;

  if(byMeasActive != 0)
  {
    int  nValue;
    word wLo;
    word wHi;

    wHi = qwMessage >> @sysAddMsg::anBitPos[1];
    wLo = qwMessage >> @sysAddMsg::anBitPos[0];

    wHi = wHi & ((1 << @sysAddMsg::anSize[1]) - 1);
    wLo = wLo & ((1 << @sysAddMsg::anSize[0]) - 1);

    wHi = wHi << (16 -  @sysAddMsg::anSize[1]);
    wLo = wLo << (16 - (@sysAddMsg::anSize[0] + @sysAddMsg::anSize[1]));

    nValue = wHi | wLo;
    nValue = nValue >> (16 - (@sysAddMsg::anSize[0] + @sysAddMsg::anSize[1]));

    dwTimeStampAbs = dwMeasTick + lMeasDelay * 10;  // add measurement delay in 100µs resolution

    nMeasValue [wChannel][wPtrIn[wChannel]] = nValue;
    dwTimeStamp[wChannel][wPtrIn[wChannel]] = dwTimeStampAbs;
    wPtrIn[wChannel] = IncBufferPtr(wPtrIn[wChannel]);
  }
}


on key 'c'
{
  PrepareStepperMeasurement();
}

on sysvar AL_Stepper::PrepareStepperMeasurement
{
  if (@this == 0)
    return;
  @this = 0; // reset

  PrepareStepperMeasurement();
}

void PrepareStepperMeasurement(void)
{
  write("Prepare-Stepper-Measurement: NOW!");

  // 0: 0,1,2,3 - 1:4,5,6,7 - 2:8,9,10,11 - 3:12,13,14,15 - 4:16,17,18,19 - 5:20,21,22,23 - 6:24,25,26,27
  @sys_Control::anRelay_OL[/*4*/24] = 1; // Stp (Card-1)
  @sys_Control::anRelay_OL[/*5*/25] = 1; // Stp
  @sys_Control::anRelay_OL[/*6*/26] = 1; // Stp
  @sys_Control::anRelay_OL[/*7*/27] = 1; // Stp
  // jk-tbd: @sys_Control::anRelay_OL[36] = 1; // ECU-Pwr (Card-9)
  
  @sys_Control::anActiveCard[6] = 1; // -> @sys_Control::nActiveCard = 1;
  
  gPrepareStepperMeasurement = 1;
  cancelTimer(prepareStepperMeasurement);
  setTimer(prepareStepperMeasurement, 100);
}

on timer prepareStepperMeasurement
{
  int idx;
  int SetMeasOutChn_Chn = 0;
  int ready = 0;

  // reset all data-bytes
  for (idx = 2; idx <= 7; idx++)
      DiagReqMessage.byte(idx) = 0;
  
  // Peter Ott:
  // Freq=1kHz           - sys_Diag::Execute: ID=0x780, card=6, DiagCmd=0x13, byte[3]=xE8, byte[4]=x03, byte[5]=x00, byte[6]=x00, byte[7]=x00
  // MeasStpCurr (0|11)  - sys_Diag::Execute: ID=0x780, card=6, DiagCmd=0x14, byte[3]=x00, byte[4]=x0B, byte[5]=x00, byte[6]=x00, byte[7]=x00
  // MeasStpPosAbs (1|9) - sys_Diag::Execute: ID=0x780, card=6, DiagCmd=0x14, byte[3]=x01, byte[4]=x09, byte[5]=x00, byte[6]=x00, byte[7]=x00
  // SetMeasOutEn (3)    - sys_Diag::Execute: ID=0x780, card=6, DiagCmd=0x15, byte[3]=x03, byte[4]=x00, byte[5]=x00, byte[6]=x00, byte[7]=x00
  // SetStpSim (1|1)     - sys_Diag::Execute: ID=0x780, card=6, DiagCmd=0x40, byte[3]=x01, byte[4]=x01, byte[5]=x00, byte[6]=x00, byte[7]=x00
  // StpBlockPos (0|500) - sys_Diag::Execute: ID=0x780, card=6, DiagCmd=0x41, byte[3]=x00, byte[4]=x00, byte[5]=xF4, byte[6]=x01, byte[7]=x00
  // SetStpPos (400|0)   - sys_Diag::Execute: ID=0x780, card=6, DiagCmd=0x30, byte[3]=x90, byte[4]=x01, byte[5]=x00, byte[6]=x00, byte[7]=x00


  ready = 0;
  switch(gPrepareStepperMeasurement)
  {
    case 1:
      @sys_Control::nMeasEnable = 0;
      @sys_User::StopMeasure = 1;
      SetMeasOutChn_Chn = 0;
      break;

    // SetMeasOutFreq(1kHz=1000) = sys_Diag::Execute: ID=0x780, card=0, DiagCmd=0x13, byte[3]=xE8, byte[4]=x03, byte[5]=x00, byte[6]=x00, byte[7]=x00
    case 2:
    //write(" ... (%i): SetMeasOutFreq(1kHz=1000)", gPrepareStepperMeasurement);
      write(" ... (%i): SetMeasOutFreq(1kHz=750)" , gPrepareStepperMeasurement);
      @DiagCmd = 0x13;
      // 1kHz: DiagReqMessage.byte(3) = 0xE8; // 232
      // 1kHz: DiagReqMessage.byte(4) = 0x03;
      DiagReqMessage.byte(3) = 238;
      DiagReqMessage.byte(4) = 2;
      @sys_Diag::Execute = 1;
      break;

    // SetStpSim(on=1, dir=1) = sys_Diag::Execute: ID=0x780, card=0, DiagCmd=0x40, byte[3]=x01, byte[4]=x01, byte[5]=x00, byte[6]=x00, byte[7]=x00
    case 3:
      write(" ... (%i): SetStpSim(on=1, dir=0)", gPrepareStepperMeasurement);
      @DiagCmd = 0x40;
      DiagReqMessage.byte(3) = 0x01; // on
      DiagReqMessage.byte(4) = 0x00; // dir
      @sys_Diag::Execute = 1;
      break;

    // SetMeasOutChn(0,8,0,0)    = sys_Diag::Execute: ID=0x780, card=0, DiagCmd=0x14, byte[3]=x00, byte[4]=x08, byte[5]=x00, byte[6]=x00, byte[7]=x00
    case 4:
      write(" ... (%i): SetMeasOutChn(0,8,0,0) - MeasStpPosRel", gPrepareStepperMeasurement);
      @DiagCmd = 0x14;
      DiagReqMessage.byte(3) = SetMeasOutChn_Chn++; // 0x00; // Meas-Ch: Ch 0&1 -> Rx 0x7C0
      DiagReqMessage.byte(4) = 8; // MeasStpPosRel
      DiagReqMessage.byte(5) = 0x00;
      DiagReqMessage.byte(6) = 0x00;
      @sys_Diag::Execute = 1;
      break;

    // SetMeasOutChn(1,9,0,0)    = sys_Diag::Execute: ID=0x780, card=0, DiagCmd=0x14, byte[3]=x01, byte[4]=x09, byte[5]=x00, byte[6]=x00, byte[7]=x00
    case 5:
      write(" ... (%i): SetMeasOutChn(1,9,0,0) - MeasStpPosAbs", gPrepareStepperMeasurement);
      @DiagCmd = 0x14;
      DiagReqMessage.byte(3) = SetMeasOutChn_Chn++; // Meas-Ch = 0,1,2,3,...
      DiagReqMessage.byte(4) = 9; // MeasStpPosAbs
      DiagReqMessage.byte(5) = 0x00;
      DiagReqMessage.byte(6) = 0x00;
      @sys_Diag::Execute = 1;
      break;

    case 6:
      // Altu-Update pending write(" ... (%i): '@DiagCmd = 0x14 | MeasStpSpeed'", gPrepareStepperMeasurement);
      // Altu-Update pending @DiagCmd = 0x14;
      // Altu-Update pending DiagReqMessage.byte(3) = SetMeasOutChn_Chn++; // Meas-Ch = 0,1,2,3,...
      // Altu-Update pending DiagReqMessage.byte(4) = 12; // MeasStpSpeed
      // Altu-Update pending DiagReqMessage.byte(5) = 0x00;
      // Altu-Update pending DiagReqMessage.byte(6) = 0x00;
      // Altu-Update pending @sys_Diag::Execute = 1;
      break;

    case 7:
      // Altu-Update pending write(" ... (%i): '@DiagCmd = 0x14 | MeasStpCurrent'", gPrepareStepperMeasurement);
      // Altu-Update pending @DiagCmd = 0x14;
      // Altu-Update pending DiagReqMessage.byte(3) = SetMeasOutChn_Chn++; // Meas-Ch = 0,1,2,3,...
      // Altu-Update pending DiagReqMessage.byte(4) = 11; // MeasStpCurrent
      // Altu-Update pending DiagReqMessage.byte(5) = 0x00;
      // Altu-Update pending DiagReqMessage.byte(6) = 0x00;
      // Altu-Update pending @sys_Diag::Execute = 1;
      break;

    case 8:
      write(" ... (%i): '@DiagCmd = 0x14 | MeasCurrNrmSmpl'", gPrepareStepperMeasurement);
      @DiagCmd = 0x14;
      DiagReqMessage.byte(3) = SetMeasOutChn_Chn++; // Meas-Ch = 0,1,2,3,...
      DiagReqMessage.byte(4) = 5; // MeasCurrNrmSmpl
      DiagReqMessage.byte(5) = 0x00; // Input '0' = S1A
      DiagReqMessage.byte(6) = 0x00;
      @sys_Diag::Execute = 1;
      break;

    case 9:
      write(" ... (%i): '@DiagCmd = 0x14 | MeasCurrNrmSmpl'", gPrepareStepperMeasurement);
      @DiagCmd = 0x14;
      DiagReqMessage.byte(3) = SetMeasOutChn_Chn++; // Meas-Ch = 0,1,2,3,...
      DiagReqMessage.byte(4) = 5; // MeasCurrNrmSmpl
      DiagReqMessage.byte(5) = 0x03; // Input '3' = S1B
      DiagReqMessage.byte(6) = 0x00;
      @sys_Diag::Execute = 1;
      break;

    case 10:
      write(" ... (%i): '@DiagCmd = 0x14 | MeasStpSpeed'", gPrepareStepperMeasurement);
      @DiagCmd = 0x14;
      DiagReqMessage.byte(3) = SetMeasOutChn_Chn++; // 0x00; // Meas-Ch: Ch 0&1 -> Rx 0x7C0
      DiagReqMessage.byte(4) = 12; // MeasStpSpeed  - '13' = 'MeasStpAcc'
      DiagReqMessage.byte(5) = 0x00;
      DiagReqMessage.byte(6) = 0x00;
      @sys_Diag::Execute = 1;
      break;

    // SetStpPos(abs=,rel=) =  sys_Diag::Execute: ID=0x780, card=1, DiagCmd=0x30, byte[3]=xF4, byte[4]=x01, byte[5]=xDC, byte[6]=x05, byte[7]=x00
    case 11:
      write(" ... (%i): SetStpPos(abs=500,rel=1500)", gPrepareStepperMeasurement);
      @DiagCmd = 0x30;                // = '48'
      DiagReqMessage.byte(3) = 0xF4;  // = '244' |
      DiagReqMessage.byte(4) = 0x01;  // = '001' |--> '500'
      DiagReqMessage.byte(5) = 0xDC;  // = '220'   |
      DiagReqMessage.byte(6) = 0x05;  // = '005'   |--> '1500'  (1500*16=24000)
      DiagReqMessage.byte(7) = 0x00;  // unused
      @sys_Diag::Execute = 1;
      break;

    // SetStpBlockPos(min,max) = ID=0x780, card=6, DiagCmd=0x41, byte[3]=x00, byte[4]=x00, byte[5]=xF4, byte[6]=x01, byte[7]=x00
    case 12:
      write(" ... (%i): SetStpBlockPos(min=0,max=500)", gPrepareStepperMeasurement);
      @DiagCmd = 0x41;
      DiagReqMessage.byte(3) = 0x00;
      DiagReqMessage.byte(4) = 0x00;
      DiagReqMessage.byte(5) = 0xF4;  // = '244'   |
      DiagReqMessage.byte(6) = 0x01;  // = '001'   |--> '500'
      DiagReqMessage.byte(7) = 0x00;  // unused
      @sys_Diag::Execute = 1;
      break;

    // SetMeasOutEn(1) = ID=0x780, card=0, DiagCmd=0x15, byte[3]=x01, byte[4]=x08, byte[5]=x00, byte[6]=x00, byte[7]=x00
    case 13:
      write(" ... (%i): SetMeasOutEn(x)", gPrepareStepperMeasurement);
      @DiagCmd = 0x15;
      DiagReqMessage.byte(3) = 0x03; // chn 1 & 2
      DiagReqMessage.byte(3) = 0x07; // chn 1 & 2 & 3
      DiagReqMessage.byte(3) = 0x0F; // chn 1 & 2 & 3 & 4 - '0000.1111' - '15'
      // Altu-Update pending   DiagReqMessage.byte(3) = 0x1F; // chn 1 & 2 & 3 & 4 & 5 - '0001.1111' - '31'
      /* Altu-Update pending*/ DiagReqMessage.byte(3) = 0x07; // chn 1 & 2 & 3 - '0000.0111' = '7'
      /* Altu-Update pending*/ DiagReqMessage.byte(3) = 0x0F; // chn 1 & 2 & 3 & 4 - '0000.1111' = '15'
      /* Altu-Update pending*/ DiagReqMessage.byte(3) = 0x1F; // chn 1 & 2 & 3 & 4 & 5 - '0001.1111' = '31'
      @sys_Diag::Execute = 1;
      break;

    // Btn 'Start Measurement': @sys_User::StartMeasure = 1;
    case 14:
      write(" ... (%i): '@sys_User::StartMeasure = 1;'", gPrepareStepperMeasurement);
      @sys_User::StartMeasure = 1;
      break;

    // ready
    case 15:
      ready = 1;
      break;

    default:
      write("unknown state");
      break;
  }
  
  // if (gPrepareStepperMeasurement <= 9)
  if (ready == 0)
  {
    gPrepareStepperMeasurement++;
    setTimer(prepareStepperMeasurement, 100);
  }
}


on sysvar sys_User::Adapt
{
  if(@sys_User::Adapt)
  {
    fDispOffset[@sys_Measure::nAdaptDest] = @sys_Measure::afOutput[@sys_Measure::nAdaptSrc]
                                          - @sys_Measure::afOutput[@sys_Measure::nAdaptDest];
  }
}


void ScalingProfileWrite()
{
  writeProfileInt("SCALING", "Divisor0", @sys_Measure::anDivisor[0], "Measure.ini");
  writeProfileInt("SCALING", "Divisor1", @sys_Measure::anDivisor[1], "Measure.ini");
  writeProfileInt("SCALING", "Divisor2", @sys_Measure::anDivisor[2], "Measure.ini");
  writeProfileInt("SCALING", "Divisor3", @sys_Measure::anDivisor[3], "Measure.ini");
  writeProfileInt("SCALING", "Divisor4", @sys_Measure::anDivisor[4], "Measure.ini");
  writeProfileInt("SCALING", "Divisor5", @sys_Measure::anDivisor[5], "Measure.ini");
  writeProfileInt("SCALING", "Divisor6", @sys_Measure::anDivisor[6], "Measure.ini");
  writeProfileInt("SCALING", "Divisor7", @sys_Measure::anDivisor[7], "Measure.ini");
  writeProfileInt("SCALING", "Divisor8", @sys_Measure::anDivisor[8], "Measure.ini");
  writeProfileInt("SCALING", "Divisor9", @sys_Measure::anDivisor[9], "Measure.ini");
}


on sysvar sys_Diag::Arg8
{
  DiagReqMessage.byte(3) = @sys_Diag::Arg8[0];
  DiagReqMessage.byte(4) = @sys_Diag::Arg8[1];
  DiagReqMessage.byte(5) = @sys_Diag::Arg8[2];
  DiagReqMessage.byte(6) = @sys_Diag::Arg8[3];
  DiagReqMessage.byte(7) = @sys_Diag::Arg8[4];

  @sys_Diag::Arg16[0] = DiagReqMessage.int(3);
  @sys_Diag::Arg16[1] = DiagReqMessage.int(5);
}


on sysvar sys_Diag::Arg16
{
  DiagReqMessage.int(3) = @sys_Diag::Arg16[0];
  DiagReqMessage.int(5) = @sys_Diag::Arg16[1];

  @sys_Diag::Arg8[0] = DiagReqMessage.byte(3);
  @sys_Diag::Arg8[1] = DiagReqMessage.byte(4);
  @sys_Diag::Arg8[2] = DiagReqMessage.byte(5);
  @sys_Diag::Arg8[3] = DiagReqMessage.byte(6);
}


on envVar MeasType
{
  @sys_Diag::Arg8[1] = getValue(MeasType);
}


on sysvar sys_Diag::Execute
{
  if(@this)
  {
    @sys_Diag::Execute = 0; // reset at once

    write("sys_Diag::Execute: ID=0x%03X, card=%d, DiagCmd=0x%02X, byte[3]=x%02X, byte[4]=x%02X, byte[5]=x%02X, byte[6]=x%02X, byte[7]=x%02X"
                            , mDiag_Req_00.ID, @sys_Control::nActiveCard, @DiagCmd
                            , DiagReqMessage.byte(3), DiagReqMessage.byte(4), DiagReqMessage.byte(5), DiagReqMessage.byte(6), DiagReqMessage.byte(7));
    DiagReqMessage.ID      = mDiag_Req_00.ID + @sys_Control::nActiveCard;
    DiagReqMessage.byte(0) = 0x07;
    DiagReqMessage.byte(1) = 0xBA;
    DiagReqMessage.byte(2) = getValue(DiagCmd);

    output(DiagReqMessage);

    @sys_Diag::CmdSuccess = 0;
  }
}


EvaluateDiagResponse (int nCard)
{
  char buffer[500];

  @sys_Diag::Ret8[0]  = DiagResMessage.byte(2);
  @sys_Diag::Ret8[1]  = DiagResMessage.byte(3);
  @sys_Diag::Ret8[2]  = DiagResMessage.byte(4);
  @sys_Diag::Ret8[3]  = DiagResMessage.byte(5);
  @sys_Diag::Ret8[4]  = DiagResMessage.byte(6);
  @sys_Diag::Ret8[5]  = DiagResMessage.byte(7);

  @sys_Diag::Ret16[0] = DiagResMessage.int(2);
  @sys_Diag::Ret16[1] = DiagResMessage.int(4);
  @sys_Diag::Ret16[2] = DiagResMessage.int(6);

  @sys_Diag::CmdSuccess = ! @sys_Diag::Ret8[0];
  
  buffer[0] = 0;
  if (@sys_Diag::Ret16[0] != 0) // error?
    snprintf(buffer, elCount(buffer), "*#*#*#* ERROR *#*#*#* ");
  snprintf(buffer, elCount(buffer), "%sEvaluateDiagResponse: card=%d, ret16[0] = 0x%X, ret16[1]= 0x%02X = %d"
                          , buffer, nCard
                          , @sys_Diag::Ret16[0], @sys_Diag::Ret16[1], @sys_Diag::Ret16[1]);
  write(buffer);
}


// tbd-confusing on key *
// tbd-confusing {
// tbd-confusing   if((this >= '0') && (this <= '9'))
// tbd-confusing   {
// tbd-confusing     @sys_Control::anActiveCard[this - '0'] = 1;
// tbd-confusing   }
// tbd-confusing }

// EOF
