variables
{
  // General consts
  const int kNameLen = 512;             // Used as length for character arrays

  const int cTextBufferLength = 512;    // For the textual status query

  // For the panel-representation of statistic values, that are independent to check notifications  
  const kCycStatUpdateTime = 500;       // Cycle time update of the panel [ms]
  MsTimer tCycStatUpdate;               // Timer for the cyclic output of the statistic values

  // Remember the handles for the different checks:
  dword mCycCheckId = 0;                // cycle time violation check-handle
  dword mSigCheckId = 0;                // signal value violation check-handle
  dword mDistCheckId = 0;               // message distance violation check-handle
  dword mNodeCheckId = 0;               // node dead check-handle
  dword mNodeBabblingCheckId = 0;       // node dead check-handle
  dword mUndefineMessageCheckId = 0;    // node dead check-handle
  dword mMessageSendCountCheckId = 0;   // node dead check-handle
  dword mDLCCheckId = 0;                // DLC invalid check-handle

  /* 
   * Description of the architecture of the demo: See comment for CANoe-Configuration
   *
   * Description of Test-Node implementation:
   * - User interactions (buttons, switches) are handled with the environment-handlers.
   *   There are 2 types: ...ApplyParameters() are the handlers for the apply-buttons of the check panels
   *                      ...TestActive() are the handlers for the main switches of the checks panels
   * - Checks are setup in the ...SetupCheck()-functions. 
   *   Here all check parameters are retrieved from the environment and supplied during creation of checks
   *   These functions will do nothing in the case of already existing checks.
   * - Checks are destroyes in the ...TerminateCheck()-functions.
   *   These functions will do nothing in the case of not existing checks.
   * - All check violations are reported in the ...Callback()-functions.
   *   There, further information about the problem is queried and output
   *   to the write window and the panels.
   * - The cyclic update of the statistic (only used for the cycle time violation check)
   *   is done with the single timer-handler.
   * - Default values for the check parameters are retrieved from the network DB
   */

  int NodeTestActive = 0; 
  int gSutStatus = 0;

}

on preStart
{
  // Initialize the DTF functionality.
  // Dependent to the development progress of the test,
  // DTF should be initialized with "user" or "developer".
  // The setting "user" is very silent and reports only severe problems.
  // The setting "developer" is more verbose and reports useful information

  ChkConfig_Init("user");
}

on start
{
  /**************************************************************************
   * Publish all environment vars, that should be reset on a new measurement
   * (and that may not retain the values from the last measurement)
   */

  // For the cycle time check
  @sysvar::DirectTest_TN::TNCycleNumberBadCycles = 0;  

  // For the signal value check
  @sysvar::DirectTest::SigCountProblems = 0;
  SysSetVariableString(sysvar::DirectTest_TN::TNCycleCurrentOk, "not checked");

  // For the message distance violation check
  @sysvar::DirectTest_TN::TNDistCountProblems = 0;

  /**************************************************************************
   * Setup the demo-application functionality
   */

  // Set (and start) the timer that triggers the update of the statistic part of the
  // cycle time check panel
  SetTimer(tCycStatUpdate, kCycStatUpdateTime);

  enableControl("Test Control: Node Check","Offline",1); 
  enableControl("Test Control: Node Check","Online",0);
}

CycSetupCheck ()
{
  // Setup the check and start it
  float lCycMinCycleTimeRel;  // Minimum relative distance 
  float lCycMaxCycleTimeRel;  // Maximum relative distance

  if(!mCycCheckId)      // Setup a check only, if there is none
  {
    // Now do the check setup:
    // Read the check parameters from the environment (check panel)
    lCycMinCycleTimeRel = @sysvar::DirectTest_TN::TNCycleTimeRelMin;
    lCycMaxCycleTimeRel = @sysvar::DirectTest_TN::TNCycleTimeRelMax;

    mCycCheckId = ChkStart_MsgRelCycleTimeViolation(CycleMsg,             // Message to supervise
                                                    lCycMinCycleTimeRel,  // min-limit
                                                    lCycMaxCycleTimeRel,  // max-limit
                                                    "CycCallback");       // CAPL callback for violation notification

    // New check, new count:
    @sysvar::DirectTest_TN::TNCycleNumberBadCycles = 0;
    SysSetVariableString(sysvar::DirectTest_TN::TNCycleLastBadCycleTime, "");
    SysSetVariableString(sysvar::DirectTest_TN::TNCycleCurrentOk, "checked");
  }
}

