/*@@var:*/
/*
 * Version 1.5
 *
 * Copyright 2010 - Vector Informatik GmbH
 *
 * This CAPL model simulates a File Server client ECU (ISO11783 Part 13)
 *
 * Dependencies
 *       Nodelayer DLLs ISO11783DLL.dll
 *       ISO11783.dbc database
 *       FileServerClient.dbc database
 *       FileServerBrowser.cnp Panel
 *
 * History
 * 1.0   2004-09-17 Jr, Created
 * 1.1   2004-04-28 Jr, Changed PGN of FSClient and FSServer
 * 1.2	 2005-11-24 Pr, Adapted to meet draft international standard ISO/DIS 11783-13
 * 1.3   2007-06-29 Gw, Remove some compiler warnings
 * 1.4   2007-11-12 Jr, Updated for FDIS
 * 1.4   2010-11-12 Gw, Update version in Client Connection Maintenance
 *
 */
variables
{
  //
  // Constants
  //

  const BYTE kNullAddr           = 0xfe;   // Null address
  const BYTE kGlobalAddr         = 0xff;   // Global address
  const LONG kSuccess            = 0;      // Function was executed successfully
  const LONG kMaxPath            = 256;    // Max. length of a file path

  const int kNotConnected        = 0;      // State: not connected
  const int kStarting            = 1;      // State: ECU is claiming an address
  const int kOperational         = 2;      // State: operational

  //
  // Global variables for communication
  //
  int   gECUAddress   = kNullAddr;         // Address of the WSM
  char  gECULabel[32] = "FSBrowser";        // Label for output to write window
  char  gECUBus[20]   = "default";
  int   gECUState     = kNotConnected;     // State of the communication state machine of this WSM
  dword gECUHandle;                        // ECU handle
  char  gECUName[8];                       // Device name of this ECU
  long  ci, i, l, k;                       // index for 'for' loops
  long  result;                            // return value of Nodelayer functions
  byte 	gNewDir = 0;

  // File Server access variables
  BYTE        FS_Address      = kNullAddr; // Address of the File Server ECU
  LONG        FS_FileHandle   = 0;         // Handle of the opened file or directory
  DWORD       FS_LastStatus   = 0;         // Time of last received status message
  BYTE        FS_TAN          = 0;         // Transaction number
  BYTE        FS_RequestState = 0;         // State of the request, 0=no request in process, 1=request sending, 2=waiting for response
  msTimer     FS_MaintainTimer;            // Time for maintenance message
  msTimer     FS_TimeoutTimer;             // Time for maintenance message
  pg FSServer FS_Response = { DLC = 1785 };                 // Status PG
  pg FSClient FS_Request = { DLC = 1785 };    // Last received File Server Command

  char        gPanelName[32] = "File Server Browser"; // Title of the panel
  char        gFileNames[100][14];                    // File names
  dword       gFileInfo[100][8];                      // File attributes, size, date & time
  long        gFileCount     = 0;                     // Number of files in gFileName and gFileInfo
  long        gScrollPos     = 0;

  // Definition of debugging constants
  const int kDbgInfo    = 10;
  const int kDbgWarning = 5;
  const int kDbgError   = 1;
  const int kDbgQuiet   = 0;

  // General global variables
  int gDbgLevel         = kDbgWarning; // Set debug level for output to write window
}
/*@@end*/

/*@@preStart:PreStart:*/
on preStart
{
  setWriteDbgLevel( gDbgLevel );

  EcuInit(); // initialize global variables
}
/*@@end*/

/*@@startStart:Start:*/
on start
{
  if ((getValue( EvISOBUS_ECU_PWR ) > 10.0) && (getValue(EvFSB_Connection))) {
    EcuStartUp();
  }
}
/*@@end*/

/*@@timer:FS_MaintainTimer:*/
/*
 * File Server Maintenance Timer
 */
on timer FS_MaintainTimer
{
  char maintainData[8] = { 0, -1, -1, -1, -1, -1, -1, -1 }; // -1 is equal to 0xff

  // Version number
  maintainData[1] = 3; // First published edition and Amd1 of the International Standard

  if ((gECUState == kOperational) && (FS_Address != kNullAddr)) {
    if (FS_LastStatus + 600000 > timeNow()) { // timeout after 6 sec.
      Iso11783TxReqPG( gECUHandle, FS_Request.pgn, FS_Address, 7, 8, maintainData ); 

      setTimer( FS_MaintainTimer, 2000 );
    }
    else {
      // missing Status message from File Server for more than 6 seconds
      FS_Address      = kNullAddr;
      FS_RequestState = 0; 
      cancelTimer( FS_TimeoutTimer );
    }
  }
}
/*@@end*/

/*@@caplFunc:EcuInit():*/
/*
 * Initialize all global variables in this function.
 */
void EcuInit()
{
  int l;

  //
  // Initialize the global variables
  //
  gECUAddress     = kNullAddr;
  gECUState       = kNotConnected;
  gECUHandle      = 0;

  // set working set master name
  Iso11783MakeName( gECUName, 
                    FSBrowser.NmJ1939AAC,
                    FSBrowser.NmJ1939IndustryGroup,
                    FSBrowser.NmJ1939SystemInstance,
                    FSBrowser.NmJ1939System,
                    FSBrowser.NmJ1939Function,
                    FSBrowser.NmJ1939FunctionInstance,
                    FSBrowser.NmJ1939ECUInstance,
                    FSBrowser.NmJ1939ManufacturerCode,
                    FSBrowser.NmJ1939IdentityNumber
                  );
}
/*@@end*/

/*@@caplFunc:EcuStartUp():*/
/*
 * Call this function to connect the node to the bus
 */
void EcuStartUp()
{
  if ((gECUHandle == 0) && (gECUState == kNotConnected)) {
    // create ECU handle
    gECUHandle  = Iso11783CreateECU( gECUBus, gECUName );
    gECUAddress = FSBrowser.NmStationAddress;
    Iso11783ECUGoOnline( gECUHandle, gECUAddress );

    gECUState = kStarting;
  }
  else {
    writeDbgLevel( kDbgInfo, "<%s> function 'EcuStartUp' failed, already connected", gECULabel );
  }

	putValue(EvFSB_CurrentDirectory, "~\\");
}
/*@@end*/

