#include "lpc176x.h"
#include "usb_hardware.h"

#ifdef USBTMC   // #endif в конце файла

#include "utils.h"
#include "../usbtmc/gpib_parser.h"
#include "../usbtmc/gpib_parser_validators.h"
#include "../usbtmc/gpib_parser_strutils.h"
#include "../usbtmc/gpib_parser_numutils.h"
#include "usb_enumeration.h"
#include "../usbtmc/usbtmc.h"
#include "../usbtmc/utilits.h"
#include "usb_options.h"
#include "options.h"

#include "usb_application.h"
#include "usb_proto.h"

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h> // ptrdiff_t

static char SCPIBParam[ MEMORY_SIZE_PARAMQUEUE ];

// Парсинг GPIB комманд. Комманды типа :COMMAND1:COMMAND2:COMMAND3 <DATA> разбиваются на отдельные
// комманды COMMAND1, COMMAND2, COMMAND3. Изначально, требуется определить, какие комманлы будут поддерживаться.
// Каждая комманда представляет собой некую структуру данных, описанную ниже (sScpiCommand_t). Она имеет заголовок,
// тоесть собственно имя комманды, сокращенное имя комманды, указатель на функцию, которая будет выполнена,
// если комманда принята, и указатель на список дочерних комманд. Так, например, комманда :SYSTem может иметь
// две дочерние комманды INFO и VERsion. Для парсинга строится дерево комманд. Сначала нужно объявить корневую комманду,
// которая не имеет имени, но имеет дочерние комманды, так называемая root комманда. ПРи начале разбора, комманды
// будут изначально искаться именно в дочерних коммандах root. Для того, чтобы объявить root, вызовем  функцию
// GPIB_InitializeCommand(sScpiCommand_t * Pointer, char * header, char *shortheader, TCmdParser_f function, unsigned int childcount )
// где Pointer - указатель на выделенную под струтуру память, header и shortheader указатели на строки, содержащие
// полное и сокращенные имена комманды соответсвенно (они могут быть размещены в секции кода), function - указатель на
// функцию - обработчик типа TCmdParser_f, которая принимает два параметра - указатель на строку, и на USB_DEVICE_INFO
// childcount - количество дочерних комманд, необходимо указать точное количество комманд в секции root для выделения под них
// памяти. После того, как GPIB_InitializeCommand вернет указатель на структуру sScpiCommand_t, его можно запомнить в переменную и использовать
// как список комманд при передаче в функцию parse(). После создания root, добавляем все комманды, вызывая
// GPIB_AddChildCommand( sScpiCommand_t * OwnerCommand, char * header, char *shortheader, TCmdParser_f function, unsigned int childcount )
// где OwnerCommand - указатель на комманду, для которой добавляемая комманда будет дочерней. Если добавляемая комманда не должна быть
// дочерней, а должна быть основной (:SYSTem), нужно передать указатель root. Остальные параметры аналогичны  GPIB_InitializeCommand
// Каждый вызов GPIB_AddChildCommand возвращает указатель на вновь созданную комманду, который нужно сохранить в переменной,
// если создаваемая комманда будет иметь дочерние комманды.
/*
   char * fGPIB_Parser_CMD1(char*);       // -- прототипы
   char * fGPIB_Parser_CMD2(char*);       //
   char * fGPIB_Parser_CMD3(char*);       //
   char * fGPIB_Parser_CMD4(char*);       //
   char * fGPIB_Parser_CMD5_1(char*);     //
//------------------------------------------------------------------------------------------------------------

   sScpiCommand_t Root;                                                       // -- переменная хранит комманду root
   sScpiCommand_t * root = GPIB_InitializeCommand( &Root, NULL, NULL, NULL, 5 );   // -- предполагается создать 5 комманд в root

        sScpiCommand_t * Command1 = GPIB_AddChildCommand( root, "CoMmanD1","CMD1", fGPIB_Parser_CMD1, 0 );   // -- комманда CMD1 не будет иметь дочерних. Обрабатываться комманда будет функцией char * fGPIB_Parser_CMD1(char*)
        sScpiCommand_t * Command2 = GPIB_AddChildCommand( root, "CoMmanD2","CMD2", fGPIB_Parser_CMD2, 0 );
        sScpiCommand_t * Command3 = GPIB_AddChildCommand( root, "CoMmanD3","CMD3", fGPIB_Parser_CMD3, 0 );
        sScpiCommand_t * Command4 = GPIB_AddChildCommand( root, "CoMmanD4","CMD4", fGPIB_Parser_CMD4, 0 );
        sScpiCommand_t * Command5 = GPIB_AddChildCommand( root, "CoMmanD5","CMD5", fGPIB_Parser_CMD5, 1 );   // -- комманда будет иметь дочернюю

                sScpiCommand_t * Command5_1 = GPIB_AddChildCommand( Command5, "CoMmanD5_1","CM51", fGPIB_Parser_CMD5_1, 0 );   // -- комманда добавлена к комманде Command5 как дочерняя

*/
// Таким образом, добавлять и изменять количество и поддержку комманд значительно проще, чем обрабатывать
// всю строку в лоб с кучей if-ов
// ==================== прототипы =========================================================

// ============================= СЕРВИСНЫЕ ФУНКЦИИ ===================================================


// GPIB_InitializeCommand:
// Initializes the GPIB command
// @pointer - the pointer to the command entry to initialize
// @header - the command header (name)
// @shortheader - the short command name
// @function - command handler
// @childMax - the maximum amount of child commands
// @enablerequest - boolean flag, allows the request mode for the command
sScpiCommand_t * GPIB_InitializeCommand ( sScpiCommand_t * pCmd, 
                                          const char * header, 
                                          const char *shortheader, 
                                          TCmdParser_f function, 
                                          size_t childMax, 
                                          BOOL enablerequest )
{  
    if( !IsCorrectPointer(pCmd) )
        return NULL;                    

    pCmd->enablerequest = enablerequest; // request mode enable
    pCmd->header        = header;        // full header
    pCmd->shortheader   = shortheader;   // short header
    pCmd->function      = function;      // handler
    pCmd->childcount    = 0;             // amount of child commands
    pCmd->childcountmax = childMax;      // maximum available of child commands  
    pCmd->pcommands     = (sScpiCommand_t*)malloc( sizeof(sScpiCommand_t) * childMax );   // allocate memory for child commands
    pCmd->bTagArgUsed = FALSE;

    if(   (NULL == pCmd->pcommands) 
       && (childMax > 0) )
    {
        return NULL;
    }
        
    return pCmd;    
}
//-----------------------------------------------------------------------------------------------------
// GPIB_MakeSpecialCommand() transform the command to special command
// Special command has an extra tag parameter to specialize the handler call.
BOOL GPIB_MakeSpecialCommand ( sScpiCommand_t * pCmd, int xTag, BOOL bUsed )
{
    if( !IsCorrectPointer(pCmd) )
        return FALSE;
          
    pCmd->bTagArgUsed = bUsed;
    pCmd->xTagArg     = xTag;
             
    return TRUE;    
}
// ------------------------------------------------------------------------------------------------------
// GPIB_AddChildCommand:
// Appends the child command to the specified owner @pOwnerCommand
sScpiCommand_t * GPIB_AddChildCommand( sScpiCommand_t * pOwnerCommand, char * header, char * shortheader, TCmdParser_f function, size_t childMax, BOOL enablerequest )
{
    if( !IsCorrectPointer(pOwnerCommand) ) 
        return NULL;

    sScpiCommand_t * ChildCommand;

    // check the limit of children
    if( pOwnerCommand->childcountmax > pOwnerCommand->childcount ) 
    {
        // Use already allocated memory for child command in @pOwnerCommand->pcommands block.
        ChildCommand = GPIB_InitializeCommand( &pOwnerCommand->pcommands[ pOwnerCommand->childcount ], header, shortheader, function, childMax, enablerequest );
        
        if( ChildCommand != NULL ) 
            pOwnerCommand->childcount++;        // increment count of children
    }
    else
        ChildCommand = NULL;      //error 
    
    return ChildCommand;
}

//-------------------------------------------------------------------------------