CycTerminateCheck ()
{
  if(mCycCheckId)  // Destroy the check only, if there is one
  {
    ChkControl_Destroy(mCycCheckId); // Destroy the check
    mCycCheckId = 0;
    SysSetVariableString(sysvar::DirectTest_TN::TNCycleCurrentOk, "not checked");   // re-initialize the value as ok, if the check is active.
  }
}

DistTerminateCheck ()
{
  if(mDistCheckId)    // Destroy the check only, if there is one
  {
    ChkControl_Destroy(mDistCheckId);
    mDistCheckId = 0;
  }
}

DistSetupCheck ()
{
  long lDistMinDistTimeAbs;  // Minimum timely distance (absolute [ms])
  long lDistMaxDistTimeAbs;  // Maximum timely distance (absolute [ms])

  if(!mDistCheckId)      // Setup a check only, if there is none
  {
    // Read the check parameters from the environment (the check panel)
    lDistMinDistTimeAbs = @sysvar::DirectTest_TN::TNDistTimeAbsMin;
    lDistMaxDistTimeAbs = @sysvar::DirectTest_TN::TNDistTimeAbsMax;

    mDistCheckId = ChkStart_MsgDistViolation(RequestMsg,          // Request message
                                             ResponseMsg,         // Response message
                                             lDistMinDistTimeAbs, // minimum time distance between req. and resp.
                                             lDistMaxDistTimeAbs, // maximum time distance between req. and resp.
                                             "DistCallback");     // CAPL callback for violation notification

    // New check, new count:
    @sysvar::DirectTest_TN::TNDistCountProblems = 0;
  }
}

SigTerminateCheck ()
{
  if(mSigCheckId)    // Destroy the check only, if there is one
  {
    ChkControl_Destroy(mSigCheckId);
    mSigCheckId = 0;
  }
}

SigSetupCheck ()
{
  long lSigMinValue;    // Minimum allowed signal value
  long lSigMaxValue;    // Maximum allowed signal value

  if(!mSigCheckId)      // Setup a check only, if there is none
  {
    // Read the check paramters from the environment (the check panel)
    lSigMinValue = @sysvar::DirectTest_TN::TNSigMinValue;
    lSigMaxValue = @sysvar::DirectTest_TN::TNSigMaxValue; 

    // Create and start the check
    mSigCheckId = ChkStart_MsgSignalValueRangeViolation(MsgValue::SigValue, // Signal to supervise
                                                        lSigMinValue,       // Minimum allowed value
                                                        lSigMaxValue,       // Maximum allowed value
                                                        "SigCallback");     // CAPL callback for violation notification
    // New check, new count:
    @sysvar::DirectTest::SigCountProblems = 0;
  }
}

CycUpdateStatistics ()
{
  // Query some useful statistical values and publish them to the panel

  long lQueryResult;

  if(!mCycCheckId)   // If the check-id is not set
    return;

  if(ChkQuery_NumEvents(mCycCheckId) >= 0)  // Condition is true, if the check for the id exists
  {                                         // In this case, all further queries will result correct values
    /* 
     * Let's query the statistic values and output them to the check panel one by the other
     */

    lQueryResult = ChkQuery_StatEventFreePeriodAvg(mCycCheckId);
    @sysvar::DirectTest_TN::TNCycleAvg = lQueryResult;

    lQueryResult = ChkQuery_StatEventFreePeriodMin(mCycCheckId);
    @sysvar::DirectTest_TN::TNCycleMin = lQueryResult;

    lQueryResult = ChkQuery_StatEventFreePeriodMax(mCycCheckId);
    @sysvar::DirectTest_TN::TNCycleMax = lQueryResult;

    lQueryResult = ChkQuery_StatEventFreePeriodMed(mCycCheckId);
    @sysvar::DirectTest_TN::TNCycleMed = lQueryResult;
  }
}

on timer tCycStatUpdate
{
  /*
   * This function updates the panel of the cycle time violation check cyclically
   * and resets the timer for the cyclic update.
   *
   * Note: The timer is not needed for the check functionality. It is only used
   *       for the panel update during measurement when the check does not report errors.
   */

  CycUpdateStatistics();
  CycUpdateProbeStatistics();
  SetTimer(this, kCycStatUpdateTime);
}