/*@@caplFunc:EcuShutDown():*/
//
// Call this function to disconnect the node from the bus
//
void EcuShutDown()
{
  if (gECUState != kNotConnected) {
    gECUState   = kNotConnected;
    gECUAddress = kNullAddr;

    cancelTimer( FS_MaintainTimer );

    putValue( EvFSB_EcuAddress, kNullAddr );

    Iso11783DestroyECU( gECUHandle );
    gECUHandle = 0;
  }
}
/*@@end*/

/*@@caplFunc:utilCopyDataToPG(pg*,char[],long):*/
/*
 * Copies the data of an array to a parameter group.
 */
utilCopyDataToPG( pg * _pg, char data[], long length )
{
  int i;

  for( i = 0; i < length; i++ ) {
    _pg.byte(i) = data[i];
  }
}
/*@@end*/

/*@@caplFunc:EcuLostAddress():*/
/*
 * The VT lost its address. It tries to find a new address in the range
 * of 128..238. 
 */
EcuLostAddress()
{
  dword result;

  putValue( EvFS_EcuAddress, kNullAddr );

  // calculate new address
  if ((gECUAddress < 128) || (gECUAddress > 238)) {
    gECUAddress = 128;
  }
  else {
    gECUAddress++;
    if (gECUAddress > 238) {
      return;
    }
  }

  // ecu(s) going online
  result = Iso11783ECUGoOnline( gECUHandle, gECUAddress );
  if (result) {
    write( "<%s> Iso11783ECUGoOnline failed, result=%d", gECULabel, result );
  }
}
/*@@end*/

/*@@envVar:EvISOBUS_ECU_PWR:*/
on envVar EvISOBUS_ECU_PWR
{
  double ecuPWR, ecuCurrent;;
  ecuPWR = getValue( EvISOBUS_ECU_PWR );

  if (getValue( EvFS_Connection )) {
    if ((gECUAddress == kNullAddr) && (ecuPWR > 10.0)) {
      EcuStartUp();
    }
    else if ((gECUAddress < kNullAddr) && (ecuPWR < 8.0)) {
      if (gECUHandle) {
        EcuShutDown();
      }
    }
  }
}
/*@@end*/

/*@@envVar:EvFSB_Connection:*/
//
// Connect/unconnect the implement to the implement bus
//
on envVar EvFSB_Connection
{
  switch(getValue(this)) {
    case 0:
      EcuShutDown();
      break;
    case 1:
      EcuStartUp();
      break;
  }
}
/*@@end*/

/*@@caplFunc:fsSendRequest():*/
/*
 * Sends a Request to the File Server. The data is taken from
 * the global RX_FSRequest variable.
 *
 * return - 1 = request was put to send queue
 *          0 = a request is pending
 */
int fsSendRequest()
{
  char txBuffer[1785];
  long size;

  if (FS_RequestState == 0) {
    FS_RequestState = 1;

    for( i = 0; i < FS_Request.DLC; i++ ) {
      txBuffer[i] = FS_Request.BYTE(i);
    }

    // fill up with 0xFF, if necessary
    if (FS_Request.DLC < 8) {
      size = 8;
      for( ; i < 8; i++ ) {
        txBuffer[i] = 0xff;
      }
    }
    else {
      size = FS_Request.DLC;
    }
 
    Iso11783TxReqPG( gECUHandle, FS_Request.pgn, FS_Address, 7, size, txBuffer ); 

    setTimer( FS_TimeoutTimer, 1500 );

    panelUpdateState();

    return 1;
  }
  else {
    return 0;
  }
}
/*@@end*/

/*@@stop:StopMeasurement:*/
on stopMeasurement
{
  EcuShutDown();
}
/*@@end*/

/*@@caplFunc:fsOnResponse():*/
/*
 * A response from the File Server was received.
 */
void fsOnResponse()
{
  char path[kMaxPath];
  char data[200];

  if (gECUState != kOperational) return;

  if (FS_Response.FSServerCommand == 0x00) {
    // Status
    if (FS_Response.SA != FS_Address) {
      FS_Address = FS_Response.SA;
      cancelTimer( FS_MaintainTimer );
      cancelTimer( FS_TimeoutTimer );
      setTimer( FS_MaintainTimer, 0 );
    }
    FS_LastStatus = timeNow();
    return;
  }
  else if (FS_Address == kNullAddr) {
    return;
  }

  // check TAN
  if (FS_Response.OF_TAN != FS_Request.OF_TAN) {
    // wrong TAN, ignore it !
    return;
  }

  // request complete
  if ((FS_RequestState > 0) && (FS_Response.FSServerCommand == FS_Request.FSClientCommand)) {
    cancelTimer( FS_TimeoutTimer );
    FS_RequestState = 0; 
  }

  putValue( EvFSB_ErrorText, "" );

  // dispatch command
  switch(FS_Response.FSServerCommand) {
    case 0x20: // Open File
      if (FS_Response.OF_ErrorCode == 0) {
        FS_FileHandle = FS_Response.OF_Handle;
      }
      else {
        fsOnError(FS_Response.OF_ErrorCode);
      }
	  	if (gNewDir == 0)
      	fsReadDirectoryContent( "", FS_Response.OF_ErrorCode );
	  	if (gNewDir == 1)
	  		gNewDir = 0;
      break;
    case 0x21: // Seek File
      if (FS_Response.SF_ErrorCode == 0) {
      }
      else {
        fsOnError(FS_Response.SF_ErrorCode);
      }
      break;
    case 0x22: // Read File
      if ((FS_Response.RF_ErrorCode == 0) || (FS_Response.RF_ErrorCode == 45)) {
        fsReadDirectoryContent( "", FS_Response.RF_ErrorCode ); 
      }
      else {
        fsOnError(FS_Response.RF_ErrorCode);
      }
      break;
    case 0x23: // Write File
      if (FS_Response.WF_ErrorCode == 0) {
      }
      else {
        fsOnError(FS_Response.WF_ErrorCode);
      }
      break;
    case 0x24: // Close File
      fsReadDirectoryContent( "", FS_Response.CF_ErrorCode ); 
      if (FS_Response.CF_ErrorCode == 0) {
        FS_FileHandle = 0;
      }
      else {
        fsOnError(FS_Response.CF_ErrorCode);
      }
      break;
    case 0x30: // Delete File
      if (FS_Response.MF_ErrorCode == 0) {
      }
      else {
        fsOnError(FS_Response.MF_ErrorCode);
      }
      break;
    case 0x31: // Get File Attribute
      if (FS_Response.GA_ErrorCode == 0) {
      }
      else {
        fsOnError(FS_Response.GA_ErrorCode);
      }
      break;
    case 0x32: // Set File Attribute
      if (FS_Response.SA_ErrorCode == 0) {
      }
      else {
        fsOnError(FS_Response.SA_ErrorCode);
      }
      break;
    case 0x33: // Get Date and Time
      if (FS_Response.FDT_ErrorCode == 0) {
      }
      else {
        fsOnError(FS_Response.FDT_ErrorCode);
      }
      break;
    case 0x10: // Get Current Directory
      if (FS_Response.GCD_ErrorCode == 0) {
        fsReadDirectoryContent( "", FS_Response.GCD_ErrorCode ); 
      }
      else {
        fsOnError(FS_Response.GCD_ErrorCode);
      }
      break;
    case 0x11: // Change Current Directory
      fsReadDirectoryContent( "", FS_Response.CCD_ErrorCode ); 
      if (FS_Response.CCD_ErrorCode != 0) {
        fsOnError(FS_Response.CCD_ErrorCode);
      }
      break;
    default:   // invalid command
      break;
  }

  panelUpdateState();
}
/*@@end*/