// GPIB_Parse:
// Parse one command with parameters.
// Note: the command must be without subsystem-character in the begining (semicolon)
// E.g. SYSTem:TRIGger
//
sScpiCommand_t * GPIB_Parse( USB_DEVICE_INFO * udi, char * str, BOOL request, char * end )
{    
    sScpiCommand_t * searchRoot = udi->usbtmcGpib.gpib_search;   // retrieve the root of the search 

    // -----------------------------------------------------------------------------------------------------------
    // Validate the pointer @searchRoot
    if( ! IsCorrectPointer(searchRoot) )  
        return NULL;        // error
    
    // Validate the pointers @str and @end
    // It must be non NULL.
    if( NULL == str || NULL == end )
        return NULL;
    
    // Check if the @end greater than @str
    if( ChooseLowestPtrConst(str, end) == end )
        // error: the @str greater than @end
        // @end it's end of the string and must be greater.
        return NULL;
    // -----------------------------------------------------------------------------------------------------------
    
    // -----------------------------------------------------------------------------------------------------------
    // @sysNodeBegin: search for the subsystem delimiter (semicolon)
    char * sysNodeBegin = GPIB_StrChr_rw( str, GPIB_CHAR_SCPI_SUBSYS_SEPARATOR_SIGN, end); 
    // @sysNodeEnd: search for the end of all subsystems (whole command header, e.g. space btw cmd and data)    
    
  //char * sysNodeEnd = strwhite( str );                // So, search for first white-charater    
    char * sysNodeEnd = searchHeaderEnd( str, end );    // Search for the end of the header    
    
    char charWhite = *sysNodeEnd;                       // Save the white character by offset @sysNodeEnd.
        
    // Calculate the string length
    size_t strLength = (int)((ptrdiff_t)(end) - (ptrdiff_t)(str));
    size_t strLength2 = strlen(str);
    // Choose the shortest one between strlen() and substraction btw @end and @str
    strLength = (strLength<strLength2 ? strLength : strLength2);
        
    BOOL bCommandFound = FALSE;                            // flag, is TRUE if a command found
    BOOL bRequest = FALSE;                                 // is TRUE for the REQUEST command, e.g. *CMD? for "*CMD" command
  
    // Note: it is normal that @sysNodeBegin is NULL.
    // Note: it is normal that @sysNodeEnd is NULL.
  
    if( NULL != sysNodeBegin )    *sysNodeBegin = '\0';    // make the long command shorter - only one subsystem will be used til the semicolon
    if( NULL != sysNodeEnd )      *sysNodeEnd = '\0';      // make the long command shorter - replace white character with the null terminator
    // -----------------------------------------------------------------------------------------------------------
    // Validate the header
    if( TRUE != GPIB_CheckHeader(str, end) )
  //if( TRUE != GPIB_CheckHeader(str) )
    {         
        // restore the subsystem delimiter
        if( sysNodeBegin != NULL ) *sysNodeBegin = GPIB_CHAR_SCPI_SUBSYS_SEPARATOR_SIGN;
        
        // restore the white character
        if( sysNodeEnd != NULL ) *sysNodeEnd = charWhite;
        
        /* лишний код - та же самая ошибка генерируется в GPIB_CommandExecure, и она получается дублирована
        // error: raise an error
        // 27/08/18 if( usbtmc_RaiseError( udi, GPIB_ERROR_ID_EXE, ERROR_USBTMC_EXECUTION, "'", 1 ) ) 
        // 30/08/18 if( usbtmc_RaiseError( udi, errClass_Command, ERROR_USBTMC_EXECUTION, "'", 1 ) ) 
        if( usbtmc_RaiseError( udi, errClass_Command, ERROR_USBTMC_INVALID_HEADER, "'", 1 ) ) 
        {  
          //if( usbtmc_RaiseError_CatDescription( udi, str, strlen(str) ) )     // strlen(str): takes the command til the first '\0'  
            if( usbtmc_RaiseError_CatDescription( udi, str, strLength ) )
            {  
                if( !usbtmc_RaiseError_CatDescription( udi, "'", 1) )
                {
                  usbtmc_ErrorQueue_RestoreCheckpoint( udi );
                }
            }
            else
              usbtmc_ErrorQueue_RestoreCheckpoint( udi );
        }
        else
          usbtmc_ErrorQueue_RestoreCheckpoint( udi );
        */
        
        // return NULL
        return NULL;
    } 
  
    if( GPIB_CHAR_MANDATORY_IEEE488_SIGN == str[0] && strLength < 3 )
    {
    }
    else
    {
        // search for the header in the command list from specified root (@searchRoot)
        for( size_t i = 0; i < searchRoot->childcount; ++i )
        {   
            bCommandFound = FALSE;
            
            do
            {
                // -------------------------------------------------------------
                // Attempt 1.
                // -------------------------------------------------------------
                // Compare the command header            
                if(    GPIB_HeaderCompare( searchRoot->pcommands[i].header, str )        // the command header matchs
                    || GPIB_HeaderCompare( searchRoot->pcommands[i].shortheader, str ) ) // the command short header matchs
                {
                    bCommandFound = TRUE;
                    
                    // In case searchRoot->pcommands[i].enablerequest is FALSE, but
                    // the command actually is a request, the header contain a request
                    // character in the end. It is required to set @bRequst to appropriate value.
                    if( GPIB_CHAR_REQUEST_SIGN == *(sysNodeEnd - 1) ) 
                        bRequest = TRUE;   

                    break; // do-while
                }
                
                // -------------------------------------------------------------
                // Attempt 2.
                // -------------------------------------------------------------
                // Note: if @enablerequest it means the command in the list is registered
                // without question mark, e.g. ESE, but not ESE?. So it means the command
                // can be used as either "command" and "request". In this case GPIB_HeaderCompare
                // does not find full match
                // Check if the command is registered without the request mark (?).                
                if( searchRoot->pcommands[i].enablerequest )
                {
                    // get the header length
                    // Note: @str string had been cut with null-terminator above.
                    strLength = strlen( str );
                    // Check if @str contain a request character
                    if(    (GPIB_CHAR_REQUEST_SIGN == str[ strLength-1 ]) 
                        && (strLength > 1)
                      )
                    {
                        // replace the request character with a null-terminator.
                        str[ strLength-1 ] = '\0';
                        
                        // Perform another comparaion of the command header            
                        if(    GPIB_HeaderCompare( searchRoot->pcommands[i].header, str )        // the command header matchs
                            || GPIB_HeaderCompare( searchRoot->pcommands[i].shortheader, str ) ) // the command short header matchs  
                        {
                            bCommandFound = TRUE;
                            bRequest = TRUE;
                            str[ strLength-1 ] = GPIB_CHAR_REQUEST_SIGN;    // restore the request character
                            break; // do-while
                        }
                        
                        str[ strLength-1 ] = GPIB_CHAR_REQUEST_SIGN; // restore the request character
                    }
                }
            }
            while(0);
                      
            if( bCommandFound )    
            {
                // Check is there next subsystem separator in the string.
                // If there no such separator, the the whole input command string is processed.                
                if( sysNodeBegin != NULL )
                {  
                    // There are more subsystem separators
                    
                    *sysNodeBegin = GPIB_CHAR_SCPI_SUBSYS_SEPARATOR_SIGN;       // restore subsystem separator
                    
                    if( sysNodeEnd != NULL ) *sysNodeEnd = charWhite;           // restore white character
                    
                    udi->usbtmcGpib.gpib_owner = udi->usbtmcGpib.gpib_search;   // save the command owner: searchRoot->pcommands[i]
                    udi->usbtmcGpib.gpib_search = &searchRoot->pcommands[i];    // set the root for the searching in the next recursive call
                    
                    sysNodeBegin++;                                             // skip subsystem separator
                    
                    return GPIB_Parse( udi, sysNodeBegin, bRequest, end );      // parse the command recursive in the found command
                }
                else                                                            // if the input string is processed til the end
                {
                    if( sysNodeEnd != NULL )                                    // if there is a white character in the tail of header
                    {                                                           // Note: a white character always presents.
                        *sysNodeEnd = charWhite;                                // restore white character
                      
                      
                        
                        size_t nRespondBytes = 0;                               // @nRespondBytes - amount of sent bytes
                        size_t nOffset = 0;
                      
                        // It is required to insert the delimiter between replies
                        // relating to the different input requests in case more than one
                        // command is in the string.
                        if( bRequest )
                        {
                            /*
                            // pointer to the buffer for respond
                            BULKIN_HEADER * pBulkInHeader= (BULKIN_HEADER*) udi->EPBulkStatus.InPipe.pData;
                            
                            // if there are any bytes in the respond buffer - it must be a reply of another command
                            if( 0 < pBulkInHeader->stRespondMessage.TransferSize )
                            { 
                                // insert a delimiter
                                udi->usbtmcGpib.pData[0] = GPIB_CHAR_RPLSEPARATOR_SIGN;
                                udi->usbtmcGpib.pData++;
                                nOffset++;
                            }
                          */
                            if( udi->BulkRespondStatus.dwDeviceOut > 0 )
                            {
                                // insert a delimiter
                                udi->usbtmcGpib.pData[0] = GPIB_CHAR_RPLSEPARATOR_SIGN;
                                udi->usbtmcGpib.pData++;
                                nOffset++;
                            }
                        }                                    
                      
                      
                        size_t paramLength = 0;
                        // -------------
                        // If the end of header reached - set @sysNodeEnd to NULL, and set @paramLength to 0
                        sysNodeEnd++;
                        if( sysNodeEnd >= end )
                            
                            sysNodeEnd = NULL;
                        else
                            paramLength = SubPointers( end, sysNodeEnd );
                        // -------------
                                             
                        // Enumerate parameters and pack it into the queue
                        QUEUE QParameters; 
                        queue_create( SCPIBParam, MEMORY_SIZE_PARAMQUEUE, &QParameters );  // @SCPIBParam - global and static buffer

                        // Check command tag:
                        if(searchRoot->pcommands[i].bTagArgUsed == TRUE)
                        {   
                            // push the tag-parameter into the queue
                            queue_add(  &QParameters, 
                                        (char*)&searchRoot->pcommands[i].xTagArg, 
                                        sizeof(searchRoot->pcommands[i].xTagArg) );
                        }

                        // Allow unlimited amount of non-request commands, and limited amount of requests
                        if( !bRequest || udi->usbtmcGpib.cRequestCountSupport > 0 )      
                        {
                            // request: decrease amount of allowed requests (@cRequestCountSupport)
                            if( bRequest ) udi->usbtmcGpib.cRequestCountSupport--;
                            
                            int rcEnum = 1;
                            
                            if( NULL != sysNodeEnd && 0 != paramLength )
                            {
                                // Enumerate all the parameters and pack it into the queue @QParameters
                                rcEnum = GPIB_EnumParameters( sysNodeEnd, paramLength, &QParameters, NULL );
                            }
                                                        
                            if( rcEnum > 0 )
                            {
                                QParameters.nStringLen = paramLength;
                                QParameters.pString = sysNodeEnd;
                                // ----------------------------------------------------------------------------
              
                                // It is required to keep command context to give
                                // enough time the current command to send it's reply.
                                // --- usbtmc_delete_function_context( udi ) ---
                            
                                // see USBTMC spec, rev 1.0, 2003, page 16, table 12, index 1
                                // Stall and abort the endpoint in case the IRQ-transfer is in progress
                                if(udi->BulkRespondStatus.INTransferInProgress == TRUE && bRequest)
                                    usbtmc_bulkin_stall_and_abort( udi );
              
                                // call the command processor                                
                                nRespondBytes += (searchRoot->pcommands[i].function( udi, &QParameters, bRequest ));  
                            
                                udi->usbtmcGpib.pData += nRespondBytes;                                  // shift the pointer of the reply buffer
                                udi->BulkRespondStatus.RespondBufferSize -= (nRespondBytes + nOffset);   // and decrease amount of free space.
                                udi->BulkRespondStatus.dwDeviceOut += (nRespondBytes + nOffset);         // Increase of output transfer.
                            } 
                            else
                            {  
                                // 27/08/18
                                // GPIB_RaiseStandartError( udi, ERROR_GPIB_INVALID_ENUM_PARAMETERS, NULL, GPIB_ERROR_ID_EXE );
                                GPIB_RaiseStandartError( udi, ERROR_GPIB_INVALID_ENUM_PARAMETERS, NULL, errClass_Autodetect );
                            }    
                        }    
                        else
                        {  
                            // 27/08/18
                            // GPIB_RaiseStandartError( udi, ERROR_GPIB_TOO_MANY_REQUESTS, NULL, GPIB_ERROR_ID_EXE );
                            GPIB_RaiseStandartError( udi, ERROR_GPIB_TOO_MANY_REQUESTS, NULL, errClass_Command );
                        }    
                        
                      return searchRoot;
                    }
                    
                    break;
                }
            }
        } 
    }
    
    // Reach this point in case the command header not found.
    if( bCommandFound == FALSE )
    {      
        sScpiCommand_t * pseudo_result; 
        sScpiCommand_t * SearchSave;
        sScpiCommand_t * OwnerSave;
        
        searchRoot = udi->usbtmcGpib.gpib_owner;    // get the owner for the search
                                                    // it is possible that optional header is omitted
                                                                  
        if( sysNodeBegin != NULL ) *sysNodeBegin = GPIB_CHAR_SCPI_SUBSYS_SEPARATOR_SIGN;  // restore subsystem separator
        if( sysNodeEnd != NULL ) *sysNodeEnd = charWhite;                                 // restore white character
  
        for( size_t i = 0; i < searchRoot->childcount; ++i ) 
        {    
            if(    IsOptionalHeader( searchRoot->pcommands[i].shortheader ) 
                || IsOptionalHeader( searchRoot->pcommands[i].header ) )        // optional header found
            {  
                /*!*/SearchSave = udi->usbtmcGpib.gpib_search;                  // save the search pointer
                /*!*/OwnerSave  = udi->usbtmcGpib.gpib_owner;                   // save the owner command
  
                udi->usbtmcGpib.gpib_owner  = udi->usbtmcGpib.gpib_search;
                udi->usbtmcGpib.gpib_search = &searchRoot->pcommands[i];        // change the search point for a while
  
                pseudo_result = GPIB_Parse( udi, str, request, end);            // call the parser recursively
  
                /*!*/udi->usbtmcGpib.gpib_search = SearchSave;                  // restore the search pointer
                /*!*/udi->usbtmcGpib.gpib_owner  = OwnerSave;                   // restore the owner command
  
                if( pseudo_result != NULL ) 
                       return pseudo_result;                                    // command found among  optional commands
            }
        }   
   }  
  
   return NULL;
}
/*
//-------------------------------------------------------------------------------
//
// Parameters: str    - указатель на одну комманду. Пример, "SYSTem:TRIGger"
//                      Комманда ":SYSTem:TRIGger" некорректна, так как имеется незначащее двоеточие в начале.
//                      Нужно убирать двоеточие в начале   
sScpiCommand_t * parse( USB_DEVICE_INFO * udi, char * str, BOOL request, char * end)
{
  sScpiCommand_t * Search = udi->usbtmcGpib.gpib_search;   // -- нод дерева, с которого начинается поиск
  BULKIN_HEADER * pBulkInHeader= (BULKIN_HEADER*) udi->EPBulkStatus.InPipe.pData; // указатель на начало буфера-ответа, указатель на заголовок Bulk-IN
  // -----------------------------------------------------------------------------------------------------------
  if( !IsCorrectPointer(Search) )  return NULL;        // -- проверяем корректность указателя
  // -----------------------------------------------------------------------------------------------------------
  char * p = GPIB_StrChr_rw( str, GPIB_CHAR_SCPI_SUBSYS_SEPARATOR_SIGN, end);              // -- ищем следующий нод в строке
  char * s = strwhite( str );                          // -- ищем завершающий символ команды
  char _s = *s;                                        // -- сохраняем завершающий символ комманды  
  int   l;                                             // -- хранит длинну строки
  BOOL bCommandFound = FALSE;                          // -- комманда ненайдена
  BOOL q = FALSE;                                      // -- равен TRUE если пришел запрос "*REQUEST?", Хотя имеется комманда "*REQUEST"
  if( p != NULL ) *p = 0;                              // -- если нашли след. нод, укорачиваем комманду, делая из последовательности комманд одну комманду
  if( s != NULL ) *s = 0;                              // -- если нашли завершающий символ, заменяем его на нуль терминатор
  // -----------------------------------------------------------------------------------------------------------
  if( GPIB_CheckHeader(str) != TRUE )
  {    
       // сначала генерим ошибку, strlen дойдет до '\0'
       //  а потом уже вернем пробел на место
       if( usbtmc_RaiseError( udi, GPIB_ERROR_ID_EXE, ERROR_USBTMC_EXECUTION, "'", 1 ))
        if(usbtmc_RaiseError_CatDescription( udi, str, strlen(str)))
           usbtmc_RaiseError_CatDescription( udi, "'", 1);
       if( p != NULL ) *p = GPIB_CHAR_SCPI_SUBSYS_SEPARATOR_SIGN;                      // -- восстанавливаем двоеточие
       if( s != NULL ) *s = _s;                       // -- восстанавливаем white символ, (если это например пробел, отделяющий данные)
  } 
  else
  {
   if(!( *str == GPIB_CHAR_MANDATORY_IEEE488_SIGN && strlen(str)<=2 ))
    for(int i=0; i< Search->childcount; i++)           // -- ищем по текущему ноду поиска нужную комманду
        // ------ проверяем совпадения комманд...
        // --------------------------------------
        if(  TRUE==GPIB_HeaderCompare( Search->pcommands[i].header, str ) ||      // -- или если совпало имя комманды
             TRUE==GPIB_HeaderCompare( Search->pcommands[i].shortheader, str ) || // -- или если совпала аббревиатура комманды 
               // ---------- проверяем совпадение запроса...............
               // -- проверка дальше, если имя и аббревиатура не совпали
                    (Search->pcommands[i].enablerequest == TRUE &&  // -- или если разрешено сравнение без '?'
                              ( // ---------- проверяем, есть ли в конце запросный символ '?'              
                                ((l = strlen( str )) && (str[l-1] == '?')) &&          // ---- и '?' есть в конце строки
                                         (  // --------- символ есть, сравниваем без символа                          
                                          0==strncmp( Search->pcommands[i].header, str, l-1 ) ||    // -- сверяем имя без '?'
                                          0==strncmp( Search->pcommands[i].shortheader, str, l-1 )  // -- сверяем аббр без '?'
                                         ) && (l>1) &&  ((q = TRUE),q)                                  // -- если все условия выполнены, q=TRUE
                                            
                              )
                    )  
          )    
        {
         bCommandFound = TRUE;
         if (p != NULL )                                            // -- если строка разобрана не полностью
         {  if( p != NULL ) *p = GPIB_CHAR_SCPI_SUBSYS_SEPARATOR_SIGN;                               // -- восстанавливаем двоеточие
            if( s != NULL ) *s = _s;                                // -- восстанавливаем white символ, (если это например пробел, отделяющий данные)
            udi->usbtmcGpib.gpib_owner = udi->usbtmcGpib.gpib_search; // сохраняем владельца комманды Search->pcommands[i]
            udi->usbtmcGpib.gpib_search = &Search->pcommands[i];
            return parse( udi, p+1, q, end);         // -- рекурсивно парсим следующую комманду. поиск в уже найденом ноде
         }
         else                                                       // -- если строка разобрана полностью
            if( s != NULL )                                         // -- если имеется white символ, а он всегда имеется
            { *s = _s;                                              // -- восстанавливаем его
              
              
              int dwRespondBytes=0;                                 // -- dwRespondBytes - количество байт, отправляемых в ответ на комманду
              int dwOffset=0;
              
              
              if( q == FALSE && *(s-1) == '?' ) q = TRUE;   // если комманда добавлена как RequestOnly, ее header содержит '?'. q=FALSE если комманда RequestOnly. Эта строка исправляет ситуацию
              // --------------------- вставка разделителя ; в ответное сообщение между ответами ------------------------
              // --------------------------------------------------------------------------------------------------------
              if(q==TRUE                                         // -- если пришел ЗАПРОС
                 && 
                 pBulkInHeader->stRespondMessage.TransferSize    // -- если уже были данные в буфере от предыдущей комманде
                 && 
                 (++dwOffset))                                   // -- увеличиваем Offset
                              (udi->usbtmcGpib.pData++)[0] = ';';                // -- вставляем разделитель
              // --------------------------------------------------------------------------------------------------------
              
              
              

              if( end <= ++s ) s = NULL;  // если конец строки или конец комманды (разделитель следом) то s=NULL
              
              unsigned int strl = SubPointers(end,s);
               
              // -------------------------- энумеруем параметры и ложим их в очередь --------
              QUEUE QParameters; 
              queue_create( SCPIBParam, MEMORY_SIZE_PARAMQUEUE, &QParameters );  // буфер очереди глобальный и статический.

              if(Search->pcommands[i].bTagArgUsed == TRUE)
                queue_add( &QParameters, (char*)&Search->pcommands[i].xTagArg, sizeof(Search->pcommands[i].xTagArg) );

                 if( !q || (q && udi->usbtmcGpib.cRequestCountSupport-- > 0) )      
                    if( GPIB_EnumParameters( s, strl, &QParameters, NULL ) != -1 )
                    {
                          QParameters.strl = strl;
                          QParameters.str = s;
                          // ----------------------------------------------------------------------------
      
                          //-- перед выполнением GPIB функции  нельзя удалять контекст, 
                          //-- ведь след комманда может его и не использует,
                          //-- а текущая не успеет передать данные
                          //usbtmc_delete_function_context( udi );
                    
                          // see USBTMC spec, rev 1.0, 2003, page 16, table 12, index 1
                          if(udi->BulkRespondStatus.INTransferInProgress  == TRUE   &&   q) 
                                                       usbtmc_bulkin_stall_and_abort( udi );
      
      
                          dwRespondBytes+= (Search->pcommands[i].function( udi, &QParameters, q ));  // -- вызываем обработчик комманды, передавая ему оставшуюся часть строки после white символа
                    
                          udi->usbtmcGpib.pData+=dwRespondBytes;                                     // -- сдвигаем указатель буфера, в который складываются ответы
                          udi->BulkRespondStatus.RespondBufferSize -= (dwRespondBytes+dwOffset);     // -- уменьшаем объем доступного места в буфере
                           udi->BulkRespondStatus.dwDeviceOut+=(dwRespondBytes+dwOffset);            // -- увеличиваем размер ответной передачи    

                    } else GPIB_RaiseStandartError( udi, ERROR_GPIB_INVALID_ENUM_PARAMETERS, NULL, GPIB_ERROR_ID_EXE );
              else GPIB_RaiseStandartError( udi, ERROR_GPIB_TOO_MANY_REQUESTS, NULL, GPIB_ERROR_ID_EXE );      
              return Search;
            }
         break;
        }
   
   // -- сюда мы попадем если комманда не найдена!
   if( bCommandFound == FALSE )
   {      
          sScpiCommand_t * pseudo_result; 
          sScpiCommand_t * SearchSave;
          sScpiCommand_t * OwnerSave;
          Search = udi->usbtmcGpib.gpib_owner; // получаем хозяина
                                                                  // -- возможно опущен необязательный заголовок!
          if( p != NULL ) *p = GPIB_CHAR_SCPI_SUBSYS_SEPARATOR_SIGN;                               // -- восстанавливаем двоеточие
          if( s != NULL ) *s = _s;  
  
          for(int i=0; i< Search->childcount; i++) 
           if( IsOptionalHeader(Search->pcommands[i].shortheader) || IsOptionalHeader(Search->pcommands[i].header)) // если найдена опциональная комманда
           {  
                // !!!!!!!!!!!!!
              SearchSave = udi->usbtmcGpib.gpib_search;   // сохраняем текущий указатель на точку поиска
              OwnerSave  = udi->usbtmcGpib.gpib_owner;    // сохраняем текущий указатель на владельца комманды
                // !!!!!!!!!!!!!
              udi->usbtmcGpib.gpib_owner  = udi->usbtmcGpib.gpib_search;
              udi->usbtmcGpib.gpib_search = &Search->pcommands[i]; // изменяем на время текущий указатель на точку поиска
  
              pseudo_result = parse( udi, str, request, end);
  
                // !!!!!!!!!!!!!
              udi->usbtmcGpib.gpib_search = SearchSave;            // восстанавливаем прежний указатель на точку поиска
              udi->usbtmcGpib.gpib_owner  = OwnerSave;            // восстанавливаем прежний указатель на владельца комманды
               // !!!!!!!!!!!!!
              if( pseudo_result != NULL ) return pseudo_result;    // ура, нашли комманду в дерева в одном из опциональных нодов
           } 
   }  
  } // CheckHeader

   return NULL;
}
*/
//----------------------------------------------------------------------
// GPIB_InitializeAllCommands:
// Create a list of supported commands of the device.
sScpiCommand_t * GPIB_InitializeAllCommands()
{
    // Allocate memory for the root command
    sScpiCommand_t * root = (sScpiCommand_t *)malloc( sizeof(sScpiCommand_t) );

    if( !IsCorrectPointer(root) )
        return NULL;
//  -------------------------------------------------------------------------------------------------
    // Create a root command:
    root = GPIB_InitializeCommand( root,    "root" , NULL, GPIB_DUMMY_FUNCTION, 16, FALSE );
//  -------------------------------------------------------------------------------------------------  
    // Mandatory commands, add as a child of root command:
    GPIB_AddChildCommand( root,                 "*IDN?",       "*IDN?",    GPIB_Identification      , 0, FALSE);
    GPIB_AddChildCommand( root,                 "*CLS" ,       "*CLS" ,    GPIB_Clear               , 0, FALSE);
    GPIB_AddChildCommand( root,                 "*RST" ,       "*RST" ,    GPIB_Reset               , 0, FALSE);
    GPIB_AddChildCommand( root,                 "*ESE" ,       "*ESE" ,    GPIB_EventStatusEnable   , 0, TRUE );
    GPIB_AddChildCommand( root,                 "*ESR" ,       "*ESR" ,    GPIB_EventStatusRegister , 0, TRUE ); 
    GPIB_AddChildCommand( root,                 "*STB" ,       "*STB" ,    GPIB_StatusRegister      , 0, TRUE );
    GPIB_AddChildCommand( root,                 "*SRE" ,       "*SRE" ,    GPIB_StatusEnableRegister, 0, TRUE ); 
    GPIB_AddChildCommand( root,                 "*TRG" ,       "*TRG" ,    GPIB_Trigger             , 0, FALSE);  // ?
    GPIB_AddChildCommand( root,                 "*WAI" ,       "*WAI" ,    GPIB_Wait                , 0, FALSE);  // ?
    GPIB_AddChildCommand( root,                 "*OPC" ,       "*OPC" ,    GPIB_OperationComplete   , 0, TRUE );
    // -------------------------------------------------------------------------------------------------
    // Child commands of root:
    sScpiCommand_t * gpib_Mem =     
    GPIB_AddChildCommand( root,                 "MEMORY",      "MEM",      GPIB_DEF_FUNCTION        , 1, FALSE );    
    // -------------------------------------------------------------------------------------------------
    // Child commands of "MEMORY":
    sScpiCommand_t * gpib_MemTable =     
    GPIB_AddChildCommand( gpib_Mem,             "TABLE",       "TABL",     GPIB_DEF_FUNCTION        , 12, FALSE );
    // -------------------------------------------------------------------------------------------------
    // Create child commands for "MEMORY:TABLE":

    GPIB_AddChildCommand( gpib_MemTable,        "POINTS",      "POIN",     GPIB_MemoryTablePoints   , 0, TRUE );
    GPIB_AddChildCommand( gpib_MemTable,        "DATE",        "DATE",     GPIB_MemoryTableDate     , 0, TRUE );
    GPIB_AddChildCommand( gpib_MemTable,        "TIME",        "TIME",     GPIB_MemoryTableTime     , 0, TRUE );
    GPIB_AddChildCommand( gpib_MemTable,        "TEMPERATURE", "TEMP",     GPIB_MemoryTableTemp     , 0, TRUE );
    GPIB_AddChildCommand( gpib_MemTable,        "CONNECTOR",   "CONN",     GPIB_MemoryTableConnector, 0, TRUE );
    GPIB_AddChildCommand( gpib_MemTable,        "ADAPTER",     "ADAP",     GPIB_MemoryTableAdapter  , 0, TRUE );
    GPIB_AddChildCommand( gpib_MemTable,        "ANALYZER",    "ANAL",     GPIB_MemoryTableAnalyzer , 0, TRUE );
    GPIB_AddChildCommand( gpib_MemTable,        "OPERATOR",    "OPER",     GPIB_MemoryTableOperator , 0, TRUE );
    GPIB_AddChildCommand( gpib_MemTable,        "PLACE",       "PLAC",     GPIB_MemoryTablePlace    , 0, TRUE );
    GPIB_AddChildCommand( gpib_MemTable,        "DATA",        "DATA",     GPIB_MemoryTableData     , 0, TRUE );

    // ---------------------------------------------------------------------------------------------------------------            
    // Child commands of "MEMORY:TABLE":  
    
    sScpiCommand_t * gpib_MemTableTher = 
    GPIB_AddChildCommand(   gpib_MemTable,      "THERMO",      "THER",     GPIB_DEF_FUNCTION        , 1, FALSE );
    
    sScpiCommand_t * gpib_MemTableTherCorr = 
    GPIB_AddChildCommand(   gpib_MemTableTher,  "CORRECTION",  "CORR",     GPIB_DEF_FUNCTION        , 4, FALSE );

    // ---------------------------------------------------------------------------------------------------------------            
    // Child commands of "MEMORY:TABLE:THERMO:CORRECTION":   
    
    //GPIB_MakeSpecialCommand(
    GPIB_AddChildCommand( gpib_MemTableTherCorr,"MAGNITUDE",   "MAGN",     GPIB_MemTableTherCorrMagniture, 0, TRUE );
    GPIB_AddChildCommand( gpib_MemTableTherCorr,"PHASE"    ,   "PHAS",     GPIB_MemTableTherCorrPhase,     0, TRUE );
    GPIB_AddChildCommand( gpib_MemTableTherCorr,"POINTS"   ,   "POIN",     GPIB_MemTableTherCorrPoints,    0, TRUE );
                                                                           
    sScpiCommand_t * gpib_MemTableTherCorrFreq = 
    GPIB_AddChildCommand( gpib_MemTableTherCorr,"FREQUENCY",   "FREQ",     GPIB_DEF_FUNCTION,              2, FALSE );

    // ---------------------------------------------------------------------------------------------------------------            
    // Child commands of "MEMORY:TABLE:THERMOCOMPENSATION:FREQUENCY":  
    GPIB_AddChildCommand( gpib_MemTableTherCorrFreq, "START",    "STAR",   GPIB_MemTableTherCorrFreqStart, 0, TRUE );
    GPIB_AddChildCommand( gpib_MemTableTherCorrFreq, "STOP" ,    "STOP",   GPIB_MemTableTherCorrFreqStop,  0, TRUE );

    // ---------------------------------------------------------------------------------------------------------------            
    // Child commands of "MEMORY:TABLE":    
    sScpiCommand_t * gpib_MemTableFreq = 
    GPIB_AddChildCommand( gpib_MemTable,        "FREQUENCY",   "FREQ",      GPIB_DEF_FUNCTION,         5, FALSE );    
    // ---------------------------------------------------------------------------------------------------------------
    // Child commands of "MEMORY:TABLE:FREQUENCY":    
    
    // MEMORY:TABLE:FREQUENCY:START?                          
    GPIB_AddChildCommand( gpib_MemTableFreq,   "START",       "STAR",      GPIB_MemoryTableFreqStart, 0, TRUE ),

    // MEMORY:TABLE:FREQUENCY:STOP?                          
    GPIB_AddChildCommand( gpib_MemTableFreq,   "STOP",        "STOP" ,     GPIB_MemoryTableFreqStop,  0, TRUE ),
    
    // MEMORY:TABLE:FREQUENCY:TYPE?       
    GPIB_AddChildCommand( gpib_MemTableFreq,    "TYPE",        "TYPE",      GPIB_MemoryTableFreqType,  0, TRUE );
        
    sScpiCommand_t * gpib_MemTableFreqSegm = 
    // MEMORY:TABLE:FREQUENCY:SEGM     
    GPIB_AddChildCommand( gpib_MemTableFreq,    "SEGM",        "SEGM",      GPIB_DEF_FUNCTION,         1, FALSE );
    
    // MEMORY:TABLE:FREQUENCY:SEGM:DATA?   
    GPIB_AddChildCommand( gpib_MemTableFreqSegm,"DATA",        "DATA",      GPIB_MemoryTableFreqSegmData,0,TRUE );
    
    // MEMORY:TABLE:FREQUENCY:DATA?   
    GPIB_AddChildCommand( gpib_MemTableFreq,    "DATA",        "DATA",      GPIB_MemoryTableFreqData,  0, TRUE );
    // ---------------------------------------------------------------------------------------------------------------
    // Child commands of root:
    sScpiCommand_t * gpib_DeviceDependent = 
    GPIB_AddChildCommand( root,                 "MEASURE",     "MEAS",      GPIB_DEF_FUNCTION,         1, FALSE);             //root function 8
    // ---------------------------------------------------------------------------------------------------------------
    // Child commands of "MEASURE":
    GPIB_AddChildCommand( gpib_DeviceDependent, "TEMPERATURE", "TEMP",      GPIB_GetTemperature,       0, TRUE);             //Cчитывает температуру внутри калибровочного модуля  (только запрос).
    // ---------------------------------------------------------------------------------------------------------------
    // Child commands of root:
    sScpiCommand_t * gpib_Interface =  
    GPIB_AddChildCommand( root,                 "INTERFACE",   "INT",       GPIB_DEF_FUNCTION,         1, FALSE );           //root function 7  
    // ---------------------------------------------------------------------------------------------------------------
    // Child commands of "INTERFACE":
    GPIB_AddChildCommand( gpib_Interface,       "SWITCH",      "SW",        GPIB_InterfaceSwitch,      0, TRUE ); 
    // ---------------------------------------------------------------------------------------------------------------
    // Child commands of root:
    sScpiCommand_t * gpib_System = 
    GPIB_AddChildCommand( root,                 "[SYSTEM]",    "[SYST]",    GPIB_DEF_FUNCTION,         3, FALSE );              //root function 4
    // ---------------------------------------------------------------------------------------------------------------
    // Child commands of "SYSTEM": 
    GPIB_AddChildCommand( gpib_System,          "VERSION?",    "VERS?",     GPIB_SystemVersion,        0, FALSE ); 
    sScpiCommand_t * gpib_SystemErr = 
    GPIB_AddChildCommand( gpib_System,          "ERROR",       "ERR",       GPIB_SystemError,          2, TRUE ); 
    sScpiCommand_t * gpib_Source =
    GPIB_AddChildCommand( gpib_System,          "[SWITCH]",    "[SWIT]",    GPIB_DEF_FUNCTION,         3, FALSE );             //root function 9 
    // ---------------------------------------------------------------------------------------------------------------
    // Child commands of "SYSTEM:ERR": 
    GPIB_AddChildCommand( gpib_SystemErr,       "NEXT?",       "NEXT?",     GPIB_SystemError,          0, FALSE); 
    GPIB_AddChildCommand( gpib_SystemErr,       "ALL?",        "ALL?",      GPIB_SystemErrorAll,       0, FALSE);
    // ---------------------------------------------------------------------------------------------------------------
    // Child commands of "SYSTEM:SWITCH"
    GPIB_AddChildCommand( gpib_Source,          "COUNT",       "COUN",      GPIB_SwitchCount,          0, TRUE );
    GPIB_AddChildCommand( gpib_Source,          "STATE",       "STAT",      GPIB_SwitchState,          0, TRUE );
    GPIB_AddChildCommand( gpib_Source,          "LIST",        "LIST",      GPIB_SwitchList,           0, TRUE ); 
    // ---------------------------------------------------------------------------------------------------------------
    
    // Child commands of root:
    // sScpiCommand_t * gpib_Status   =  
    // GPIB_AddChildCommand( root,                 "STATUS",      "STAT",      GPIB_DEF_FUNCTION,         3, FALSE );              //root function 5                
    // ---------------------------------------------------------------------------------------------------------------
    // Child commands of "STATUS":
    // GPIB_AddChildCommand( gpib_Status,          "PRESET",       "PRES",     GPIB_StatusPreset, 0, FALSE );     // No Subcomands                         
    // sScpiCommand_t * gpib_StatusOperation  = 
    // GPIB_AddChildCommand( gpib_Status,          "OPERATION",   "OPER",      GPIB_StatusOperationEvent, 3, TRUE );   // 3 Subcommands, Request Enable -> EVENt?
    // sScpiCommand_t * gpib_StatusQuestionable = 
    // GPIB_AddChildCommand( gpib_Status,          "QUESTIONABLE","QUES",      GPIB_StatusQuestionableEvent,  3, TRUE );  // 3 Subcommands, Request Enable -> EVENt?
    // ---------------------------------------------------------------------------------------------------------------
    // Child commands of "STATUS:OPERATION":    
    // GPIB_AddChildCommand( gpib_StatusOperation, "EVENT?",      "ENENt?",    GPIB_StatusOperationEvent,     0, FALSE );   // No Subcomands, Request Only
    // GPIB_AddChildCommand( gpib_StatusOperation, "CONDition?",  "COND?",     GPIB_StatusOperationCondition, 0, FALSE );  // No Subcomands, Request Only
    // GPIB_AddChildCommand( gpib_StatusOperation, "ENABLE",      "ENAB",      GPIB_StatusOperationEnable,    0, TRUE );  // No Subcomands, Request enable    
    // ---------------------------------------------------------------------------------------------------------------
    // Child commands of "STATUS:QUESTIONABLE":
    // GPIB_AddChildCommand( gpib_StatusQuestionable, "EVENT?",    "ENENt?",   GPIB_StatusQuestionableEvent,     0, FALSE ); // No Subcomands, Request Only
    // GPIB_AddChildCommand( gpib_StatusQuestionable, "CONDition?","COND?",    GPIB_StatusQuestionableCondition, 0, FALSE ); // No Subcomands, Request Only
    // GPIB_AddChildCommand( gpib_StatusQuestionable, "ENABLE",    "ENAB",     GPIB_StatusQuestionableEnable,    0, TRUE );  // No Subcomands, request enable
     // ---------------------------------------------------------------------------------------------------------------      
  
  return root;
}