CycUpdateProbeStatistics ()
{
  long lQueryResult;

  if(mCycCheckId)
  {
    lQueryResult = ChkQuery_StatNumProbes(mCycCheckId);

    if(lQueryResult > 0)
    {
      SysSetVariableString(sysvar::DirectTest_TN::TNCycleCurrentProbeOk, "probed");   

      @sysvar::DirectTest_TN::TNCycleNumberProbeCycles = lQueryResult;

      // All subsequent queries are only relevant, if there are some probes available
      lQueryResult = ChkQuery_StatProbeIntervalAvg(mCycCheckId);
      if(lQueryResult >= 0)
        @sysvar::DirectTest_TN::TNCycleProbeAvg = lQueryResult;
      else
        @sysvar::DirectTest_TN::TNCycleProbeAvg = 0;

      lQueryResult = ChkQuery_StatProbeIntervalMin(mCycCheckId);
      if(lQueryResult >= 0)
        @sysvar::DirectTest_TN::TNCycleProbeMin = lQueryResult;
      else
        @sysvar::DirectTest_TN::TNCycleProbeMin = 0;
      

      lQueryResult = ChkQuery_StatProbeIntervalMax(mCycCheckId);
      if(lQueryResult >= 0)
        @sysvar::DirectTest_TN::TNCycleProbeMax = lQueryResult;
      else
        @sysvar::DirectTest_TN::TNCycleProbeMax = 0;

    }
  }
}

NodeBabblingCheck()
{
  message TNOffOnMsg msgTNOffOnMsg; 

  // Create and start the check
  mNodeBabblingCheckId = ChkStart_NodeBabbling(DirectTestDemo::SUT,                  // Node to supervise
                                               @sysvar::DirectTest_TN::TNBabblingMaxQuietTime,  // Maximum allowed value
                                              "NodeBabblingCallback");               // CAPL callback for violation notification
}

on stopMeasurement
{
}

TerminateNodeNodeBabblingCheck ()
{
  message TNOffOnMsg msgTNOffOnMsg; 

  ChkControl_Destroy(mNodeBabblingCheckId); // Destroy the check
  mNodeBabblingCheckId = 0;
  
  msgTNOffOnMsg.Offline = 0x00;
  msgTNOffOnMsg.Online  = 0x01;
  output(msgTNOffOnMsg);
}

NodeDeadCheck()
{
  // Create and start the check
  mNodeCheckId = ChkStart_NodeDead(DirectTestDemo::SUT,                 // Node to supervise
                                   @sysvar::DirectTest_TN::TNDeadMaxQuietTime,     // Maximum allowed value
                                   "NodeDeadCallback");                 // CAPL callback for violation notification 
}

TerminateNodeDeadCheck ()
{
  ChkControl_Destroy(mNodeCheckId); // Destroy the check
  mNodeCheckId = 0;
}

UndefinedMessageReceivedCallback (dword aCheckId)
{
  long   lEventUndefineMessageName;             
  long   lEventUndefineMessageId;             
  char   lEventMsgName[512];

  /* 
   * This function is called if the node SUT check reports a problem.
   * So we have the possibility to resolve further information about the problem
   * and report this information to the check panel.
   */

  /* ******************************************************************
   * Update the write window
   */

   ChkQuery_EventStatusToWrite(aCheckId);   // Put the textual status to the write window


  /* ******************************************************************
   * Update the check panel
   */

  // Event message name
  lEventUndefineMessageName = ChkQuery_EventMessageName (aCheckId, lEventMsgName, cTextBufferLength);
  SysSetVariableString(sysvar::DirectTest_TN::TNUndefineMessageName,lEventMsgName); 

  // Event message ID
  lEventUndefineMessageId = ChkQuery_EventMessageId(aCheckId);
  @sysvar::DirectTest_TN::TNEventUndefineMessageId = lEventUndefineMessageId; 
}

UndefineMessageCheck ()
{
  if(!mUndefineMessageCheckId)      // Setup a check only, if there is none
  {
    // Create and start the check
    mUndefineMessageCheckId = ChkStart_UndefinedMessageReceived("UndefinedMessageReceivedCallback");
  }
}