/*@@timer:FS_TimeoutTimer:*/
/*
 * Timout timer for File Server requests
 */
on timer FS_TimeoutTimer
{
  // No response from the File Server received.
  // Abort the current request.

  FS_RequestState = 0;

  panelUpdateState();
}
/*@@end*/

/*@@caplFunc:panelUpdateState():*/
/*
 * Update the state of the controls
 */
void panelUpdateState()
{
  int buttonEnable;

  buttonEnable = (FS_RequestState == 0) ? 1 : 0;

  enableControl( gPanelName, "RefreshButton", buttonEnable );
  enableControl( gPanelName, "DirUpButton", buttonEnable );

  enableControl( gPanelName, "UpButton", buttonEnable && (gScrollPos > 0) );
  enableControl( gPanelName, "DownButton", buttonEnable && (gScrollPos < gFileCount-5) );
}
/*@@end*/

/*@@caplFunc:fsReadDirectoryContent(char[],byte):*/
/*
 * Read directory content On the File Server
 *
 * return - 1 = success
 * path   - Path of the new current directory
 */
int fsReadDirectoryContent( char path[], BYTE status )
{
  long i, length;
  long state = 0;
  char dirPath[kMaxPath];
  long offset;
  long maxentries = 0;

  if (FS_RequestState == 0) {
    switch(state) {
      case 0:
        length = strlen( path );

        FS_Request.FSClientCommand     = 0x11; // change current directory
        FS_Request.CCD_TAN             = ++FS_TAN;
		    FS_Request.CCD_PathName        = length;
        FS_Request.DLC                 = 4 + length;

        for( i = 0; i < length; i++ ) {
          FS_Request.byte(4+i) = path[i];
        }
        state = 1;

        return fsSendRequest();
      case 1:
        if (status != 0) {
          state = 0;
          return 0;
        }
        else {
          FS_Request.FSClientCommand = 0x10;
          FS_Request.GCD_TAN         = ++FS_TAN;
          FS_Request.DLC             = 2;
          state = 2;
        }
        return fsSendRequest();
      case 2:
        if (status != 0) {
          state = 0;
          return 0;
        }
        else {
          // set directory
          for( i = 13; i < FS_Response.DLC; i++ ) {
            dirPath[i-13] = FS_Response.byte(i);
          }
          dirPath[i-13] = 0;
          putValue( EvFSB_CurrentDirectory, dirPath );

          // open directory
          FS_Request.FSClientCommand = 0x20; // open directory

  		    FS_Request.OF_TAN                 = ++FS_TAN;
      	  FS_Request.OF_FlagExclusiveAccess = 0;
      	  FS_Request.OF_FlagAppend          = 0;
      	  FS_Request.OF_FlagCreateNew       = 0;
  		    FS_Request.OF_FlagReadWrite		    = 3;	// open directory for reading
      	  FS_Request.OF_PathName            = 1;

          FS_Request.DLC     = 5 + 1;
          FS_Request.byte(5) = '.';
          state      = 3;
          gFileCount = 0;
        }
        return fsSendRequest();
      case 3:
        if (FS_Response.FSServerCommand == 0x22) {
		      offset = 5;
		      maxentries = FS_Response.RF_Count;
          for(gFileCount = 0; gFileCount <  maxentries; gFileCount++) {
		  	    for( i = 0; (i < FS_Response.byte(offset)); i++ ) {
            	gFileNames[gFileCount][i] = FS_Response.byte(offset+1+i);
          	}
  		      gFileNames[gFileCount][i] = 0;
  		      gFileInfo[gFileCount][0] 	= FS_Response.byte(offset+i+1);
      		  gFileInfo[gFileCount][3] 	= FS_Response.byte(offset+i+2) & 0x1F;
      		  gFileInfo[gFileCount][2] 	= ((FS_Response.byte(offset+i+2) & 0xE0) >> 5) + ((FS_Response.byte(offset+i+3) & 0x01) << 3);
      		  gFileInfo[gFileCount][1] 	= ((FS_Response.byte(offset+i+3) & 0xFE) >> 1) + 1980;
		  
      		  gFileInfo[gFileCount][6] 	= (FS_Response.byte(offset+i+4) & 0x1F) >> 1;
      		  gFileInfo[gFileCount][5] 	= ((FS_Response.byte(offset+i+4) & 0xE0) >> 5) + ((FS_Response.byte(offset+i+5) & 0x07) << 3);
      		  gFileInfo[gFileCount][4] 	= ((FS_Response.byte(offset+i+5) & 0xF8) >> 3);

      		  gFileInfo[gFileCount][7] 	= FS_Response.byte(offset+i+6) + (FS_Response.byte(offset+i+7) << 8) + (FS_Response.byte(offset+i+8) << 16) + (FS_Response.byte(offset+i+9) << 24);
      		  offset += 10 + FS_Response.byte(offset);
		      }
		
          gFileCount++;
        }

        if ((status != 45) && (status != 0) && (gFileCount == 0)) {
          state = 0;
          return 0;
        }
        else if (status != 0) {
          FS_Request.FSClientCommand = 0x25; // close file
          FS_Request.CF_TAN         = ++FS_TAN;
          FS_Request.CF_Handle      = FS_FileHandle;
          FS_Request.DLC            = 3;
          state = 4;
        }

		    gFileCount--;

        if ((FS_Request.FSClientCommand == 0x20)) {
          FS_Request.FSClientCommand = 0x22; // read directory (file)
          FS_Request.RF_TAN          = ++FS_TAN;
          FS_Request.RF_Handle       = FS_FileHandle;
		      FS_Request.RF_Count		     = 100;
          FS_Request.DLC             = 6;
          FS_Request.RF_ReportHiddenFiles = 0;
        }
        else {
          FS_Request.FSClientCommand = 0x24; // close file
          FS_Request.CF_TAN          = ++FS_TAN;
          FS_Request.CF_Handle       = FS_FileHandle;
          FS_Request.DLC             = 3;
          state = 4;
        }
        return fsSendRequest();
      case 4:
        state      = 0;
        gScrollPos = 0;
        panelUpdateList();
        return 1;
    }
    return 0;
  }
  else {
    return 0;
  }
}
/*@@end*/