// GPIB_TrimCommand:
// Removes either the leading and trailing white characters from the command string
// Returns the length of command
int GPIB_TrimCommand( char * begin, char * end, char * * pCmdStart, char * * pCmdEnd )
{
    
    // check the input pointers
    if( NULL == end || NULL == begin || NULL == pCmdStart || NULL == pCmdEnd )
        return -1;
        
    if( SubPointers(end, begin) <= 0 )
        return -1;

    // Skip leading white characters
    while( IsWhiteOrEndChar( *begin ) )
    {
        begin++;
    }
    
    // Skip trailing white characters
    do
    {
        end--;
    }
    while( IsWhiteOrEndChar( *end ) );
    
    end++;
    
    *pCmdEnd = end;
    *pCmdStart = begin;
    
    return SubPointers( end, begin );
}

// GPIB_SearchCommand:
// Searches for the end of command in the string @begin til the @end.
// @ppCmdEnd - the pointer to the receiving cell of pointer to the end of command
// Note: the function processes GPIB strings and GPIB datablocks.
int GPIB_SearchCommand( const char * begin, const char * end, const char * * ppCmdEnd )
{
    // check the input pointers
    if( NULL == end || NULL == begin )
        return -1;
    
    // check the input pointers
    if( SubPointers(end, begin) <= 0 )
        return -1;
    
    const char * iter = begin;
    const char * near;
    
    do
    {    
        // Search for special characters in the string since @iter til the @end
        const char * separator = GPIB_StrChr( iter, GPIB_CHAR_CMDSEPARATOR_SIGN, end );   // search for the command separator
        const char * datablock = GPIB_StrChr( iter, GPIB_CHAR_DATATABLE_SIGN,    end );   // search for the datablock
        const char * squote    = GPIB_StrChr( iter, GPIB_CHAR_SINGLEQUOTE_SIGN,  end );   // search for the quoted string
        const char * dquote    = GPIB_StrChr( iter, GPIB_CHAR_DOUBLEQUOTE_SIGN,  end );   // search for the quoted string
        
        // Select the first faced special character in the string since @iter til the @end
        near = ChooseLowestPtrConst( squote, dquote );
        near = ChooseLowestPtrConst( near, separator );
        near = ChooseLowestPtrConst( near, datablock );
        
        // check the simplest case: no separator characters found (and any other special characters)
        if( NULL == near )
        {
            // the whole string [@begin ... @end] is a command
            if( NULL != ppCmdEnd ) *ppCmdEnd = end;
            
            // return the length of command
            return SubPointers( end, begin );
        }
        
        // check if the first command does contain neither datablock or quotes
        // In this case a separator is faced first
        if( separator == near )
        {
            // the  string [@iter ... @separator] is a command
            if( NULL != ppCmdEnd ) // fixed: 23/08/18
            {  
                *ppCmdEnd = separator;
            }
            
            // return the length of command
            return SubPointers( separator, iter );
        }
        
        // check if the double quote is faced first        
        if( dquote == near )
        {
            // the string contains GPIB string. It is required to skip it.
            
            // search a closing quote
            dquote = GPIB_StrChr( iter, GPIB_CHAR_DOUBLEQUOTE_SIGN, end );
            
            if( NULL == dquote )
            {
                // error: unpaired quote found
                return -1;
            }

            // skip the quoted string
            iter = (dquote + 1);
            
            continue;
        }
        
        // check if the single quote is faced first        
        if( squote == near )
        {
            // the string contains GPIB string. It is required to skip it.
            
            // search a closing quote
            squote = GPIB_StrChr( iter, GPIB_CHAR_SINGLEQUOTE_SIGN, end );
            
            if( NULL == squote )
            {
                // error: unpaired quote found
                return -1;
            }

            // skip the quoted string
            iter = (squote + 1);
            
            continue;
        }
        
        // check if the datablock is faced first
        if( datablock == near )
        {
            size_t nDataBlock;
            
            // check if it is valid datablock
            if( IsDataBlock( datablock, SubPointers(end, datablock), &nDataBlock, NULL ) )
            {
                // datablock is valid
                
                // skip datablock
                iter = (datablock + nDataBlock);
            }
            else
            {
                // datablock is invalid
                
                // skip the datablock signature
                // maybe, it is not datablock.
                iter = (datablock + 1);
            }
            
            continue;
        }
    }
    // continue until the end of string (@end is reached)
    // and interrupt in case @near is null.
    while( (NULL != near) && SubPointers(end, iter) > 0 );
        
    // no separator found
    
    if( NULL != ppCmdEnd ) *ppCmdEnd = end;
    
    // the whole string is a command
    // return the length of command
    return SubPointers( end, begin );
}