TerminateUndefineMessageCheck ()
{
  if(mUndefineMessageCheckId)    // Destroy the check only, if there is one
  {
    ChkControl_Destroy(mUndefineMessageCheckId);
    mUndefineMessageCheckId = 0;
  }
}

TerminateMsgSendCountCheck ()
{
  // Enabled panel controls  
  enableControl("Test Control: Message Send Count Violation","Off",1); 

  ChkControl_Destroy(mMessageSendCountCheckId); // Destroy the check
  mMessageSendCountCheckId = 0;
}

DLCSetupCheck ()
{
  long lDistMinDistTimeAbs;  // Minimum timely distance (absolute [ms])
  long lDistMaxDistTimeAbs;  // Maximum timely distance (absolute [ms])

  if(!mDLCCheckId)      // Setup a check only, if there is none
  {
    // Read the check parameters from the environment (the check panel)
    mDLCCheckId = ChkStart_InconsistentTxDlc(SUT, "DLCCallback");

    // New check, new count:
    @sysvar::DirectTest_TN::TNDLCNumberBadMsgs = 0;
  }
  
}

DLCTerminateCheck ()
{
  if(mDLCCheckId)    // Destroy the check only, if there is one
  {
    ChkControl_Destroy(mDLCCheckId);
    mDLCCheckId = 0;
  }
}

CycCallback (dword aCheckId)
{
  /* 
   * This function is called if the cycle time violation check reports a problem.
   * So we have the possibility to resolve further information about the problem
   * and report this information to the write-window and the check panel.
   */

  // Local variables:
  double lQueryResult;                // Result of the last query. 
                                      // May be an error code or the queried value
  char   lLastBadCycleTime[kNameLen]; // Textual representation of the last bad cycle time
                                      // Textual, because strings like "not available" are shown also
  long   lCountProblems;              // Count of occured problems

  /* ******************************************************************
   * Update the write window
   */

  write("There is a Cycle time violation callback for check id %d", aCheckId);
  ChkQuery_EventStatusToWrite(aCheckId);

  /* ******************************************************************
   * Update the check panel
   */

  // a: Number of events
  lCountProblems = ChkQuery_NumEvents(aCheckId);
  @sysvar::DirectTest_TN::TNCycleNumberBadCycles = lCountProblems;

  // b: Event interval
  lQueryResult = ChkQuery_EventInterval(aCheckId);

  // This query may fail in the case that the event interval was terminated by the check
  // supervision and not by an external event. This occurs if the check has waited
  // and the maximum waiting time has exceeded.

  if(lQueryResult > 0)
  {
    // Query was successful -> event interval was too short
    snprintf(lLastBadCycleTime, kNameLen, "%.1f", lQueryResult);
  }
  else
  {
    // Query was not successful -> so the event interval is not available.
    // For this demo application, it means that the cycle time was too long.
    snprintf(lLastBadCycleTime, kNameLen, "not available");
  }

  // Print the information into the check panel
  SysSetVariableString(sysvar::DirectTest_TN::TNCycleLastBadCycleTime, lLastBadCycleTime);
}

DistCallback (dword aCheckId)
{
  /* 
   * This function is called if the message distance violation check reports a problem.
   * So we have the possibility to resolve further information about the problem
   * and report this information to the write-window and the check panel.
   */

  // Local variables
  long   lCountProblems;              // Count of occured problems

  /* ******************************************************************
   * Update the write window
   */

  ChkQuery_EventStatusToWrite(aCheckId);   // Put the textual status to the write window

  /* ******************************************************************
   * Update the check panel
   */

  // a: Number of events
  lCountProblems = ChkQuery_NumEvents(aCheckId);
  @sysvar::DirectTest_TN::TNDistCountProblems = lCountProblems;

}

DLCCallback (dword aCheckId)
{
  long   lEventDLCInvalidMessageId;             

  /* 
   * This function is called if the node SUT check reports a problem.
   * So we have the possibility to resolve further information about the problem
   * and report this information to the check panel.
   */

  /* ******************************************************************
   * Update the write window
   */

   ChkQuery_EventStatusToWrite(aCheckId);   // Put the textual status to the write window


  /* ******************************************************************
   * Update the check panel
   */

  // Event message ID
  @sysvar::DirectTest_TN::TNDLCLastBadMsg = ChkQuery_EventMessageId(aCheckId); 
  @sysvar::DirectTest_TN::TNDLCNumberBadMsgs = ChkQuery_NumEvents(aCheckId);
}