/*@@envVar:EvFSB_Refresh:*/
/*
 * Read a directory on the File Server
 */
on envVar EvFSB_Refresh
{
  char path[kMaxPath];

  if (getValue(this) == 1) {
    getValue( EvFSB_CurrentDirectory, path );
    fsReadDirectoryContent( path, 0 );
  } 
}
/*@@end*/

/*@@caplFunc:fsOnError(byte):*/
/*
 *
 */
void fsOnError( BYTE status)
{
  char text[200];

  // check status
  if (status != 0) {
    switch(FS_Response.OF_ErrorCode) {
      case 1: snprintf( text, elCount(text), "Access Denied (0x%x)"          , status ); break;
      case 2: snprintf( text, elCount(text), "Invalid Access (0x%x)"         , status ); break;
      case 3: snprintf( text, elCount(text), "Too many files open (0x%x)"    , status ); break;
      case 4: snprintf( text, elCount(text), "File or path not found (0x%x)" , status ); break;
      case 5: snprintf( text, elCount(text), "Invalid Handle (0x%x)"         , status ); break;
      case 6: snprintf( text, elCount(text), "Invalid given name (0x%x)"     , status ); break;
      case 7: snprintf( text, elCount(text), "Invalid slot (0x%x)"           , status ); break;
      case 8: snprintf( text, elCount(text), "Media out of free space (0x%x)", status ); break;
      case 45:snprintf( text, elCount(text), "Bad parameter (0x%x)"          , status ); break;
      default:
        snprintf( text, elCount(text), "File Server Error, status 0x%x", status );
        break;
    }
    putValue( EvFSB_ErrorText, text );
    panelUpdateState();
    return;
  }
}
/*@@end*/

/*@@caplFunc:panelUpdateList():*/
/*
 * Update file list
 */