// -----------------------------------
// GPIB_CommandExecute() выполнение строки комманд GPIB.
//     root - указатель на root комманду дерева комманд
//     GPIB_Commands_String - указатель на стоку комманд GPIB, разделенных точкой с запятой
int GPIB_CommandExecute( USB_DEVICE_INFO * udi, const char * input_str ) 
{
    size_t length = udi->BulkMessageStatus.OutTransferSize;    // get the input string length
    
    char * iter = GPIB_StrCopyToCache( input_str, length );    // bufferize the string into internal cache
                                                               // now @iter points to the cached string    
    if( NULL == iter )                                         // Check if the string is cached successfully
        return -1;                                             // error: cache overflow 
    
    char * end = iter + length;                                // end of input string
    
    // Check the end of string: check the last character
    if( GPIB_CHAR_ENDLINE_SIGN == *(end-1) )                        
    {     
        // Shift @end back for one character
        // Now: @end points to end-of-line character
        end --;                                                
    }                                                          
    else                                                       
    {   
        // error: end-of-line character not found.
        // Maybe, input string is too long.
        return -1;                                             
    }                                                          
    //--------------------------------------------------------------------------
    sScpiCommand_t * xResult;                                   // pointer to the processed command
    sScpiCommand_t * pCmdRoot = udi->usbtmcGpib.gpib_root;      // root-command
    sScpiCommand_t * pCmdSearch = pCmdRoot;                     // search pointer in the tree
    //--------------------------------------------------------------------------
    udi->usbtmcGpib.cRequestCountSupport = 1;
    //--------------------------------------------------------------------------
    udi->usbtmcGpib.gpib_owner = pCmdRoot;                      // search in ROOT by default
    //--------------------------------------------------------------------------
    size_t nCmdProcessed = 0;                                   // count of processed commands   
    int nCmdLength = 0;
    
    
    do
    {
        // Skip the only first command separator
       if( GPIB_CHAR_CMDSEPARATOR_SIGN == *iter )
       {  
          iter++;
       }
    
        // Search for the end of the command
        nCmdLength = GPIB_SearchCommand( iter, end, NULL );
        
        if( nCmdLength > 0 )
        {
            char * cmdStart;
            char * cmdEnd;
            
            // Trim the command
            if( GPIB_TrimCommand( iter, (iter + nCmdLength), &cmdStart, &cmdEnd ) > 0 )
            {                
                if( GPIB_CHAR_MANDATORY_IEEE488_SIGN == *cmdStart )
                {
                    pCmdSearch = pCmdRoot;  // change the search pointer to the root
                }
                else
                if( GPIB_CHAR_SCPI_SUBSYS_SEPARATOR_SIGN == *cmdStart )
                {
                    pCmdSearch = pCmdRoot;  // change the search pointer to the root
                    cmdStart++;                 // skip subsystem separator
                }                
       
                udi->usbtmcGpib.gpib_search = pCmdSearch;     
                udi->usbtmcGpib.sgpib_current = cmdStart;

                xResult = GPIB_Parse( udi, cmdStart, FALSE, cmdEnd );
               
                if( NULL != xResult )
                {
                    // Success
                    nCmdProcessed++;    // increment the processed command counter 
                }
                else
                {
                    // Error
                    
                    // Form a error message
        /* 27/08/18 if(usbtmc_RaiseError( udi, GPIB_ERROR_ID_EXE, ERROR_USBTMC_INVALID_HEADER, "'", 1)) */
                    if(usbtmc_RaiseError( udi, errClass_Command, ERROR_USBTMC_INVALID_HEADER, "'", 1))
                    {
                       if(usbtmc_RaiseError_CatDescription( udi, cmdStart, SubPointers(cmdEnd, cmdStart) ) ) 
                       {  
                           if( !usbtmc_RaiseError_CatDescription( udi, "'", 1 ) )
                           {
                              usbtmc_ErrorQueue_RestoreCheckpoint( udi );
                           }
                       }
                       else
                         usbtmc_ErrorQueue_RestoreCheckpoint( udi );
                    }
                    else
                      usbtmc_ErrorQueue_RestoreCheckpoint( udi );
                    
                    xResult = pCmdSearch;  
                }
               
                pCmdSearch = xResult;           // set the search point for the next command
                
                iter += nCmdLength;
            }
            else break;
        }
    }
    while( nCmdLength > 0 );
       
   return nCmdProcessed; 
}