NodeBabblingCallback (dword aCheckId)
{
  long   lEventInterval;             
  long   lEventMessageId;             
  long   lQueryResultOnMsgName = 0;
  long   lCountProblems;
  char   lEventMsgName[512];

  /* 
   * This function is called if the node SUT check reports a problem.
   * So we have the possibility to resolve further information about the problem
   * and report this information to the check panel.
   */


  /* ******************************************************************
   * Update the check panel
   */

  // Event interval
  lEventInterval = ChkQuery_EventInterval(aCheckId);
  @sysvar::DirectTest_TN::TNEventInterval = lEventInterval; 

  // Event message ID
  lEventMessageId = ChkQuery_EventMessageId(aCheckId); 

  // Event message name
  lQueryResultOnMsgName = ChkQuery_EventMessageName (aCheckId, lEventMsgName, cTextBufferLength);
  SysSetVariableString(sysvar::DirectTest_TN::TNEventMessageName,lEventMsgName); 

  // Number of problems
  lCountProblems = ChkQuery_NumEvents(aCheckId);
  @sysvar::DirectTest_TN::TNEventCountProblems = lCountProblems; 


  /* ******************************************************************
   * Update the write window
   */
  ChkQuery_EventStatusToWrite(aCheckId);
}

NodeDeadCallback (dword aCheckId)
{
  /* 
   * This function is called if the node SUT check reports a problem.
   * So we have the possibility to resolve further information about the problem
   * and report this information to the check panel.
   */
  long   lCountProblems;


  /* ******************************************************************
   * Update the write window
   */

  ChkQuery_EventStatusToWrite(aCheckId);   // Put the textual status to the write window

  /* ******************************************************************
   * Update the check panel
   */

  // Number of problems
  lCountProblems = ChkQuery_NumEvents(aCheckId);
  @sysvar::DirectTest_TN::TNEventNodeDeadCountProblems = lCountProblems; 
}

SigCallback (dword aCheckId)
{
  /* 
   * This function is called if the signal value violation check reports a problem.
   * So we have the possibility to resolve further information about the problem
   * and report this information to the write-window and the check panel.
   */

  // Local variables
  double lQueryResult;                // Result of the last query. 
                                      // May be an error code or the queried value
  char   lLastBadSigValue[kNameLen];  // Textual representation of the last bad cycle time
                                      // Textual, because strings like "not available" are shown also
  long   lCountProblems;              // Count of occured problems

  /* ******************************************************************
   * Update the write window
   */

  write("There is a signal value violation for check id %d", aCheckId);
  ChkQuery_EventStatusToWrite(aCheckId);

  /* ******************************************************************
   * Update the check panel
   */

  // a: Number of events
  lCountProblems = ChkQuery_NumEvents(aCheckId);
  @sysvar::DirectTest::SigCountProblems = lCountProblems;

  // b: Ocurred signal value that has led to the event
  lQueryResult = ChkQuery_EventSignalValue(aCheckId);

  // This query returns also negative values in the case that the signal supervision interval
  // is (partly) negative. 
  // If it is, then a more complex query that separates the query validity and the signal value, can be used.

  if(lQueryResult >= 0)
  {
    // Query was sucessful (for not negative signal value ranges)
    snprintf(lLastBadSigValue, kNameLen, "%.1f", lQueryResult);
  }
  else
  {
    snprintf(lLastBadSigValue, kNameLen, "not available", lQueryResult);
  }

  SysSetVariableString(sysvar::DirectTest_TN::TNSigLastBadSignalValue, lLastBadSigValue);
}

on sysvar_update sysvar::DirectTest_TN::TNCycleApplyParamters
{
  /*
   * This function is called, of the uses presses the "Apply"-Button on the check panel.
   * The effect is, that an already setup check applies new check parameters.
   * If there is no check active, then the button is without effect.
   */

  if(@this && mCycCheckId) // button pressed and check active?
  {
    CycTerminateCheck();
    CycSetupCheck();
  }
}