void panelUpdateList()
{
  long i;
  char text[32];

  i = gScrollPos;

  // Line 1
  if (i < gFileCount) {
    putValue( EvFSB_Line1, i+1 );
    putValue( EvFSB_Filename1, gFileNames[i] );
    putValue( EvFSB_FileSize1, gFileInfo[i][7] );

    snprintf( text, elCount(text), "%d-%d-%d %.2d:%.2d:%.2d",
              gFileInfo[i][1], gFileInfo[i][2], gFileInfo[i][3], 
              gFileInfo[i][4], gFileInfo[i][5], gFileInfo[i][6] );
    putValue( EvFSB_DateTime1, text );
    putValue( EvFSB_FileIcon1, (gFileInfo[i][0] & 0x10) ? 2 : 1 );
    enableControl( gPanelName, "Show1", (gFileInfo[i][0] & 0x10) ? 1 : 0 );
    enableControl( gPanelName, "Delete1", 1 );
  }
  else {
    putValue( EvFSB_Line1, 0 );
    putValue( EvFSB_Filename1, "" );
    putValue( EvFSB_FileSize1, 0 );
    putValue( EvFSB_DateTime1, "" );
    putValue( EvFSB_FileIcon1, 0 );
    enableControl( gPanelName, "Show1", 0 );
    enableControl( gPanelName, "Delete1", 0 );
  }

  // Line 2
  i++;
  if (i < gFileCount) {
    putValue( EvFSB_Line2, i+1 );
    putValue( EvFSB_Filename2, gFileNames[i] );
    putValue( EvFSB_FileSize2, gFileInfo[i][7] );

    snprintf( text, elCount(text), "%d-%d-%d %.2d:%.2d:%.2d",
              gFileInfo[i][1], gFileInfo[i][2], gFileInfo[i][3], 
              gFileInfo[i][4], gFileInfo[i][5], gFileInfo[i][6] );
    putValue( EvFSB_DateTime2, text );
    putValue( EvFSB_FileIcon2, (gFileInfo[i][0] & 0x10) ? 2 : 1 );
    enableControl( gPanelName, "Show2", (gFileInfo[i][0] & 0x10) ? 1 : 0 );
    enableControl( gPanelName, "Delete2", 1 );
  }
  else {
    putValue( EvFSB_Line2, 0 );
    putValue( EvFSB_Filename2, "" );
    putValue( EvFSB_FileSize2, 0 );
    putValue( EvFSB_DateTime2, "" );
    putValue( EvFSB_FileIcon2, 0 );
    enableControl( gPanelName, "Show2", 0 );
    enableControl( gPanelName, "Delete2", 0 );
  }

  // Line 3
  i++;
  if (i < gFileCount) {
    putValue( EvFSB_Line3, i+1 );
    putValue( EvFSB_Filename3, gFileNames[i] );
    putValue( EvFSB_FileSize3, gFileInfo[i][7] );

    snprintf( text, elCount(text), "%d-%d-%d %.2d:%.2d:%.2d",
              gFileInfo[i][1], gFileInfo[i][2], gFileInfo[i][3], 
              gFileInfo[i][4], gFileInfo[i][5], gFileInfo[i][6] );
    putValue( EvFSB_DateTime3, text );
    putValue( EvFSB_FileIcon3, (gFileInfo[i][0] & 0x10) ? 2 : 1 );
    enableControl( gPanelName, "Show3", (gFileInfo[i][0] & 0x10) ? 1 : 0 );
    enableControl( gPanelName, "Delete3", 1 );
  }
  else {
    putValue( EvFSB_Line3, 0 );
    putValue( EvFSB_Filename3, "" );
    putValue( EvFSB_FileSize3, 0 );
    putValue( EvFSB_DateTime3, "" );
    putValue( EvFSB_FileIcon3, 0 );
    enableControl( gPanelName, "Show3", 0 );
    enableControl( gPanelName, "Delete3", 0 );
  }

  // Line 4
  i++;
  if (i < gFileCount) {
    putValue( EvFSB_Line4, i+1 );
    putValue( EvFSB_Filename4, gFileNames[i] );
    putValue( EvFSB_FileSize4, gFileInfo[i][7] );

    snprintf( text, elCount(text), "%d-%d-%d %.2d:%.2d:%.2d",
              gFileInfo[i][1], gFileInfo[i][2], gFileInfo[i][3], 
              gFileInfo[i][4], gFileInfo[i][5], gFileInfo[i][6] );
    putValue( EvFSB_DateTime4, text );
    putValue( EvFSB_FileIcon4, (gFileInfo[i][0] & 0x10) ? 2 : 1 );
    enableControl( gPanelName, "Show4", (gFileInfo[i][0] & 0x10) ? 1 : 0 );
    enableControl( gPanelName, "Delete4", 1 );
  }
  else {
    putValue( EvFSB_Line4, 0 );
    putValue( EvFSB_Filename4, "" );
    putValue( EvFSB_FileSize4, 0 );
    putValue( EvFSB_DateTime4, "" );
    putValue( EvFSB_FileIcon4, 0 );
    enableControl( gPanelName, "Show4", 0 );
    enableControl( gPanelName, "Delete4", 0 );
  }

  // Line 5
  i++;
  if (i < gFileCount) {
    putValue( EvFSB_Line5, i+1 );
    putValue( EvFSB_Filename5, gFileNames[i] );
    putValue( EvFSB_FileSize5, gFileInfo[i][7] );

    snprintf( text, elCount(text), "%d-%d-%d %.2d:%.2d:%.2d",
              gFileInfo[i][1], gFileInfo[i][2], gFileInfo[i][3], 
              gFileInfo[i][4], gFileInfo[i][5], gFileInfo[i][6] );
    putValue( EvFSB_DateTime5, text );
    putValue( EvFSB_FileIcon5, (gFileInfo[i][0] & 0x10) ? 2 : 1 );
    enableControl( gPanelName, "Show5", (gFileInfo[i][0] & 0x10) ? 1 : 0 );
    enableControl( gPanelName, "Delete5", 1 );
  }
  else {
    putValue( EvFSB_Line5, 0 );
    putValue( EvFSB_Filename5, "" );
    putValue( EvFSB_FileSize5, 0 );
    putValue( EvFSB_DateTime5, "" );
    putValue( EvFSB_FileIcon5, 0 );
    enableControl( gPanelName, "Show5", 0 );
    enableControl( gPanelName, "Delete5", 0 );
  }
}
/*@@end*/

/*@@envVar:EvFSB_ScrollUp:*/
/*
 * Scroll list up
 */ 
on envVar EvFSB_ScrollUp
{
  if (getValue(this) == 1) {
    if (gScrollPos > 0) {
      gScrollPos--;
      panelUpdateList();
      panelUpdateState();
    }
  }
}
/*@@end*/

/*@@envVar:EvFSB_ScrollDown:*/
/*
 * Scroll list dowm
 */ 
on envVar EvFSB_ScrollDown
{
  if (getValue(this) == 1) {
    if (gScrollPos < gFileCount-5) {
      gScrollPos++;
      panelUpdateList();
      panelUpdateState();
    }
  }
}
/*@@end*/

/*@@envVar:EvFSB_DirUp:*/
/*
 * Read a directory on the File Server
 */
on envVar EvFSB_DirUp
{
  char path[kMaxPath];

  if (getValue(this) == 1) {
    fsReadDirectoryContent( "..", 0 );
  }
}
/*@@end*/

/*@@envVar:EvFSB_Show1:*/
/*
 * Open directory in line 1
 */
on envVar EvFSB_Show1
{
  char path[kMaxPath];
  char newPath[kMaxPath];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( newPath, elCount(newPath), "\\%s", gFileNames[i] );
      }
      else {
        snprintf( newPath, elCount(newPath), "%s\\%s", path, gFileNames[i] );
      }

      fsReadDirectoryContent( newPath, 0 );
    }  
  }
}
/*@@end*/

/*@@envVar:EvFSB_Show2:*/
/*
 * Open directory in line 2
 */
on envVar EvFSB_Show2
{
  char path[kMaxPath];
  char newPath[kMaxPath];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos + 1;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( newPath, elCount(newPath), "\\%s", gFileNames[i] );
      }
      else {
        snprintf( newPath, elCount(newPath), "%s\\%s", path, gFileNames[i] );
      }

      fsReadDirectoryContent( newPath, 0 );
    }  
  }
}
/*@@end*/