//
//// -----------------------------------
//// GPIB_CommandExecute() выполнение строки комманд GPIB.
////     root - указатель на root комманду дерева комманд
////     GPIB_Commands_String - указатель на стоку комманд GPIB, разделенных точкой с запятой
//int GPIB_CommandExecute( USB_DEVICE_INFO * udi, char * str ) {
//   // ----------------------------------------------------------------------------------------------------------
//
//   sScpiCommand_t * rc;                                    // -- указатель на выполненную комманду
//   sScpiCommand_t * root = udi->usbtmcGpib.gpib_root;      // -- корневая комманда древа комманд
//   sScpiCommand_t * Search=root;                           // -- указатель на текущий "путь" поиска комманды
////-----------------------------------------------------
//   unsigned int datatablesize = 0;                   // -- размер таблицы данных
//   unsigned int strl;                                // -- размер вх строки 
//   unsigned int ret_val = 0;                         // -- возвращаемое значение количества распознанных комманд
////-----------------------------------------------------
//   BOOL datatable_found;                             // -- таблица данных найдена в строке
////-----------------------------------------------------
//   char * semicolon=NULL;                            // -- указатель на разделитель комманд, если такой есть в строке
//   char * datatable;                                 // -- указатель на таблицу данных
//   char * stre;                                      // -- конец строки
//   char * cmde;                                      // конец комманды, 
//   char * gstre;                               // -- конец строки GPIB ("")
//   char * squote;                                    // -- кавычки строки GPIB
//   char * dquote;                                    // -- кавычки строки GPIB
//   char * quote;                                     // -- кавычки строки GPIB
////-----------------------------------------------------
//
//   // -------------------------------
//   udi->usbtmcGpib.gpib_owner = root;                // -- поиск в root
//   // -------------------------------
//   udi->usbtmcGpib.cRequestCountSupport = 1;         // -- только один запрос в строке
//   // -------------------------------------------
//   strl = udi->BulkMessageStatus.OutTransferSize;    // -- длинна переданной строки комманд
//   str = GPIB_StrCopyToCache( str, strl );                     // -- добавляет нуль символ к концу строки
//   stre = str + strl - 1;                            // -- конец строки. до 0x0A
//   if(str == NULL) return -1;                        // -- ОШИБКА ! НЕТ ПАМЯТИ!
//    // -------------------------------------------
//
//    
//   // ------- ИНИЦИАЛИЗАЦИЯ ПЕРЕДАЧИ ------
//   // произведена в DEV_DEP_MSG_OUT()
//   // -------------------------------------
//   
//   strucase( str, strl);                             // -- переводим строку в ВЕРХНИЙ РЕГИСТР
//
//   while( *str || semicolon != NULL)                 // -- пока не дошли до конца строки
//   {
//      gstre = str;
// 
//    // ----------------------------------------------------------------------------------------
//    // -- пропуск строки GPIB
//     do  
//       {
//         
//        datatable = GPIB_StrChr_rw( gstre, GPIB_CHAR_DATATABLE_SIGN, stre );      // +поиск сигнатуры таблицы данных
//        semicolon = GPIB_StrChr_rw( gstre, GPIB_CHAR_CMDSEPARATOR_SIGN, stre );   // +поиск разделителя коммад
//        squote    = GPIB_StrChr_rw( gstre, GPIB_CHAR_SINGLEQUOTE_SIGN, stre );    // +поиск строки GPIB
//        dquote    = GPIB_StrChr_rw( gstre, GPIB_CHAR_DOUBLEQUOTE_SIGN, stre );    // +поиск строки GPIB
//   
//        if(quote = ChooseLowestPtr(squote,dquote))                   // -- возвращает наиближайший указатель (наименьший, если оба не NULL. Если один NULL, возвращает второй)   
//          if( quote == ChooseLowestPtr( quote, semicolon )  )                   // -- если на пути строка GPIB (которая может содержать ';')
//            gstre = GPIB_GetStringEnd_rw( quote, strl - SubPointers(quote, str) );    // -- ищем ее конец
//          else
//            gstre = NULL;
//        else
//           gstre = NULL;
//
//       } 
//    while( gstre != NULL );
//    // ----------------------------------------------------------------------------------------
//
//   // нужно разделить строку на комманды, (разделитель ;) причем, учесть, что
//   // после каждой комманды могут быть данные и ТАБЛИЦЫ данных типа #NZZZ..ZBBBBBB...B (см GPIB)
//   // или GPIB строки, заключенные в кавычки ("") или (' ').
//   // Для этого нужно пропарсить таблицы, так как они могут содержать разделитель (;)
//   // а также строки GPIB, они тоже могут содержать все что угодно
//   
//   // =================================================================================================================
//    
//    
//   // ---------- пропуск таблиц данных -----------------------------
//        
//      datatable_found = FALSE;
//           if(datatable!=NULL && (semicolon==NULL || ((unsigned int)semicolon> (unsigned int)datatable)))  // если сигнатура обнаружена и она раньше разделителя
//           { 
//              if(IsNUMBER(*(datatable+1)))     // -- если за # следует цифра N ( цифра определяет длинну числа следом за ней )
//              { 
//                 datatable_found = TRUE;       // -- таблица данных обнаружена
//                                               // -- если за # и цифройN следует N цифр, то это заголовок таблицы данных
//                 for(  int m=1  ;  datatable_found && (m<=(HexCharToNumber(*(datatable+1))))  ;  m++)  
//                  if( !IsNUMBER( *(datatable+1+m) ) ) // -- проверяем что все это цифры
//                   datatable_found = FALSE;           // -- выход из цикла (см заголовок for)
//              } 
//           } 
//           else  
//              datatable_found = FALSE; // -- таблиа данных не обнаружена
//   // -------------------------------------------------------------
//   
//                     
//           if(datatable_found==FALSE)  // -- если не обнаружена таблица или разделитель раньше таблицы
//           {
//              if(semicolon!=NULL) 
//               { 
//                  *semicolon = 0x00;   // -- делим комманду
//                  cmde = semicolon;
//               } 
//               else 
//                  cmde = stre;         // -- конец сообщения
//           }   
//           else 
//           {  
//              semicolon = NULL;
//              char * data;
//              datatablesize = 0;
//              datatable++;            // -- пропускаем # //str++;   //!#! #!#
//
//        
//        int n = HexCharToNumber(*(datatable)); // +цифра N определяет количество цифр после #NZZZZZBBBBBBBBBBB
//                                            // +N - количество Z
//                                            // +Z - цифры составляют число, которое определяет длинну таблицы ( количетсво B )
//        data = datatable + n + 1;           // +устанавливаем указатель на начало таблицы
//        datatable++;                        // +уст указатель на начало числа ZZZZ  
//        StrToInt( datatable,  n,  &n );     // +конвертиуем число записанное в ZZZZ в str в число в int в n
//        
//        // --- таблица данных начинается с datatable и длинна ее n
//
//        datatable += n+1;                   // +прыгаем к концу таблицы данных  !#!
//        cmde = datatable;
//        semicolon = GPIB_StrChr_rw( datatable, GPIB_CHAR_CMDSEPARATOR_SIGN, stre);     // +ищем разделитель снова, но уже после таблицы данных
//        if(semicolon == datatable) datatable++;          // +если он сразу после таблицы, проспускаем его, чтобы не выполнять пустую комманду
//        
//        datatable = data;
//        datatablesize = n;
//     }
//   // =================================================================================================================
//    
//   if(datatable_found==TRUE)
//    if(datatablesize>0)
//     ;  
//
//
//   while( IsWhiteChar( *str ) ) str++;                // -- пропуск WHITE
//
//   if( *str == GPIB_CHAR_MANDATORY_IEEE488_SIGN ||  (*str == GPIB_CHAR_SCPI_SUBSYS_SEPARATOR_SIGN  && (str++)) ) Search = root;  
//                                                  /* проверям, абсолютный ли путь задан
//                                                  FAQ: если путь абс., он имеет двоеточие в начале. Если есть двоеточие,
//                                                  то первое условие в if() выполнится и выполнится второе,
//                                                  которое по сути просто сдвинет указатель str так, чтобы исключить двоеточие
//                                                  поиск будет произведен в root, так как пусть абсолютный */
//   
//   udi->usbtmcGpib.gpib_search = Search;     
//   udi->usbtmcGpib.sgpib_current = str;
//
//   
//
//       if(NULL != (rc=parse( udi,str, FALSE, cmde)))
//       {   // -- обработка одной составной комманды в строке
//
//          ret_val++;                                     // -- инкрементируем счетчик выполненных комманд
//
//       } 
//       else 
//       {
//    
//         if( SubPointers( cmde, str ) > 0 ) // if( INT(cmde) - INT(str) > 0 )         
//           { char * tmpe = cmde; 
//             if( IsEndOfLine(*tmpe) ) tmpe--;
//             while( IsWhiteChar(*tmpe) ) tmpe--;
//             if(usbtmc_RaiseError( udi, GPIB_ERROR_ID_EXE ,ERROR_USBTMC_INVALID_HEADER, "'", 1))               
//               if(usbtmc_RaiseError_CatDescription( udi, str , SubPointers( tmpe+1, str ) )) // strl подсчитывается в начале ф-ии. -1 -это пропуск 0x0A; алсо: (INT(tmpe+1) - INT(str))  ===>  SubPointers( tmpe+1, str )               
//                    usbtmc_RaiseError_CatDescription( udi, "'", 1 );
//              
//           }
//    
//         rc= Search;
//       }
//     
//   
//
//   if( semicolon != NULL ) {       // -- если не NULL, значит есть еще не выполненные команды в строке
//        str = (++semicolon);       // -- переход к следующей комманде в строке, исключаяя точку с запятой
//        if( *str != GPIB_CHAR_SCPI_SUBSYS_SEPARATOR_SIGN  )         // -- если комманда не абсолютная и путь ее опущен
//            Search = rc;           // -- указываем путь поиска на последний путь, где была выполнена комманда
//     } else break;                 // -- если NULL выходим, нет больше комманд в строке
//
//   }
//   
//   return ret_val;                 // -- возвращаем количество распознанных комманд
//}