on sysvar_update sysvar::DirectTest_TN::TNCycleTimeTestActive
{
  /*
   * This function is called, if the main switch for the check has changed.
   * Either the check is switched on, or off.
   */

  if(@this)      // Should the test be active (switch is on)?
  {
    CycSetupCheck();      // If there is already a check, the method ignores the call
  }
  else
  {
    CycTerminateCheck();  // If there is already no check, the method ignores the call
  }
}

on sysvar_update sysvar::DirectTest_TN::TNDistApplyParameters
{
  /*
   * This function is called, of the uses presses the "Apply"-Button on the check panel.
   * The effect is, that an already setup check applies new check parameters.
   * If there is no check active, then the button is without effect.
   */

  if(@this && mDistCheckId) // button pressed and check active?
  {
    DistTerminateCheck();
    DistSetupCheck();
  }
}

on sysvar_update sysvar::DirectTest_TN::TNDistTestActive
{
  /*
   * This function is called, if the main switch for the check has changed.
   * Either the check is switched on, or off.
   */

  if(@this)      // Should the test be active (switch is on)?
  {
    DistSetupCheck();     // If there is already a check, the method ignores the call
  }
  else
  {
    DistTerminateCheck(); // If there is already no check, the method ignores the call
  }
}

on sysvar_update sysvar::DirectTest_TN::TNDLCActive
{
  if(@this)      // Should the test be active (switch is on)?
  {
    DLCSetupCheck();     // If there is already a check, the method ignores the call
  }
  else
  {
    DLCTerminateCheck(); // If there is already no check, the method ignores the call
  }
}

on sysvar_update sysvar::DirectTest_TN::TNNodeBabblingTestActive
{
  /*
   * This function is called, if the main switch for the check has changed.
   * Either the check is switched on, or off.
   */

  if(@this)      // Should the test be active (switch is on)?
  {
    NodeBabblingCheck();       // If there is already a check, the method ignores the call
    putValueToControl("Test Control: Node babbling check", "CAPLOutputView1", "SUT is now checked for going offline.\nPlease, operate the SUT accordingly onto\nthe SUT-Panel.");
  }
  else
  {
    TerminateNodeNodeBabblingCheck();  // If there is already no check, the method ignores the call
    putValueToControl("Test Control: Node babbling check", "CAPLOutputView1", "");     
  }
}

on sysvar_update sysvar::DirectTest_TN::TNNodeDeadTestActive
{
  /*
   * This function is called, if the main switch for the check has changed.
   * Either the check is switched on, or off.
   */

  if(@this)      // Should the test be active (switch is on)?
  {
    NodeDeadCheck();      // If there is already a check, the method ignores the call
    putValueToControl("Test Control: Node dead check", "CAPLOutputView2", "SUT is now checked for being online.\nPlease operate the SUT accordingly onto\nthe SUT-Panel.");
  }
  else
  {
    TerminateNodeDeadCheck();  // If there is already no check, the method ignores the call
    putValueToControl("Test Control: Node dead check", "CAPLOutputView2", "");
  }
}

on sysvar_update sysvar::DirectTest_TN::TNSigApplyParameters
{
  /*
   * This function is called, of the uses presses the "Apply"-Button on the check panel.
   * The effect is, that an already setup check applies new check parameters.
   * If there is no check active, then the button is without effect.
   */

  if(@this && mSigCheckId)  // button pressed and check active?
  {
    SigTerminateCheck();
    SigSetupCheck();
  }
}

on sysvar_update sysvar::DirectTest_TN::TNSigTestActive
{
  /*
   * This function is called, if the main switch for the check has changed.
   * Either the check is switched on, or off.
   */

  if(@this)      // Should the test be active (switch is on)?
  {
    SigSetupCheck();      // If there is already a check, the method ignores the call
  }
  else
  {
    SigTerminateCheck();  // If there is already no check, the method ignores the call
  }
}

on sysvar_update sysvar::DirectTest_TN::TNUndefineMessageActive
{
  /*
   * This function is called, if the main switch for the check has changed.
   * Either the check is switched on, or off.
   */

  if(@this)                  // Should the test be active (switch is on)?
  {
    UndefineMessageCheck();           // If there is already a check, the method ignores the call
  }
  else
  {
    TerminateUndefineMessageCheck();  // If there is already no check, the method ignores the call
  }
}