/*@@envVar:EvFSB_Show3:*/
/*
 * Open directory in line 3
 */
on envVar EvFSB_Show3
{
  char path[kMaxPath];
  char newPath[kMaxPath];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos + 2;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( newPath, elCount(newPath), "\\%s", gFileNames[i] );
      }
      else {
        snprintf( newPath, elCount(newPath), "%s\\%s", path, gFileNames[i] );
      }

      fsReadDirectoryContent( newPath, 0 );
    }  
  }
}
/*@@end*/

/*@@envVar:EvFSB_Show4:*/
/*
 * Open directory in line 4
 */
on envVar EvFSB_Show4
{
  char path[kMaxPath];
  char newPath[kMaxPath];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos + 3;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( newPath, elCount(newPath), "\\%s", gFileNames[i] );
      }
      else {
        snprintf( newPath, elCount(newPath), "%s\\%s", path, gFileNames[i] );
      }

      fsReadDirectoryContent( newPath, 0 );
    }  
  }
}
/*@@end*/

/*@@envVar:EvFSB_Show5:*/
/*
 * Open directory in line 5
 */
on envVar EvFSB_Show5
{
  char path[kMaxPath];
  char newPath[kMaxPath];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos + 4;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( newPath, elCount(newPath), "\\%s", gFileNames[i] );
      }
      else {
        snprintf( newPath, elCount(newPath), "%s\\%s", path, gFileNames[i] );
      }

      fsReadDirectoryContent( newPath, 0 );
    }  
  }
}
/*@@end*/

/*@@caplFunc:fsNewDirectory(char[],byte):*/
/*
 * Create a new directory on the File Server
 *
 * return - 1 = success
 * path   - Path of the new directory
 * mode   - Mode of the new directory
 */
int fsNewDirectory( char path[], BYTE mode )
{
  long i, length;

  if (FS_RequestState == 0) {
    length = strlen( path );

    FS_Request.FSClientCommand 				= 0x20; // Open file with path set to directory
    FS_Request.OF_TAN          				= ++FS_TAN;
		FS_Request.OF_FlagExclusiveAccess = 0;
		FS_Request.OF_FlagAppend  			  = 0;
		FS_Request.OF_FlagCreateNew   		= 1;  // create new
		FS_Request.OF_FlagReadWrite				= 3;	// open directory for reading
		FS_Request.OF_PathName            = length;
    FS_Request.DLC             				= 5 + length;

    for( i = 0; i < length; i++ ) {
      FS_Request.byte(5+i) = path[i];
    }

		gNewDir = 1;
    return fsSendRequest();
	
  }
  else {
    return 0;
  }
}
/*@@end*/

/*@@envVar:EvFSB_NewDirectory:*/
/*
 * Create a new directory on the File Server
 */
on envVar EvFSB_NewDirectory
{
  char path[kMaxPath];
  char length;

  if (getValue(this) == 1) {
    getValue( EvFSB_CurrentDirectory, path );

    length = strlen(path);

    if (path[length-1] != 92) {
      path[length] = 92;
      path[length+1] = 0;
    }

    fsNewDirectory( path, 0 );
  }
}
/*@@end*/

/*@@caplFunc:fsMoveFile(char[],char[],byte):*/
/*
 * Move / Delete / Copy file on the File Server
 *
 * return - 1 = success
 * path   - Path of the file
 */
int fsMoveFile( char srcpath[], char destpath[], BYTE mode)
{
  long i, srclength, destlength;

  if (FS_RequestState == 0) {
    srclength = strlen( srcpath );
    destlength = strlen( destpath );

    FS_Request.FSClientCommand 							= 0x30; // move file
    FS_Request.MF_TAN          							= ++FS_TAN;
    FS_Request.MF_MoveModeCopy 							= (mode & 0x01);
    FS_Request.MF_MoveModeForce 						= ((mode & 0x02) >> 1);
    FS_Request.MF_MoveModeRecursive 				= ((mode & 0x04) >> 2);
    FS_Request.MF_SourcePathNameLength 			= srclength;
    FS_Request.MF_DestinationPathNameLength = destlength; // delete file
    FS_Request.DLC             							= 7 + srclength + destlength;

    for( i = 0; i < srclength; i++ ) {
      FS_Request.byte(7+i) = srcpath[i];
    }

    for (l = 0; l  < destlength; l++) {
      FS_Request.byte(7+i+l) = destpath[l];
    }
    
    return fsSendRequest();
  }
  else {
    return 0;
  }
}
/*@@end*/

/*@@envVar:EvFSB_Delete1:*/
/*
 * Delete file or  directory in line 1
 */
on envVar EvFSB_Delete1
{
  char path[kMaxPath];
  char delPath[kMaxPath];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( delPath, elCount(delPath), "\\%s", gFileNames[i] );
      }
      else {
        snprintf( delPath, elCount(delPath), "%s\\%s", path, gFileNames[i] );
      }

      if (gFileInfo[i][0] & 0x10) {
        fsDeleteFile( delPath, 0 );
      }
      else {
        fsDeleteFile( delPath, 0 );
      }
    }  
  }
}
/*@@end*/

/*@@envVar:EvFSB_Delete5:*/
/*
 * Delete file or  directory in line 5
 */
on envVar EvFSB_Delete5
{
  char path[kMaxPath];
  char delPath[kMaxPath];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos + 4;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( delPath, elCount(delPath), "\\%s", gFileNames[i] );
      }
      else {
        snprintf( delPath, elCount(delPath), "%s\\%s", path, gFileNames[i] );
      }

      if (gFileInfo[i][0] & 0x10) {
        fsDeleteFile( delPath, 0 );
      }
      else {
        fsDeleteFile( delPath, 0 );
      }
    }  
  }
}
/*@@end*/

/*@@envVar:EvFSB_Delete2:*/
/*
 * Delete file or  directory in line 2
 */