// =========================================== ОБРАБОТЧИКИ ======================================================
unsigned int GPIB_Default_Handler( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{  
  // 27/08/18
  GPIB_RaiseStandartError( udi, 
                           ERROR_GPIB_UNCOMPLETE_COMMAND, 
                           NULL, 
                           errClass_Command // == invalid header detected
                         );
  /*
  GPIB_RaiseStandartError( udi, 
                           ERROR_GPIB_UNCOMPLETE_COMMAND, 
                           NULL, 
                           GPIB_ERROR_ID_COR | request 
                         );
  */
  return 0;
}
// ====================================================================================================

// GPIB_EnumParameters:
// Processes the @pParamString with @length length and parses the command parameters.
// The following SCPI types are supported:
// - NR1, NR2, NR3
// - Mnemonic (character)
// - Strings
// - binary DataBlock
// All the parameters are packed into the @pQueue
// In case an error occurred, the function interrupts processing and return an error code.
// Note: in case of error the @pQueue is already modified.
// @pParamString - the source string
// @length - the length of @pParamString
// @pQueue - the queue to pack parameters in
// @pEnd - (NOT IMPLEMENTED!) optional pointer to store the end of parsed string after parsing. Can be NULL. 
// If @pEnd isn't NULL, it will contain the pointer to the character after the lastest parsed character in @pParamString.
// Return: error code (negative) or number of parsed parameters (positive)
int GPIB_EnumParameters( const char * pParamString, size_t length, QUEUE * pQueue, const char ** pEnd )
{ 
    if( NULL == pParamString || 0 == length || NULL == pQueue )
        return -1;
    
    if( NULL != pEnd )
    {
        *pEnd = NULL; // not implemented
    }
    
    int rval = 0; 
    
    int nsys = 0;                           // numeric system
    size_t count = 0;                       // amount of parameters enumerated 
    eQuoteMode_t qMode;                     // quoting mode (single/double)
    eParameterType_t eParamType = eParTypeNone;   // parameter type
    const char *  pParamStart = NULL;       // the parameter start pointer in @pParamString
    const char *  end = NULL;
    BOOL bSeparator = FALSE;                // flag: separator found
  
    while( length > 0 )                     // parameter search cycle
    { 
        bSeparator = FALSE;                 // clear flag: allow a separator character in this search cycle
   
        
        // ---------------------------------------------------------------------
        while( length > 0 )                 // white characters skipping cycle
        {    
            // check current character: is it a white character?
            if( IsWhiteChar( *pParamString ) )        
            {
                // yes. skip this character
                pParamString++;
                length--;
                
                // check the next character
                continue;
            }
            
            // check current character: is this a end-of-line character?
            if( IsEndOfLine( *pParamString ) )
            {
                // yes. skip this character
                pParamString++;
                length--;
                
                // if the line ends and the length is still greater than zero: 
                // it is an error condition                
                if( length > 0 )
                {                    
                    // interrupt all procedures and exit
                    rval = (-1);
                    goto L_EnumParameters_EXIT;                    
                }
                
                // Okay, @length is 0.
                // The next "continue" will interrupt the main cycle.
                
                // But let's check the @bSeparator.
                // If @bSeparator is TRUE, it means a trailing separator found.
                if( bSeparator )
                {
                    // error: trailing separator found
                    rval = (-2);
                    goto L_EnumParameters_EXIT;
                }
                
                // exit the main cycle, because @length == 0
                goto L_EnumParameters_EXIT;
            }
            
 
            // check if this character is an argument separator character (by default is ',')
            if( GPIB_CHAR_ARGSEPARATOR_SIGN == *pParamString ) 
            {
                // Yes: it is an argument separator
                
                // check if the separator is allowed now
                
                // If @bSeparator is already TRUE then it means the separator is disallowed
                // Otherwise, the separator is allowed.
                if( ! bSeparator )
                {    
                    bSeparator = TRUE;     // set the flag: the separator is found in current cycle
                    
                    pParamString++;        // skip the character
                    length--;
                    
                    // check the next character
                    continue;
                }
                else
                {    
                    // No: the separator is already found before.
                    // Extra separator found: error
                    
                    // interrupt all procedures and exit
                    rval = (-3);
                    goto L_EnumParameters_EXIT;
                }     
            }
            else
            {   
                // No: it's neither a white character or separator character
                
                // interrupt the skipping cycle.
                break;                     
            }
            
        } // :while( length > 0 )
        // ---------------------------------------------------------------------
    
        // White character are skipped.
        // An argument separator processed.
        // It is assumed that the next character is an argument.
        
        // check if the argument separator presents between arguments
        if( (count > 0) && (!bSeparator) )
        {
            // In this point @bSeparator must be TRUE in order all the arguments
            // must be separated.
            // The only one exception: @count == 0, it means neither argument or
            // or separator found yet in the string.
            
            // interrupt all procedures and exit 
            rval = (-4);
            goto L_EnumParameters_EXIT;
        }
    
        // ---------------------------------------------------------------------
        // Analyze the current character and  check for the string type
        switch( *pParamString )
        {
            // the quoted string in single quotes
            case GPIB_CHAR_SINGLEQUOTE_SIGN: qMode = eqmSingle; break; 
            
            // the quoted string in double quotes    
            case GPIB_CHAR_DOUBLEQUOTE_SIGN: qMode = eqmDouble; break;
            
            // not quoted argument        
            default:                         qMode = eqmNone;           
        }
        //
        // --------------------------------------------------------------------- 

        //
        // Let's determine the parameter type
        //
        
        size_t nParamSize;      // parameter size in [characters]
        
        // if the quoted string is found:   
        if( IsQuote( *pParamString , qMode ) )
        {
            // search for the end of quoted string
            end = GPIB_GetStringEnd( pParamString, length );
            
            // check if the string is closed
            if( NULL == end ) 
            {
                // error: unclosed quoted string
                
                // interrupt all procedures and exit 
                rval = (-5);
                goto L_EnumParameters_EXIT;
            }
            
            // calculate the parameter size in characters
            nParamSize = (size_t)SubPointers( end, pParamString );
            
            // set the parameter start pointer
            pParamStart = pParamString;
            
            // skip the parameter's body in the parent string.
            length -= nParamSize;
            pParamString += nParamSize;
            
            // And at last, set the parameter type to @eParamType. It is string.
            eParamType = eParTypeString;
            
            // modify the parameter value: "STR" -> STR
            // delete quotes:
            pParamStart++; nParamSize-=2;
              
            // stop analyzing and process the parameter
            goto L_EnumParameters_END;
        }
        
        // ---------------------------------------------------------------------
        
        // Here: parameter is not a string parameter for sure
        // Maybe it is a mnemonic, numeric or datablock parameter        
        {
            // Firstly check for DataBlock format:
            
            // @dataBlockError will store the error code of @IsDataBlock() function
            int dataBlockCode = err_IsDataBlock_Success;
            
            // Check the datablock
            if( IsDataBlock( pParamString, length, &nParamSize, &dataBlockCode ) )
            {
                // datablock detected.
                // Now @nParamSize is the size of whole datablock segment
                
                // set the parameter start pointer
                pParamStart = pParamString;
                  
                // --------------------------------------------------------
                // Note: @nParamSize is whole size of data block parameter, 
                // including the signature and service fields.
                // --------------------------------------------------------
                
                // skip the parameter's body in the parent string.
                pParamString += nParamSize;
                length -= nParamSize;
                
                // And at last, set the parameter type to @eParamType. It is datablock.
                eParamType = eParTypeDatablock;  
                
                // modify the parameter value:
                //  Set @pParamStart to the start of binary payload
                //  Set @nParamSize to the binary payload size (@dataBlockCode)
                nParamSize = dataBlockCode;
                pParamStart = pParamString - nParamSize; 
                
                // stop analyzing and process the parameter
                goto L_EnumParameters_END;
            }
            else
            {
                switch( dataBlockCode )
                {         
                    // datablock not detected
                    case err_IsDataBlock_NotFound:
                    break;
                    
                    // datablock signature detected.
                    // Maybe it is not datablock,
                    // Maybe it is corrupted parameter
                    case err_IsDataBlock_Invalid:
                    case err_IsDataBlock_Trimmed:
                    case err_IsDataBlock_Huge: 
                    
                    #if 0
                        // hmmm. is it error?
                        rval = (-6);
                        goto L_EnumParameters_EXIT;   
                    #endif
                    
                    break;
                }
            }
        }

        // ---------------------------------------------------------------------
        
        // Here: parameter is neither a string parameter or datablock parameter
        // Maybe it is a mnemonic or numeric

        // Ok, search for the end of the parameter
        // search for the end of parameter
        // @GPIB_GetParameterEnd searches til the first white character or the end-of-line.
        end = GPIB_GetParameterEnd( pParamString, length );
            
        // check if the end of the parameter is found
        if( NULL == end ) 
        {
            // error: endless parameter
            
            // interrupt all procedures and exit 
            rval = (-7);
            goto L_EnumParameters_EXIT;
        }
        
        { 
            // calculate the parameter size in characters
            nParamSize = (size_t)SubPointers( end, pParamString );
                
            // set the parameter start pointer
            pParamStart = pParamString;
            
            // skip the parameter's body in the parent string.
            length -= nParamSize;
            pParamString += nParamSize;
        }
        
        // Check for "MNEMONIC" parameter type            
        if( IsCharacter( *pParamStart ) )
        {
            // First character is an "alhpa-character":
            // So, the parameter will be considered as "MNEMONIC"
                
            // Validate the parameter format:
            if( ! GPIB_StrChkFormat( pParamStart, nParamSize, eSCM_MNEMONIC ) )
            {
                // invalid parameter format: some invalid characters found
                
                // interrupt all procedures and exit 
                rval = (-8);
                goto L_EnumParameters_EXIT;
            }
                
            // And at last, set the parameter type to @eParamType. It is MNEMONIC (character-parameter).
            eParamType = eParTypeChar;
            
            // stop analyzing and process the parameter
            goto L_EnumParameters_END;
        }
        
        // ---------------------------------------------------------------------
        
        // Here: parameter is numeric: NR1, NR2 or NR3 (SCPI types)
                
        // Check for NR1:
        
        nsys = 0;       // numeric system
        
        // Try to determine the numeric system        
        if( IsNumericSystem(pParamStart) )
        {
            // NR1, numeric system specified
            
            // Numeric system detected: get the numeric system base
            nsys = GetNumericSystemBase( pParamStart );

            switch( nsys )
            {
                // unknown system
                case 0:
                        rval = (-9);
                        goto L_EnumParameters_EXIT;
                break;
                
                // Binary system
                case 2: if( GPIB_StrChkFormat( pParamStart,nParamSize,eSCM_BIN ) )
                        {
                            // Invalid character for binary system
                            rval = (-10);
                            goto L_EnumParameters_EXIT;
                        }
                break;
                
                // Octal system
                case 8: if( GPIB_StrChkFormat( pParamStart,nParamSize,eSCM_OCT ) )
                        {
                            // Invalid character for octal system
                            rval = (-10);
                            goto L_EnumParameters_EXIT;
                        }
                break;
                
                // Hexademical system
                case 16:if( GPIB_StrChkFormat( pParamStart,nParamSize,eSCM_HEX ) )
                        {
                            // Invalid character for hexademical system
                            rval = (-10);
                            goto L_EnumParameters_EXIT;
                        }
                break;
            }
            
            // And at last, set the parameter type to @eParamType. It is NR1 (numeric, integer)
            eParamType = eParTypeNR1;
            
            // stop analyzing and process the parameter
            goto L_EnumParameters_END;
        }

        // ---------------------------------------------------------------------
        
        // Here: parameter is numeric: NR1, NR2 or NR3 (SCPI types)
        
        // Check for NR1
        if( GPIB_StrChkFormat( pParamStart,nParamSize,eSCM_DEC ) )
        {    
            eParamType = eParTypeNR1;
            
            // stop analyzing and process the parameter
            goto L_EnumParameters_END;
        }
        
        // Check for NR2
        if( GPIB_StrChkFormat( pParamStart,nParamSize,eSCM_FLOAT ) )
        {    
            eParamType = eParTypeNR2;
            
            // stop analyzing and process the parameter
            goto L_EnumParameters_END;
        }
        
        // Check for NR3
        if( GPIB_StrChkFormat( pParamStart,nParamSize,eSCM_EXP ) )
        {    
            eParamType = eParTypeNR2;
            
            // stop analyzing and process the parameter
            goto L_EnumParameters_END;
        }
        
        // ---------------------------------------------------------------------
        
        // No valid format detected
        // Here: invalid parameter format
        // Invalid character for binary system
        rval = (-11);
        goto L_EnumParameters_EXIT;
          
        // ---------------------------------------------------------------------
        
L_EnumParameters_END:

    // Valid format detected:
    // @pParamStart - the beginning of parameter
    // @nParamSize - the size of parameter
    // @eParamType - the type of parameter
    // -------------------------------------------------------------------------

    // Push the parameter into the parameters queue
        {
        BYTE pkdParamType = eParamType;        // 1-byte packed parameter type
        
        // push the type
        if( !queue_add( pQueue, &pkdParamType, sizeof(pkdParamType) ) )
        {
            // parameters queue overflow
            rval = (-12);
            goto L_EnumParameters_EXIT;
        }
        
        // push the string value
        if( !queue_cat( pQueue, pParamStart, nParamSize ) )
        {
            // parameters queue overflow
            rval = (-13);
            goto L_EnumParameters_EXIT;
        }
        } 

        // increment amount of parsed parameters
        count++;
    }
  
  rval = count;
  
L_EnumParameters_EXIT:

  return rval;
}
// ====================================================================================
eChrz_t GPIB_GetChrzTableId( const char * pParamText, size_t nTextLen )
{   
   eChrz_t ID = eCh_MAX;

   if( 0 == strncmp( pParamText, "FACT",   nTextLen ) && 4 == nTextLen )   ID = eChFactory;
   else 
   if( 0 == strncmp( pParamText, "FACTORY",nTextLen ) && 7 == nTextLen )   ID = eChFactory;
   else 
   if( 0 == strncmp( pParamText, "USER1",  nTextLen ) && 5 == nTextLen )   ID = eChUser1;
   else 
   if( 0 == strncmp( pParamText, "USER2",  nTextLen ) && 5 == nTextLen )   ID = eChUser2; 
   else 
   if( 0 == strncmp( pParamText, "USER3",  nTextLen ) && 5 == nTextLen )   ID = eChUser3;  

   return ID;
}
// ====================================================================================
ePortId_t GPIB_GetPortId( const char * pParamText, size_t nTextLen )
{   
   ePortId_t ID = ePortId_MAX;

   if( 0 == strncmp( pParamText, "A",  nTextLen ) && 1 == nTextLen )   ID = ePortId_A;
   else 
   if( 0 == strncmp( pParamText, "B",  nTextLen ) && 1 == nTextLen )   ID = ePortId_B;
   else 
   if( 0 == strncmp( pParamText, "C",  nTextLen ) && 1 == nTextLen )   ID = ePortId_C;
   else 
   if( 0 == strncmp( pParamText, "D",  nTextLen ) && 1 == nTextLen )   ID = ePortId_D;    

   return ID;
}
// ====================================================================================
ePortComb_t GPIB_GetPortComb( const char * pParamText, size_t nTextLen )
{   
   ePortComb_t ID = ePortComb_UNDEFINED;

   if( 0 == strncmp( pParamText, "A",  nTextLen ) && 1 == nTextLen )   ID = ePortComb_A;
   else 
   if( 0 == strncmp( pParamText, "B",  nTextLen ) && 1 == nTextLen )   ID = ePortComb_B;
   else 
   if( 0 == strncmp( pParamText, "C",  nTextLen ) && 1 == nTextLen )   ID = ePortComb_C;
   else 
   if( 0 == strncmp( pParamText, "D",  nTextLen ) && 1 == nTextLen )   ID = ePortComb_D;    
   else 
   if( 0 == strncmp( pParamText, "AB", nTextLen ) && 2 == nTextLen )   ID = ePortComb_AB;    
   else 
   if( 0 == strncmp( pParamText, "AC", nTextLen ) && 2 == nTextLen )   ID = ePortComb_AC;    
   else 
   if( 0 == strncmp( pParamText, "AD", nTextLen ) && 2 == nTextLen )   ID = ePortComb_AD;
   else 
   if( 0 == strncmp( pParamText, "BC", nTextLen ) && 2 == nTextLen )   ID = ePortComb_BC;
   else 
   if( 0 == strncmp( pParamText, "BD", nTextLen ) && 2 == nTextLen )   ID = ePortComb_BD;
   else 
   if( 0 == strncmp( pParamText, "CD", nTextLen ) && 2 == nTextLen )   ID = ePortComb_CD;
   else 
   if( 0 == strncmp( pParamText, "CHEC",nTextLen) && 4 == nTextLen )   ID = ePortComb_CHECK;
   else 
   if( 0 == strncmp( pParamText, "CHECK",nTextLen)&& 5 == nTextLen )   ID = ePortComb_CHECK;

   return ID;
}
// ====================================================================================
ePortStateId_t GPIB_GetPortStateId( const char * pParamText, size_t nTextLen )
{   
   ePortStateId_t ID = ePortStateId_UNDEFINED;

   if( 4 == nTextLen && 0 == strncmp( pParamText, "SHOR",  nTextLen ) ) ID = ePortStateId_Short;
   else                 
   if( 5 == nTextLen && 0 == strncmp( pParamText, "SHORT", nTextLen ) ) ID = ePortStateId_Short;
   else                 
   if( 4 == nTextLen && 0 == strncmp( pParamText, "OPEN",  nTextLen ) ) ID = ePortStateId_Open;
   else                 
   if( 4 == nTextLen && 0 == strncmp( pParamText, "LOAD",  nTextLen ) ) ID = ePortStateId_Load;
   else                 
   if( 5 == nTextLen && 0 == strncmp( pParamText, "OPEN2", nTextLen ) ) ID = ePortStateId_Open2;
   else                 
   if( 5 == nTextLen && 0 == strncmp( pParamText, "LOAD2", nTextLen ) ) ID = ePortStateId_Load2;    
   else 
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S11", nTextLen ) )   ID = ePortStateId_S11;    
   else                 
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S12", nTextLen ) )   ID = ePortStateId_S12;    
   else                 
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S13", nTextLen ) )   ID = ePortStateId_S13;
   else                 
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S14", nTextLen ) )   ID = ePortStateId_S14;
   else                 
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S21", nTextLen ) )   ID = ePortStateId_S21;   
   else                                                                      
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S22", nTextLen ) )   ID = ePortStateId_S22;   
   else                                                                      
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S23", nTextLen ) )   ID = ePortStateId_S23;
   else                                                                      
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S24", nTextLen ) )   ID = ePortStateId_S24;
   else                 
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S31", nTextLen ) )   ID = ePortStateId_S31; 
   else                                                                      
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S32", nTextLen ) )   ID = ePortStateId_S32; 
   else                                                                      
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S33", nTextLen ) )   ID = ePortStateId_S33;
   else                                                                      
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S34", nTextLen ) )   ID = ePortStateId_S34;
   else                 
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S41", nTextLen ) )   ID = ePortStateId_S41;   
   else                                                                      
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S42", nTextLen ) )   ID = ePortStateId_S42;   
   else                                                                      
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S43", nTextLen ) )   ID = ePortStateId_S43;
   else                                                                      
   if( 3 == nTextLen && 0 == strncmp( pParamText, "S44", nTextLen ) )   ID = ePortStateId_S44;
     
   return ID;
}
// ====================================================================================
/*
EPORT GPIB_GetPortID( char * sQueueParameter, unsigned int dwQueueParameterSize )
{ 
 EPORT port = ePORT_NONE;
   if(0==strncmp(sQueueParameter+1,"A",dwQueueParameterSize-1) && dwQueueParameterSize>1) port = ePORT_A; 
   else 
   if(0==strncmp(sQueueParameter+1,"B",dwQueueParameterSize-1) && dwQueueParameterSize>1) port = ePORT_B; 
   else 
   if(0==strncmp(sQueueParameter+1,"AB",dwQueueParameterSize-1) && dwQueueParameterSize>2)port = ePORT_AB;   
   return port;
}
*/
// ====================================================================================
/*
ESTATE GPIB_GetKeystateID( char * sQueueParameter, unsigned int dwQueueParameterSize, BOOL ExpandFormat )
{ 
 ESTATE state = eSTATE_NONE;  
               if(0==strncmp(sQueueParameter+1,"OPEN",dwQueueParameterSize-1) && dwQueueParameterSize>4) state = eSTATE_OPEN; 
               else 
               if(0==strncmp(sQueueParameter+1,"LOAD",dwQueueParameterSize-1) && dwQueueParameterSize>4) state = eSTATE_LOAD; 
               else 
               if(0==strncmp(sQueueParameter+1,"SHORT",dwQueueParameterSize-1) && dwQueueParameterSize>4) state = eSTATE_SHORT; //SHOR==SHORt
               else 
               if(0==strncmp(sQueueParameter+1,"OPEN2",dwQueueParameterSize-1) && dwQueueParameterSize>5) state = eSTATE_OPEN2; 
               else 
               if(0==strncmp(sQueueParameter+1,"LOAD2",dwQueueParameterSize-1) && dwQueueParameterSize>5) state = eSTATE_LOAD2; 
               else 
               if(ExpandFormat == FALSE && 0==strncmp(sQueueParameter+1,"THRU",dwQueueParameterSize-1) && dwQueueParameterSize>4) state = eSTATE_THRU;
               else   
               if(ExpandFormat == FALSE && 0==strncmp(sQueueParameter+1,"ATT",dwQueueParameterSize-1) && dwQueueParameterSize>3) state = eSTATE_ATTEN;
               else 
               if(ExpandFormat) 
                 if(0==strncmp(sQueueParameter+1,"T11",dwQueueParameterSize-1) && dwQueueParameterSize>3) state = eSTATE_T11;
                 else 
                 if(0==strncmp(sQueueParameter+1,"T12",dwQueueParameterSize-1) && dwQueueParameterSize>3) state = eSTATE_T12;
                 else 
                 if(0==strncmp(sQueueParameter+1,"T21",dwQueueParameterSize-1) && dwQueueParameterSize>3) state = eSTATE_T21;
                 else 
                 if(0==strncmp(sQueueParameter+1,"T22",dwQueueParameterSize-1) && dwQueueParameterSize>3) state = eSTATE_T22;
                 else  
                 if(0==strncmp(sQueueParameter+1,"A11",dwQueueParameterSize-1) && dwQueueParameterSize>3) state = eSTATE_A11;
                 else 
                 if(0==strncmp(sQueueParameter+1,"A12",dwQueueParameterSize-1) && dwQueueParameterSize>3) state = eSTATE_A12;
                 else 
                 if(0==strncmp(sQueueParameter+1,"A21",dwQueueParameterSize-1) && dwQueueParameterSize>3) state = eSTATE_A21;
                 else 
                 if(0==strncmp(sQueueParameter+1,"A22",dwQueueParameterSize-1) && dwQueueParameterSize>3) state = eSTATE_A22;
                 else
                 state = eSTATE_NONE; 
   return state;
}
*/
// ====================================================================================

unsigned int GPIB_RaiseStandartError( USB_DEVICE_INFO * udi, 
                                      unsigned int errCode,        // error code
                                      const char * pDescription,   // error description
                                      GPIB_ErrorClass_t errClass   // error class
                                    )
{
  const char * pStrCurrentCmd = udi->usbtmcGpib.sgpib_current;
  
  // search for the command header's end
  while( !(IsWhiteChar( *pStrCurrentCmd ) || IsEndOfLine( *pStrCurrentCmd )) ) 
  {
      pStrCurrentCmd++;
  }
  
  size_t nCmdLength = SubPointers( pStrCurrentCmd, udi->usbtmcGpib.sgpib_current);
  pStrCurrentCmd = udi->usbtmcGpib.sgpib_current;
  
  switch( errCode )
  {
      case ERROR_GPIB_BUFFER_OVERFLOW:   
      {  
          if( errClass_Autodetect == errClass )
              errClass = errClass_Execution;
            
          //ERROR_USBTMC_BUFFER_OVERFLOW_IN
          if( !usbtmc_RaiseError( udi, errClass, ERROR_USBTMC_INTERNAL, " in '", 5 ) )
          {  
             errCode = 0; break;
          }  
          
          if( !usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength ) )
          { 
             errCode = 0; break;
          }
          
          if( !usbtmc_RaiseError_CatDescription( udi, "': ", 3 ) )
          { 
             errCode = 0; break;
          }

          if( !usbtmc_RaiseError_CatDescription( udi, pDescription, strlen(pDescription) ) )
          {  
             errCode = 0; break;
          }  
      }    
      break;

      case ERROR_GPIB_DATA_NOT_FOUND:   
      {  
          if( errClass_Autodetect == errClass )
              errClass = errClass_Execution;
          
          if( !usbtmc_RaiseError( udi, errClass, ERROR_USBTMC_DATANOTFOUND, " in '", 5 ) )
          {  
             errCode = 0; break;
          }  
          
          if( !usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength ) )
          { 
             errCode = 0; break;
          }
          
          if( !usbtmc_RaiseError_CatDescription( udi, "': ", 3 ) )
          { 
             errCode = 0; break;
          }

          if( !usbtmc_RaiseError_CatDescription( udi, pDescription, strlen(pDescription) ) )
          {  
             errCode = 0; break;
          }  
          /*
          if(usbtmc_RaiseError( udi, ErrorIdetnificator, ERROR_USBTMC_DATANOTFOUND, "", 0 ) )
          {  
            if( !usbtmc_RaiseError_CatDescription( udi, pDescription, strlen(pDescription) ) )
            {  
               errCode = 0;
            }
          }  
          else 
            errCode = 0;*/    
      }   
      break;

      case ERROR_GPIB_THERMDATA_NOT_FOUND:
      {  
          if( errClass_Autodetect == errClass )
              errClass = errClass_Execution;
          
          if( !usbtmc_RaiseError( udi, errClass, ERROR_USBTMC_NOTHERMCOMPDATA, " in '", 5 ) )
          {  
             errCode = 0; break;
          }  
          
          if( !usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength ) )
          { 
             errCode = 0; break;
          }
          
          if( !usbtmc_RaiseError_CatDescription( udi, "': ", 3 ) )
          { 
             errCode = 0; break;
          }

          if( !usbtmc_RaiseError_CatDescription( udi, pDescription, strlen(pDescription) ) )
          {  
             errCode = 0; break;
          } 
          /*if( !usbtmc_RaiseError( udi, ErrorIdetnificator, ERROR_USBTMC_NOTHERMCOMPDATA, pDescription, strlen(pDescription) ) ) 
          {
              errCode = 0;
          }*/
      }
      break;

      case ERROR_GPIB_UNCOMPLETE_COMMAND:   
      {  
          if( errClass_Autodetect == errClass )
              errClass = errClass_Command;
          
          if(usbtmc_RaiseError( udi, errClass, ERROR_USBTMC_INVALID_HEADER, "'", 1 ))
          {  
             if( usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength ) )
             {  
                if( !usbtmc_RaiseError_CatDescription( udi, "'", 1 ) )
                {
                   errCode = 0;
                }  
             } 
             else 
               errCode = 0;
          }   
          else 
            errCode = 0;
      }          
      break;
      
      /*
      case ERROR_GPIB_PARAMETER_NOT_SUPPORTED:   
      {  
          if( errClass_Autodetect == errClass )
              errClass = errClass_Execution;
          
          if( !usbtmc_RaiseError( udi, errClass, ERROR_USBTMC_PARAMETER, " in '", 5 ) )
          {  
             errCode = 0; break;
          }  
          
          if( !usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength ) )
          { 
             errCode = 0; break;
          }
          
          if( !usbtmc_RaiseError_CatDescription( udi, "'", 1 ) )
          { 
             errCode = 0; break;
          }

          // if( !usbtmc_RaiseError_CatDescription( udi, pDescription, strlen(pDescription) ) )
          // {  
          //    errCode = 0; break;
          // } 
          
          // if(usbtmc_RaiseError( udi,ErrorIdetnificator, ERROR_USBTMC_PARAMETER, "'", 1 ))
          // {
          //      if( !usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength ) )
          //      {  
          //          errCode = 0;
          //      }
          // }     
          // else 
          //   errCode = 0;
      }          
      break;
      */

      case ERROR_GPIB_TRIGGER_IGNORED: 
      {
          if( errClass_Autodetect == errClass )
              errClass = errClass_Command;
          
          if(usbtmc_RaiseError( udi, errClass, ERROR_USBTMC_TRIGGERIGNORED, "'", 1 ))
          {  
              if(usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength))
              {  
                if( !usbtmc_RaiseError_CatDescription( udi, "'", 1) )
                {  
                   errCode = 0;
                }
              }
              else 
                errCode = 0;
          }   
          else 
            errCode = 0;
      }         
      break;

      case ERROR_GPIB_REQUEST_ONLY_SUPPORT:   
      {  
          if( errClass_Autodetect == errClass )
              errClass = errClass_Command;
          
          if(usbtmc_RaiseError( udi, errClass, ERROR_USBTMC_REQUESTONLY, "'", 1 ))
          {  
             if(usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength))
             {  
                if( !usbtmc_RaiseError_CatDescription( udi, "': request only supported", 25 ) )
                {  
                   errCode = 0;
                }
             } 
             else 
               errCode = 0;
          }   
          else 
            errCode = 0;
      }          
      break;

      case ERROR_GPIB_ARRAY_CORRUPTED:  
      {  
          if( errClass_Autodetect == errClass )
              errClass = errClass_Execution;
          
          if( !usbtmc_RaiseError( udi, errClass, ERROR_USBTMC_ARRAY_CORRUPTED, "in '", 4 ) )
          {  
             errCode = 0; break;
          }  
          
          if( !usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength ) )
          { 
             errCode = 0; break;
          }
          
          if( !usbtmc_RaiseError_CatDescription( udi, "': ", 3 ) )
          { 
             errCode = 0; break;
          }

          if( !usbtmc_RaiseError_CatDescription( udi, pDescription, strlen(pDescription) ) )
          {  
             errCode = 0; break;
          } 
         /*
          if(usbtmc_RaiseError( udi,ErrorIdetnificator, ERROR_USBTMC_ARRAY_CORRUPTED, "'", 1 ))
          { 
             if(usbtmc_RaiseError_CatDescription( udi, pDescription, strlen(pDescription) ))
           //if(usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength))  
             {  
                if( !usbtmc_RaiseError_CatDescription( udi, "': invalid checksumm", 20 ) )
                {  
                   errCode = 0;
                } 
             } 
             else
               errCode = 0;
          }   
          else
            errCode = 0; */ 
      }      
      break;
      /*
      case ERROR_GPIB_HEADER_CORRUPTED:  
      {  
          if(usbtmc_RaiseError( udi,ErrorIdetnificator, ERROR_USBTMC_HEADER_CORRUPTED, "'", 1 ))
          {  
             if(usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength))
             {  
                if( !usbtmc_RaiseError_CatDescription( udi, "': header corrupted", 18 ) )
                {  
                   errCode = 0;
                } 
             } 
             else 
               errCode = 0;
          }   
          else 
            errCode = 0;            
      }     
      break; */
      case ERROR_GPIB_TOO_MANY_REQUESTS:  
      {
           if( errClass_Autodetect == errClass )
              errClass = errClass_Device;
           
           if(usbtmc_RaiseError( udi, errClass, ERROR_USBTMC_TOOMANY_REQUESTS, "'", 1 ))
           {  
              if(usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength))
              {  
                  if( !usbtmc_RaiseError_CatDescription( udi, "')", 2) )
                  {
                     errCode = 0;
                  }
              }
              else 
                errCode = 0;
           }  
           else 
             errCode = 0;
      }          
      break;
      case ERROR_GPIB_COMMAND_ONLY_SUPPORT:  
      {  
           if( errClass_Autodetect == errClass )
              errClass = errClass_Command;
           
           if(usbtmc_RaiseError( udi, errClass, ERROR_USBTMC_COMMANDONLY, "'", 1 ))
           {  
              if(usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength))
              {  
                  if( !usbtmc_RaiseError_CatDescription( udi, "': request is unexpected", 24) )
                  {  
                     errCode = 0;
                  }
              }  
              else 
                errCode = 0;
           }    
           else 
             errCode = 0;
                  
      }           
      break;
      case ERROR_GPIB_INTERNAL:
      {  
          if( errClass_Autodetect == errClass )
              errClass = errClass_Device;
          
          if( !usbtmc_RaiseError( udi, errClass, ERROR_USBTMC_INTERNAL, " at command '", 13 ) )
          {  
             errCode = 0; break;
          }  
          
          if( !usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength ) )
          { 
             errCode = 0; break;
          }
          
          if( !usbtmc_RaiseError_CatDescription( udi, "'", 1 ) )
          { 
             errCode = 0; break;
          }
          
          //usbtmc_RaiseError( udi,ErrorIdetnificator, ERROR_USBTMC_INTERNAL, ".", 1 );
      }  
      break;

      case ERROR_GPIB_INVALID_ENUM_PARAMETERS:
      {  
          if( errClass_Autodetect == errClass )
              errClass = errClass_Command;
          
          if( !usbtmc_RaiseError( udi, errClass, ERROR_USBTMC_PARAMETER, "in '", 4 ) )
          {  
             errCode = 0; break;
          }  
          
          if( !usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength ) )
          { 
             errCode = 0; break;
          }
          
          if( !usbtmc_RaiseError_CatDescription( udi, "': parsing failed", 17 ) )
          { 
             errCode = 0; break;
          }

          /*
          if( !usbtmc_RaiseError_CatDescription( udi, pDescription, strlen(pDescription) ) )
          {  
             errCode = 0; break;
          } 
          */
          /*
          if(usbtmc_RaiseError( udi,ErrorIdetnificator, ERROR_USBTMC_EXECUTION, " at '", 5 ))
          {  
             if(usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength))
             {  
                if( !usbtmc_RaiseError_CatDescription( udi, "'", 1) )
                {
                  errCode = 0;
                }  
             }   
             else
               errCode = 0;
          }     
          else
            errCode = 0;*/
      }        
      break;

      default: 
      {  
          if( errClass_Autodetect == errClass )
              errClass = errClass_Command;
          
          if( !usbtmc_RaiseError( udi, errClass, ERROR_USBTMC_PARAMETER, "in '", 4 ) )
          {  
             errCode = 0; break;
          }  
          
          if( !usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength ) )
          { 
             errCode = 0; break;
          }
          
          /*
          if( !usbtmc_RaiseError_CatDescription( udi, "'", 1 ) )
          { 
             errCode = 0; break;
          }
          */
          /*
          if( !usbtmc_RaiseError_CatDescription( udi, pDescription, strlen(pDescription) ) )
          {  
             errCode = 0; break;
          }
          */ 
          
          /*
          if(usbtmc_RaiseError( udi,ErrorIdetnificator, ERROR_USBTMC_PARAMETER, "in '", 4 ))
          {  
              if( !usbtmc_RaiseError_CatDescription( udi, pStrCurrentCmd, nCmdLength ) )
              {  
                  errCode = 0; // не удалось записать ошибку, значит и продолжать (идти во второй switch) не стоит
              }
          }      
          else 
            errCode = 0;*/
      }         

  }

  // ------------ 220 error ---------------------------------------------------------
  switch( errCode )
  { 
      case ERROR_GPIB_INVALID_PARAMETER_TYPE: // так как ошибка типа возникнет лишь тогда, когда get сможет достать параметр и его тип из очереди. А он него не достатнет, потому что зачастую ему не хватит места в буфере 
      case ERROR_GPIB_INVALID_PARAMETER: 
      {  
          if( !usbtmc_RaiseError_CatDescription( udi, "': invalid parameter", 20 ) )
          {
              errCode = 0;
          }
      }  
      break;
      
      case ERROR_GPIB_WRONG_PARAMETERS_COUNT: 
      {    
          if( !usbtmc_RaiseError_CatDescription( udi, "': wrong parameters count", 25 ) )
          {
              errCode = 0;
          }  
      }    
      break;
      
      /*
      case ERROR_GPIB_PARAMETER_NOT_SUPPORTED: 
      {      
          usbtmc_RaiseError_CatDescription( udi, "': parameter not supported", 26 );  
      }              
      break;
      */
   
     
      default:;
  }
  
  //----------------------
  // error occurred while pushing the raised error into the queue
   if( errCode == 0 )
   {
       usbtmc_ErrorQueue_RestoreCheckpoint( udi );
   }   
  //---------------------- 
  return 0;
}

/*
unsigned int CheckCompatibilePorts( ESTATE state, EPORT port )
{
// проверка правильности указания портов и состояний вообще
  if( !(     ( state == eSTATE_SHORT ||  
               state == eSTATE_LOAD  ||  
               state == eSTATE_OPEN  ||   
               state == eSTATE_LOAD2 ||  
               state == eSTATE_OPEN2 ||
               state == eSTATE_T11   ||
               state == eSTATE_T12   ||
               state == eSTATE_T21   ||
               state == eSTATE_T22   ||
               state == eSTATE_A11   ||
               state == eSTATE_A12   ||
               state == eSTATE_A21   ||
               state == eSTATE_A22   
             ) 
             &&
             ( port  == ePORT_A      ||
               port  == ePORT_B      ||
               port  == ePORT_AB     
             )
       ) 
    ) return FALSE;
  //---------------------------------------------------------------------------------------------------------------------------------------
  // проверка правильности указания порта (A/B) для сосотояния SH/LD/OP/OP2/LD2
    if(  (   ( state == eSTATE_SHORT ||  
               state == eSTATE_LOAD  ||  
               state == eSTATE_OPEN  ||
               state == eSTATE_LOAD2 ||  
               state == eSTATE_OPEN2 
             ) 
             &&
             ( port  != ePORT_A      &&
               port  != ePORT_B      
             )
         ) 
    ) return FALSE;  
  //---------------------------------------------------------------------------------------------------------------------------------------
  // проверка правильности указания порта (AB) для сосотояния T11/T12/T21/T22/A11/A12/A21/A22
    if(  (   ( state == eSTATE_T11   ||
               state == eSTATE_T12   ||
               state == eSTATE_T21   ||
               state == eSTATE_T22   ||
               state == eSTATE_A11   ||
               state == eSTATE_A12   ||
               state == eSTATE_A21   ||
               state == eSTATE_A22 
             ) 
             &&
             ( port  != ePORT_AB
             )
       ) 
    ) return FALSE;   

return TRUE;
}  
 
unsigned int CheckCompatibilePorts2( ESTATE state, EPORT port )  // using in Path:STATE
{
// проверка правильности указания портов и состояний вообще
  if( !(     ( state == eSTATE_SHORT ||  
               state == eSTATE_LOAD  ||  
               state == eSTATE_OPEN  ||   
               state == eSTATE_LOAD2 ||  
               state == eSTATE_OPEN2 ||
               state == eSTATE_THRU  ||
               state == eSTATE_ATTEN

             ) 
             &&
             ( port  == ePORT_A      ||
               port  == ePORT_B      ||
               port  == ePORT_AB     
             )
       ) 
    ) return FALSE;
  //---------------------------------------------------------------------------------------------------------------------------------------
  // проверка правильности указания порта (A/B) для сосотояния SH/LD/OP/OP2/LD2
    if(  (   ( state == eSTATE_SHORT ||  
               state == eSTATE_LOAD  ||  
               state == eSTATE_OPEN  ||
               state == eSTATE_LOAD2 ||  
               state == eSTATE_OPEN2 
             ) 
             &&
             ( port  != ePORT_A      &&
               port  != ePORT_B      
             )
         ) 
    ) return FALSE;  
  //---------------------------------------------------------------------------------------------------------------------------------------
  // проверка правильности указания порта (AB) для сосотояния T11/T12/T21/T22/A11/A12/A21/A22
    if(  (   state == eSTATE_THRU  ||
             state == eSTATE_ATTEN
         ) 
             &&
             ( port  != ePORT_AB
             )
       ) 
     return FALSE;   

return TRUE;
}
*/
unsigned int GPIBInit( USB_DEVICE_INFO * udi )
{
  _ESE = 0x00;
  _SRE = 0x00;

  // Standard Event Status Register (ESR): bit Power On
  // 27/08/18
  // Note: in this implementation this bit is disabled
  // GPIB_SET_PWN();
  
  // GPIB_SET_OPC();

  return 0;
}
#endif