on envVar EvFSB_Delete2
{
  char path[kMaxPath];
  char delPath[kMaxPath];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos + 1;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( delPath, elCount(delPath), "\\%s", gFileNames[i] );
      }
      else {
        snprintf( delPath, elCount(delPath), "%s\\%s", path, gFileNames[i] );
      }

      if (gFileInfo[i][0] & 0x10) {
        fsDeleteFile( delPath, 0 );
      }
      else {
        fsDeleteFile( delPath, 0 );
      }
    }  
  }
}
/*@@end*/

/*@@envVar:EvFSB_Delete3:*/
/*
 * Delete file or  directory in line 3
 */
on envVar EvFSB_Delete3
{
  char path[kMaxPath];
  char delPath[kMaxPath];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos + 2;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( delPath, elCount(delPath), "\\%s", gFileNames[i] );
      }
      else {
        snprintf( delPath, elCount(delPath), "%s\\%s", path, gFileNames[i] );
      }

      if (gFileInfo[i][0] & 0x10) {
        fsDeleteFile( delPath, 0 );
      }
      else {
        fsDeleteFile( delPath, 0 );
      }
    }  
  }
}
/*@@end*/

/*@@envVar:EvFSB_Delete4:*/
/*
 * Delete file or  directory in line 4
 */
on envVar EvFSB_Delete4
{
  char path[kMaxPath];
  char delPath[kMaxPath];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos + 3;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( delPath, elCount(delPath), "\\%s", gFileNames[i] );
      }
      else {
        snprintf( delPath, elCount(delPath), "%s\\%s", path, gFileNames[i] );
      }

      if (gFileInfo[i][0] & 0x10) {
        fsDeleteFile( delPath, 0 );
      }
      else {
        fsDeleteFile( delPath, 0 );
      }
    }  
  }
}
/*@@end*/

/*@@envVar:EvFSB_Rename1:*/
/*
 * Rename file or  directory in line 1
 */
on envVar EvFSB_Rename1
{
  char path[kMaxPath];
  char renamePath[kMaxPath];
  char newPath[kMaxPath];
  char newFilename[14];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );
      getValue( EvFSB_Filename1, newFilename );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( renamePath, elCount(renamePath), "\\%s", gFileNames[i] );
        snprintf( newPath, elCount(newPath), "\\%s", newFilename );
      }
      else {
        snprintf( renamePath, elCount(renamePath), "%s\\%s", path, gFileNames[i] );
        snprintf( newPath, elCount(newPath), "%s\\%s", path, newFilename );
      }

      fsMoveFile(renamePath, newPath, 0);
    }  
  }
}
/*@@end*/

/*@@envVar:EvFSB_Rename2:*/
/*
 * Rename file or  directory in line 2
 */
on envVar EvFSB_Rename2
{
  char path[kMaxPath];
  char renamePath[kMaxPath];
  char newPath[kMaxPath];
  char newFilename[14];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos + 1;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );
      getValue( EvFSB_Filename2, newFilename );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( renamePath, elCount(renamePath), "\\%s", gFileNames[i] );
        snprintf( newPath, elCount(newPath), "\\%s", newFilename );
      }
      else {
        snprintf( renamePath, elCount(renamePath), "%s\\%s", path, gFileNames[i] );
        snprintf( newPath, elCount(newPath), "%s\\%s", path, newFilename );
      }

      fsMoveFile(renamePath, newPath, 0);
    }  
  }
}
/*@@end*/

/*@@caplFunc:Iso11783AppAddrClaimed(long):*///callback
/*
 * This function is called from the Nodelayer, 
 * if the address is successfully claimed.
 */
DWORD Iso11783AppAddrClaimed ( long ecuHandle )
{
  if (ecuHandle == gECUHandle) {
    writeDbgLevel( kDbgInfo, "<%s> online with address %d", gECULabel, Iso11783GetNodeAddr(gECUHandle) );
  }

  gECUState = kOperational;

  putValue( EvFSB_EcuAddress, Iso11783GetNodeAddr(gECUHandle) );

  return 0;
}
/*@@end*/

/*@@caplFunc:Iso11783AppTxIndication(long,long,long,long):*///callback
/*
 * This function is called from the Nodelayer DLL, if a PG is successfully sent.
 */
DWORD Iso11783AppTxIndication(long ecuhdl, long TxPgn, long source, long dest) 
{
  char data[8];
  long size;

  size = Iso11783GetRxData( elCount(data), data );

  switch(TxPgn) {
    //
    // Response for the File Server
    //
    case 0xaa00:
      if (data[0] != 0) {  // ignore maintenance message
        FS_RequestState = 2;
      }
      break;
  }

  return 0;
}
/*@@end*/

/*@@caplFunc:Iso11783AppCmdAddrIndication(long,long):*///callback
/*
 * A commanded address PG was received.
 */
DWORD Iso11783AppCmdAddrIndication(long ecuHdl, long length)
{
  char rxBuffer[9];

  // copy received data to buffer
  length = Iso11783GetRxData( elCount(rxBuffer), rxBuffer );

  Iso11783ECUGoOnline( gECUHandle, rxBuffer[8] );
  
  return 0;
}
/*@@end*/

/*@@caplFunc:Iso11783AppErrorIndication(long,long,long,long):*///callback
/*
 * If an error occured, this function is called from the Nodelayer DLL.
 */
DWORD Iso11783AppErrorIndication (long ecuhdl, long errorClass, long errorCode, long addCode )
{
  char strErrInfo[64];

  switch( errorCode ) {
    case 0x1: snprintf( strErrInfo, elCount(strErrInfo), "cts error" ); break;
    case 0x2: snprintf( strErrInfo, elCount(strErrInfo), "peer to peer data timeout" ); break;
    case 0x3: snprintf( strErrInfo, elCount(strErrInfo), "BAM data timeout" ); break;
    case 0x4: snprintf( strErrInfo, elCount(strErrInfo), "acknowledge timeout" ); break;
    case 0x10: //snprintf( strErrInfo, elCount(strErrInfo), "lost address claiming or received address claim with higher priority" ); break;
      EcuLostAddress();
      return 0;
    case 0x15: snprintf( strErrInfo, elCount(strErrInfo), "init error: node name detected two times" ); break;
    case 0x17: snprintf( strErrInfo, elCount(strErrInfo), "init error: node address detected two times" ); break;
    case 0x30: snprintf( strErrInfo, elCount(strErrInfo), "exeption while choosing protokoll BAM or CMDT" ); break;
    case 0x40: snprintf( strErrInfo, elCount(strErrInfo), "internal data buffer full" ); break;
    case 0x60: snprintf( strErrInfo, elCount(strErrInfo), "user sends system pg " ); break;
    case 0x71: snprintf( strErrInfo, elCount(strErrInfo), "transmission abort by receiver" ); break;
    case 0x72: snprintf( strErrInfo, elCount(strErrInfo), "transmission abort by sender" ); break;
  }

  writeDbgLevel( kDbgError, "<%s> ERROR(0x%x) %s", gECULabel, errorCode, strErrInfo );
  
  return 0;
}
/*@@end*/

/*@@caplFunc:Iso11783AppNmtIndication(long,long,long):*///callback
/*
 * A node has changed its address or claimed an address successfully
 */
DWORD Iso11783AppNmtIndication( LONG busHandle, LONG address, LONG state )
{
  return 0;
}
/*@@end*/

/*@@caplFunc:Iso11783AppRequestIndication(long,long,long,long,long):*///callback
/*
 * A request PGN was received
 */
DWORD Iso11783AppRequestIndication(long ecuHdl, long source, long dest, long page, long pgNum)
{
  return 0;
}
/*@@end*/

/*@@caplFunc:Iso11783AppRxIndication(long,long,long,long,long):*///callback
/*
 * This function is called from the Nodelayer DLL, if a PG is received.
 */
dword Iso11783AppRxIndication( long ecuHandle, long src, long dst, long length, long _pgn )
{
  char rxBuffer[1785];
  long i, size;

  //
  // dispatch Parameter Group
  //
  switch(_pgn) {
    //
    // Response from File Server
    //
    case 0xab00:
      // copy received data to buffer
      size = Iso11783GetRxData( elCount(rxBuffer), rxBuffer );
      // copy data to RX parameter group
      for( i = 0; i < size; i++ ) {
        FS_Response.BYTE(i) = rxBuffer[i];
      }
      FS_Response.SA  = src;
      FS_Response.DA  = dst;
      FS_Response.DLC = length;
      fsOnResponse();

      break;
  }
   
  return 1;
}
/*@@end*/

/*@@envVar:EvFSB_Rename3:*/
/*
 * Rename file or  directory in line 3
 */
on envVar EvFSB_Rename3
{
  char path[kMaxPath];
  char renamePath[kMaxPath];
  char newPath[kMaxPath];
  char newFilename[14];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos + 2;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );
      getValue( EvFSB_Filename3, newFilename );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( renamePath, elCount(renamePath), "\\%s", gFileNames[i] );
        snprintf( newPath, elCount(newPath), "\\%s", newFilename );
      }
      else {
        snprintf( renamePath, elCount(renamePath), "%s\\%s", path, gFileNames[i] );
        snprintf( newPath, elCount(newPath), "%s\\%s", path, newFilename );
      }

      fsMoveFile(renamePath, newPath, 0);
    }  
  }
}
/*@@end*/

/*@@envVar:EvFSB_Rename4:*/
/*
 * Rename file or  directory in line 4
 */
on envVar EvFSB_Rename4
{
  char path[kMaxPath];
  char renamePath[kMaxPath];
  char newPath[kMaxPath];
  char newFilename[14];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos + 3;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );
      getValue( EvFSB_Filename4, newFilename );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( renamePath, elCount(renamePath), "\\%s", gFileNames[i] );
        snprintf( newPath, elCount(newPath), "\\%s", newFilename );
      }
      else {
        snprintf( renamePath, elCount(renamePath), "%s\\%s", path, gFileNames[i] );
        snprintf( newPath, elCount(newPath), "%s\\%s", path, newFilename );
      }

      fsMoveFile(renamePath, newPath, 0);
    }  
  }
}
/*@@end*/

/*@@envVar:EvFSB_Rename5:*/
/*
 * Rename file or  directory in line 5
 */
on envVar EvFSB_Rename5
{
  char path[kMaxPath];
  char renamePath[kMaxPath];
  char newPath[kMaxPath];
  char newFilename[14];
  long i;

  if (getValue(this) == 1) {
    i = gScrollPos + 4;

    if (i < gFileCount) {
      getValue( EvFSB_CurrentDirectory, path );
      getValue( EvFSB_Filename5, newFilename );

      if (strncmp( path, "\\", kMaxPath) == 0) {
        snprintf( renamePath, elCount(renamePath), "\\%s", gFileNames[i] );
        snprintf( newPath, elCount(newPath), "\\%s", newFilename );
      }
      else {
        snprintf( renamePath, elCount(renamePath), "%s\\%s", path, gFileNames[i] );
        snprintf( newPath, elCount(newPath), "%s\\%s", path, newFilename );
      }

      fsMoveFile(renamePath, newPath, 0);
    }  
  }
}
/*@@end*/

/*@@caplFunc:fsDeleteFile(char[],byte):*///function
/*
 * Move / Delete / Copy file on the File Server
 *
 * return - 1 = success
 * path   - Path of the file
 */
int fsDeleteFile( char path[], BYTE mode)
{
  long i, length;

  if (FS_RequestState == 0) {
    length = strlen( path );

    FS_Request.FSClientCommand 				= 0x31; // delete file
    FS_Request.DF_TAN          				= ++FS_TAN;
    FS_Request.DF_DeleteMode          = (mode & 0x01);
    FS_Request.DF_DeleteModeForce 		= ((mode & 0x02) >> 1);
    FS_Request.DF_DeleteModeRecursive = ((mode & 0x04) >> 2);
    FS_Request.DF_PathName 		        = length;
    FS_Request.DLC             				= 5 + length;

    for( i = 0; i < length; i++ ) {
      FS_Request.byte(5+i) = path[i];
    }

    
    return fsSendRequest();
  }
  else {
    return 0;
  }
}
/*@@end*/

