
#include "lpc176x.h"
#include "usb_hardware.h"
#include <stdlib.h>
#include <stdio.h>
#include "options.h"

#ifdef USBTMC   // #endif в конце файла
#include "utils.h"
#include "gpib_parser.h"
#include "usb_enumeration.h"
#include "usbtmc.h"
#include "utilits.h"
#include "usb_options.h"
//#include "spi.h"
#include "i2_c.h"
#include "common\buffer.h"
#include <math.h>
#include "usb_application.h"
#include "usb_proto.h"

#include "acm_base.h"
#include "acm_base_model.h"



#define DBPOINTS_BUFFER_NUM  128

// @dbPointsBuffer - the temporary buffer for the points in the intermediate, double format
static sACMChrzPoint_t dbPointsBuffer[ DBPOINTS_BUFFER_NUM ];

// ==============================================================================================
// GPIB функции идентичны по строению. Вначале они получают указатель на выходной буфер данных,
// куда собираются печатать ответ (pBulkData_DeviceOut) и размер этого буфера (MAX).
// Далее, из очереди pQParameters в зависимости от функции извлекаются параметры в том виде, в каком
// они были переданы от компьютера, за исключением того, что первым байтом каждого параметра является
// его тип (eParTypeChar, eParTypeNR1, ...) - тип параметра может быть строковый, числовой, символьный и т.п.
// К тому же, в зависимости от функции, проверяется количество переданных параметров, если оно не сов-
// падает, функция генерирует ошибку и завершает работу. Параметры извлекаются по очереди, если не удалось
// извлечь хотя бы один, функция генерирует ошибку и завершает работу. Далее, параметры обрабатываются 
// в зависимости от их типа и предполагаемого назначения (задается функцией), получаются конкретные
// значения, например тип порта (ePORT_A, ePORT_B из "A" и "B"). Если значение параметра не корректно,
// тоесть невозможно преобразовать порт C например, параметр объявляется ошибочным и функция ведет себя
// аналогично, как и в случае с ошибкой получения параметра - то есть завершает работу, генерируя ошибку
// Также она себя ведет, если значение параметра понято, обработано, но не применимо в данной ситуации,
// например, невозможно установить состояние THRU для одного порта A. 
//
// Если все эти условия не выполнились, функция приступает к генерации ответа, если она запросная
// Если нет, выполняет какие-то дейтсвия. Если функция запросная, а вызвана как комманда, функция генерирует ошибку.
// И наоборот, коммандные функции не могут вызываться как запрос. (Конечно, если функция Командно-запросная, ничего страшного)
//
// Функция GPIB ВСЕГДА возвращает количество записанных в буфер символов.
//
// Если функции недостаточно места в буфере, как например в случае с MEM:TABLE:DATA, она поступает так:
// она сохраняет контекст выполнения (все необходимые ей данные, для того чтобы можно было начать выполнениеъ
// с места остановки), регистриует себя в качестве обработчика события опустошения буфера. Затем функция
// сбросит флаг EOM и завершит работу. Обработчик канала USB будет знать, что это сообщение не последнее, EOM сброшен.
// Как только место в буфере освободится, обработчик канала USB (см в endpoints.c) вызовет функцию еще раз.
// На этот раз, она будет знать, что вызвана по событю опустошения буфера и загрузит контекст выполнения, 
// подготовит часть данных, которая уместится в освободившееся место, и вновь, при необходимости, повторит
// такую операцию с сохранением-загрузкой контекста. Как только функция обнаружит, что все данные переданы,
// она не станет сбрасывать EOM, не станет сохранять контекст а просто завершится.
//
// Если при работе функции возникает ошибка, она передается в функцию RaiseStandartError, та в свою
// очередь, зная код ошибки, запишет необходимые данные в очередь ошибок, включая необходимое описание 
// ошибки (если необходимо). При вызове SYST:ERR? эта ошибка приобретает сформатированный вид и выдается наружу.
//
// Некоторые команды, например MEM:TABLE:FREQ:STAR и MEM:TABLE:FREQ:STOP абсолютно идентичны по топологии
// но различаются лишь в самом последнем звене - смещению данных в стуртуре заголовка, которые нужно
// выдать в ответе. Чтобы не делать две одинаковые функции, отличающиеся только одним словом, сделаны т.н.
// специальные функции. При инициализации GPIB дерева обычные команды регистрируют на себя по одной функции.
// Специальные команды делят между собой одну единственную обслуживающую функцию. Эта функция идентифицирует
// каждую команду и запрос по специальному полю FunctionID, которое помещается в очередь параметров функции pQParameters
// перед вызовом и перед занесением в очередб параметров функции. Это поле задается для каждой команды
// уникальным 32 разрядным числом при иницализации дерева команд GPIB. Функция обязательно должна знать, что
// это поле нужно прочесть и использоватть. Для этого, перед получением параметров функции, она должна извлечь
// FunctionID из очереди как обычный параметр. В зависимости от значения параметра FunctionID она должна
// предпринимать различные действия: в одном случае выдавать Fmin, а в другом случае Fmax например.
//    Например, для :MEM:TABLE:FREQ:STAR;STOP;:MEM:TABLE:CONN;ADAP;ANAL;TIME;DATA ... существует всего одна
// фунция MemTableFields(), зато она разбирает 11 различных FunctionID.
// ==============================================================================================

// ==============================================================================================
// GPIB_Identification(...)
// Назначенная комманда - *IDN?
// Параметры комманды - отсутствуют
// Возвращаемое значение - строка IDN
// ---------------------------------------------------------------------------------------------------------
// IDN string format:  "<manufactor>,<device_name>,<device_serial>,0"
// <manufactor>       -  Planar (http://planar.chel.ru) or "Copper Mountain Technologies"
// <device_name>      -  Auto Calibration Module
// <device_serial>    -  0000-0000-0000  
unsigned int GPIB_Identification (  USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request ) // Обработчк *IDN?
{
  BYTE * pBulkData_DeviceOut = (BYTE*)udi->usbtmcGpib.pData;
  int MAX = udi->BulkRespondStatus.RespondBufferSize;
  size_t count;
 unsigned int err = 0;
  int i = 0+strlen(ACMClass->properties.manufacturerId);
      i+= 1+strlen(ACMClass->properties.modelName);
      i+= 1+strlen(ACMClass->properties.serialNumber);
      i+= 1+strlen(ACMClass->properties.firmwareId);

  if(!queue_getcount( pQParameters, &count ))
     err = ERROR_GPIB_INTERNAL;
  else if( count != 0)
         err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
       else if( !request ) 
               err = ERROR_GPIB_REQUEST_ONLY_SUPPORT;
            else 
            {
               if( MAX >= i )
               {                    
                  return snprintf( pBulkData_DeviceOut, MAX, "%s,%s,%s,%s", 
                                      ACMClass->properties.manufacturerId, 
                                      ACMClass->properties.modelName, 
                                      ACMClass->properties.serialNumber, 
                                      ACMClass->properties.firmwareId );                   
               }   
               else 
                  err = ERROR_GPIB_BUFFER_OVERFLOW;
            }
  // ------------------------------------------------------- 
  if( 0 != err ) 
  {  
      // 27/08/18
      // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
      GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
  }  
  return 0;
}


// ====================================================================================================
// GPIB_SystemVersion(...) - сервисная комманда, недокументирована
// Назначенная комманда - SYSTem:VERSion?
// Параметры комманды - любые
// Возвращаемое значение - сообщение о номере версии парсера SCPI
unsigned int GPIB_SystemVersion( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request ) // Обработчк system:info, system:info?
{ 
  BYTE * pBulkData_DeviceOut = (BYTE*)udi->usbtmcGpib.pData;

  int MAX = udi->BulkRespondStatus.RespondBufferSize;
  //--------------------------------------------------------
  if( MAX >= 16)
  {
     s_memcpy( pBulkData_DeviceOut, "SCPI parser v1.3", 16 ); // ----- Copy respond data
     return (request==TRUE)?16:0;   // -- respond length
  } else return 0;
}
// ==============================================================================================
// GPIB_Clear(...)
// Назначенная комманда - *CLS, очищает очередь ошибок и регисты STB и SRE
// Параметры комманды - отсутствуют
// Возвращаемое значение - отсутствует
unsigned int GPIB_Clear( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request ) // Обработчк *CLS
{ 
  QUEUE * pQueue = &udi->usbtmcGpib.StateMachine.qErrorQueue;
  
  size_t count = 0; 
  unsigned int err = 0;
  //--------------------------------------------------------
  if( !queue_getcount( pQParameters, &count ) )
  {  
     err = ERROR_GPIB_INTERNAL;
  }
  else 
  {  
       if( count != 0)
       {  
           err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
       }
       else
       {  
           if( request )
           {  
               err = ERROR_GPIB_COMMAND_ONLY_SUPPORT;
           } 
           else 
           {
              // Clear Event Status Register
              // Clear Status Register
              // Clear Error Queue              
              udi->usbtmcGpib.StateMachine.STB  = 0x00;
              udi->usbtmcGpib.StateMachine.ESR  = 0x00;
                            
              // - Error/Event Queue
              queue_clear( pQueue ); // Clear error queue

              GPIB_CLR_MAV();   // Clear Status Bit: message available
              GPIB_CLR_EAV();   // Clear Status Bit: event available
           }
       }     
  }
  
  if( 0 != err ) 
  { 
      // 27/08/18
      // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
      GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
  }  
  return 0;   // -- respond length
}
// ==============================================================================================
// GPIB_Reset(...)
// Назначенная комманда - *RST, сбрасывает состояние портов на LOAD/LOAD
// Параметры комманды - отсутствуют
// Возвращаемое значение - отсутствует
unsigned int GPIB_Reset( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request ) // Обработчк *RST
{ 
  QUEUE * pQueue = &udi->usbtmcGpib.StateMachine.qErrorQueue;
  size_t count = 0; unsigned int err = 0;
  //--------------------------------------------------------  
  if( !queue_getcount( pQParameters, &count ) )
  {  
     err = ERROR_GPIB_INTERNAL;
  }
  else
  {
     if( count != 0)
     {  
        err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
     }  
     else 
     {  
        if( request ) 
        { 
            err = ERROR_GPIB_COMMAND_ONLY_SUPPORT;
        }    
        else 
        {            
            udi->EPBulkStatus.InPipe.dwLength = 0;                    // Clear bytes counters
            udi->EPBulkStatus.OutPipe.dwLength = 0;                   //     - send counter
            udi->EPBulkStatus.shortpacket=FALSE;                      //     - recv counter
            udi->EPBulkStatus.shortpacketsending=FALSE;               // And clear also shortpacket signal flags
            //---------------
            udi->BulkRespondStatus.INTransferInProgress=FALSE;        // Clear signal variables
            udi->BulkRespondStatus.nBytesSent=0;                      // 
            udi->BulkRespondStatus.INTransferTerminating=FALSE;       //
            //---------------
            //udi->usbtmcStatus.USBTMC_status = STATUS_SUCCESS;       // usbtmcStatus do'nt depends from any bulk commands 
            //udi->usbtmcStatus.USBTMC_InitiateRecieved = FALSE;      // 
            //---------------
            queue_clear( pQueue ); // Clear error queue
            GPIB_CLR_EAV();
            GPIB_CLR_MAV();                                           // No messages available               
            //---------------
            udi->BulkMessageStatus.bBulkHeaderRecieved=FALSE;         // Clears Header flag
            udi->BulkMessageStatus.nBytesRecieved=0;                  // Clears bytes counter
            udi->BulkMessageStatus.OUTTransferInProgress=FALSE;       // Clears signal variable
            //---------------
            usb_lpc_cmd(CMD_EP_SELECT | USB_EP_PHY_ADDRESS_BULK_OUT );  // Clear EPOUT buffers
            usb_lpc_cmd(CMD_EP_CLEAR_BUFFER); 
            usb_lpc_cmd(CMD_EP_CLEAR_BUFFER); 
            //---------------
            usb_lpc_cmd(CMD_EP_SELECT | USB_EP_PHY_ADDRESS_BULK_IN );   // Clear EPIN buffers
            usb_lpc_cmd(CMD_EP_CLEAR_BUFFER); 
            usb_lpc_cmd(CMD_EP_CLEAR_BUFFER);  
            //---------------

            GPIB_DevDepInit( udi ); 
            //---------------  --- переход в состояние, когда устройство ждет комманды
        }
     }       
  }          
  if( 0 != err ) 
  {  
     // 27/08/18
     // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
    
     GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
  }  
  return 0;   // -- no bytes in respond
}
// ==============================================================================================
// GPIB_SystemError(...)
// Назначенная комманда - SYSTem:ERRor[:NEXT]?
// Параметры комманды - отсутствуют
// Возвращаемое значение - строка последней ошибки в очереди ошибок
unsigned int GPIB_SystemError( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
  BYTE * pBulkData_DeviceOut = (BYTE*)udi->usbtmcGpib.pData;
  //char * str = pQParameters->str;
  size_t count;
  unsigned int err = 0;
  int MAX = udi->BulkRespondStatus.RespondBufferSize;
  //--------------------------------------------------------
  // request - если пришел запрос Enable? то request - 0x01
  // Request and Command support  
  if(!queue_getcount( pQParameters, &count ))
  {  
     err = ERROR_GPIB_INTERNAL;
  }   
  else
  {  
     if( count != 0)
     {  
        err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
     }  
     else 
     {  
       if( !request ) 
       {  
          err = ERROR_GPIB_REQUEST_ONLY_SUPPORT;
       }  
       else
       {  
          return usbtmc_GetErrorText( udi, pBulkData_DeviceOut, MAX );
       }  
     }  
  } 
  
  if( 0!= err ) 
  {
     // 27/08/18
     // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
     GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
  }
  return 0;
}
// ==============================================================================================
// GPIB_SystemErrorAll(...)
// Назначенная комманда - SYSTem:ERRor:ALL?
// Параметры комманды - отсутствуют
// Возвращаемое значение - строка всех ошибок в очереди
unsigned int GPIB_SystemErrorAll( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
  BYTE * pBulkData_DeviceOut = (BYTE*)udi->usbtmcGpib.pData;
  int MAX = udi->BulkRespondStatus.RespondBufferSize;
  //--------------------------------------------------------
  int a = 0;
  // Request ONLY
   if (request) 
   {
      int c = 0;
      
      do{
       if( a>0 ) {  *(pBulkData_DeviceOut+a) = ','; a++; *(pBulkData_DeviceOut+a) = ' ';a++;}
       c = usbtmc_GetErrorText( udi, pBulkData_DeviceOut+a, MAX-a );
       a+=c;
      }while( GPIB_GET_EAV() );
   }
return a;
}

// ==============================================================================================
// GPIB_EventStatusEnable(...)
// Назначенная комманда - ESE?
// Параметры комманды - отсутствуют или значение регистра
// Возвращаемое значение - значение регистра разрешения статусов событий (Event Status Enable Register)
unsigned int GPIB_EventStatusEnable( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
  size_t count = 0; 
  unsigned  size = 0, err = 0;
  unsigned int ESE;
  char NUMBER[11]={0,0,0,0,0,0,0,0,0,0,0x0A};  
  int MAX = udi->BulkRespondStatus.RespondBufferSize;
  BYTE * pBulkData_DeviceOut = (BYTE*)udi->usbtmcGpib.pData;
  // --------------------------------------------------
  if(!queue_getcount( pQParameters, &count ))
  {  
     err = ERROR_GPIB_INTERNAL;
  }
  else
  {  
       if( count != TernaryOperator(request, 0, 1))
       {  
           err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
       }
       else
       {  
           if( !request && !queue_get( pQParameters, NUMBER, 10, &size )) 
           {  
               err = ERROR_GPIB_INVALID_PARAMETER;
           } 
           else
           {  
               if( !request && NUMBER[0] != eParTypeNR1 )
               {  
                   err = ERROR_GPIB_INVALID_PARAMETER_TYPE;
               }  
               else 
               {  
                   if( !request && 0!=GPIB_ParseNumbers( NUMBER+1,USBTMC_PARSENUMBERS_TYPE_INT,1, (int*)&ESE ))
                   {  
                      err = ERROR_GPIB_INVALID_PARAMETER;      
                   }   
                   else
                   {  
                       if(request) 
                       {  
                           return snprintf(pBulkData_DeviceOut, MAX, "%d", udi->usbtmcGpib.StateMachine.ESE ); 
                       }
                       else 
                       {   
                           udi->usbtmcGpib.StateMachine.ESE = ESE&0xff;
                           USBTMC_StateMachine_Modified( &udi->usbtmcGpib.StateMachine );       
                       }
                   }      
               }          
           }              
       }                  
  }
  
  if( 0 != err )
  {
     // 27/08/18
     // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
     GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
  }  
  
  return 0;
}
// ==============================================================================================
// GPIB_OperationComplete(...)
// Назначенная комманда - OPC?
// Параметры комманды - отсутствуют
// Возвращаемое значение - "1"
unsigned int GPIB_OperationComplete( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
  size_t count = 0; 
  unsigned  /*size = 0,*/ err = 0;
  //  unsigned int ESE;
  int MAX = udi->BulkRespondStatus.RespondBufferSize;
  BYTE * pBulkData_DeviceOut = (BYTE*)udi->usbtmcGpib.pData;
  // --------------------------------------------------
  if(!queue_getcount( pQParameters, &count ))
  {  
      err = ERROR_GPIB_INTERNAL;
  }
  else 
  {  
      if( count != 0)
      {  
          err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
      }   
      else
      {  
            GPIB_SET_OPC();                
            
            if(request)
            {  
               return snprintf(pBulkData_DeviceOut, MAX, "%d", 1 ); 
            }           
            else 
            {   
               (void)0;
            }
      }        
  } 
  
  if( 0!=err )
  {  
      // 27/08/18
      // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
      GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
  } 
  
  return 0;
}
// ==============================================================================================
// GPIB_Wait(...)
// Назначенная комманда - *WAI
// Параметры комманды - отсутствуют
// Возвращаемое значение - отсутствует
unsigned int GPIB_Wait( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
  size_t count = 0; 
  unsigned  /*size = 0,*/ err = 0;
  //  unsigned int ESE;
  //  int MAX = udi->BulkRespondStatus.RespondBufferSize;
  //  BYTE * pBulkData_DeviceOut = (BYTE*)udi->usbtmcGpib.pData;
  // --------------------------------------------------
  if(!queue_getcount( pQParameters, &count ))
  {  
     err = ERROR_GPIB_INTERNAL;
  }
  else
  {
     if( count != 0)
     {  
         err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
     }  
     else
     {  
         if(!request)
         { 
            (void)0;
         }
         else
         {
            (void)0;
         }
     }     
  }     

  if( 0!=err )
  {
    // 27/08/18
    // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
    
    GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
  }  
  
  return 0;
}
// ==============================================================================================
// GPIB_Trigger(...)
// Назначенная комманда - *TRG
// Параметры комманды - отсутствуют
// Возвращаемое значение - отсутствует
unsigned int GPIB_Trigger( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
  size_t count = 0; 
  unsigned int err = 0;
  // --------------------------------------------------
  if(!queue_getcount( pQParameters, &count ))
  {  
     err = ERROR_GPIB_INTERNAL;
  }
  else
  {  
     if( count != 0)
     {  
         err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
     }  
     else
     {  
         if( !request )
            err = ERROR_GPIB_TRIGGER_IGNORED;
         else
            err = ERROR_GPIB_COMMAND_ONLY_SUPPORT;
     }  
  }
  
  if( 0 != err )
  {  
      // 27/08/18
      // GPIB_RaiseStandartError( udi, err, "Trigger ignored", GPIB_ERROR_ID_COR | request );
    
      GPIB_RaiseStandartError( udi, err, "Trigger ignored", errClass_Autodetect );
  }  
  
  return 0;
}
// ==============================================================================================
// GPIB_EventStatusRegister(...)
// Назначенная комманда - *ESR?
// Параметры комманды - отсутствуют
// Возвращаемое значение - значение регистра статсуса событий
unsigned int GPIB_EventStatusRegister( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
  size_t count = 0; 
  unsigned int err = 0;
  unsigned int rc = 0;
  int MAX = udi->BulkRespondStatus.RespondBufferSize;
  BYTE * pBulkData_DeviceOut = (BYTE*)udi->usbtmcGpib.pData;
  // --------------------------------------------------
  if(!queue_getcount( pQParameters, &count ))
  {  
     err = ERROR_GPIB_INTERNAL;
  }
  else 
  {  
    if( !request)
    {  
       err = ERROR_GPIB_REQUEST_ONLY_SUPPORT;
    }   
    else 
    {  
      if( count != 0 )
      {  
           err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
      }   
      else  
      {   
           rc = snprintf(pBulkData_DeviceOut, MAX, "%d", GPIB_GET_ESR() ); 
           
           // IEEE 488.2, 11.5.1.2.2 Reading
           // The Standard Event Status Register is destructively read (that is, read and cleared) with the *ESR? common query, see 10.12.
           GPIB_CLR_ESR();
           
           // 27/08/18 
           // --- replaced with: GPIB_SET_PWN() and GPIB_SET_OPC() ----
           // udi->usbtmcGpib.StateMachine.ESR = 129;
           // USBTMC_StateMachine_Modified( &udi->usbtmcGpib.StateMachine );       
           // 
           // 
           // 27/08/18
           // --- Note: in this implementation the PowerOn(7) and OperationComplete(0) bits are disabled
           // GPIB_SET_PWN();
           // GPIB_SET_OPC(); 
           
      }
    }        
  } 
  
  if( 0 != err ) 
  {  
      // 27/08/18
      // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
    
      GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
  }
  
  return rc;
}

// ==============================================================================================
// GPIB_StatusRegister(...)
// Назначенная комманда - *STB?
// Параметры комманды - отсутствуют
// Возвращаемое значение - значение регистра статсуса устройства
unsigned int GPIB_StatusRegister( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
  size_t count = 0; 
  unsigned int err = 0;
  unsigned int rc = 0;
  int MAX = udi->BulkRespondStatus.RespondBufferSize;
  BYTE * pBulkData_DeviceOut = (BYTE*)udi->usbtmcGpib.pData;
  // --------------------------------------------------
  if(!queue_getcount( pQParameters, &count ))
  {  
      err = ERROR_GPIB_INTERNAL;
  }
  else 
  {  
      if( count != 0)
      {  
         err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
      }   
      else
      {  
         if( !request ) 
         {  
             err = ERROR_GPIB_REQUEST_ONLY_SUPPORT;
         }     
         else  
         {   
             rc = snprintf(pBulkData_DeviceOut, MAX, "%d", udi->usbtmcGpib.StateMachine.STB );
             GPIB_CLR_RQS__();   // no state-machine state modify, clear ServiceRequestBit
         }
      }        
  }  
  
  if( 0!=err )
  {  
     // 27/08/18
     // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
    
     GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
  }
return rc;
}
// ==============================================================================================
// GPIB_StatusEnableRegister(...)
// Назначенная комманда - *SRE?
// Параметры комманды - отсутствуют или значение регистра
// Возвращаемое значение - значение регистра разрешения статсуса устройства
unsigned int GPIB_StatusEnableRegister( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
  size_t count = 0; 
  unsigned int err = 0;
  unsigned int rc = 0;
  unsigned int size = 0;
  BYTE SRE;
  char NUMBER[11]={0,0,0,0,0,0,0,0,0,0,0x0A};  
  int MAX = udi->BulkRespondStatus.RespondBufferSize;
  BYTE * pBulkData_DeviceOut = (BYTE*)udi->usbtmcGpib.pData;
  // --------------------------------------------------
  if(!queue_getcount( pQParameters, &count ))
  {  
     err = ERROR_GPIB_INTERNAL;
  }
  else
  {  
     if( count != ((request)?0:1) )
     {  
         err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
     }     
     else
     {  
         if( !request && !queue_get( pQParameters, NUMBER, 10, &size )) 
         {  
             err = ERROR_GPIB_INVALID_PARAMETER;
         }       
         else 
         {  
            if( !request && NUMBER[0] != eParTypeNR1 )
            {  
                err = ERROR_GPIB_INVALID_PARAMETER_TYPE;
            }        
            else
            {  
                if( !request && 0!=GPIB_ParseNumbers( NUMBER+1,USBTMC_PARSENUMBERS_TYPE_INT,1, (int*)&SRE ))
                {  
                    err = ERROR_GPIB_INVALID_PARAMETER;      
                }         
                else
                {  
                    if(request) 
                    {  
                       rc = snprintf(pBulkData_DeviceOut, MAX, "%d", udi->usbtmcGpib.StateMachine.SRE ); 
                    }   
                    else
                    {  
                       udi->usbtmcGpib.StateMachine.SRE = SRE&0xff;
                    }   
                }         
            }             
         }                
     }                    
  } 
  
  if( 0 != err )
  {
     // 27/08/18
     // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
     GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
  }  
return rc;
}
// ==============================================================================================
/*
// GPIB_StatusOperationEvent(...)
// Назначенная комманда - STATus:OPERation[:EVENt]?
// Параметры комманды - отсутствуют
// Возвращаемое значение - "0"
unsigned int GPIB_StatusOperationEvent( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
  // request равен 0x00 если комманда :STAT:OPER, а не :STAT:OPER? или :STAT:OPER:EVEN?
  // Request ONLY
  size_t count = 0; 
  unsigned   err = 0;
  int MAX = udi->BulkRespondStatus.RespondBufferSize;
  BYTE * pBulkData_DeviceOut = (BYTE*)udi->usbtmcGpib.pData;
  // --------------------------------------------------
  if(!queue_getcount( pQParameters, &count ))
  {  
     err = ERROR_GPIB_INTERNAL;
  }
  else 
  {  
     if( count != 0)
     {  
        err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
     }  
     else
     {  
        if( !request ) 
        {  
            err = ERROR_GPIB_REQUEST_ONLY_SUPPORT;
        }     
        else
        {  
            return snprintf(pBulkData_DeviceOut, MAX, "%d", 0 ); 
        }        
     }              
  }   
  
  if( 0!= err ) 
  { 
     // 27/08/18
     // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
     GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
  }
  
  return 0;
}
*/
/*
// ==============================================================================================
// GPIB_StatusOperationCondition(...)
// Назначенная комманда - STATus:OPERation:CONDition?
// Параметры комманды - отсутствуют
// Возвращаемое значение - "0"
unsigned int GPIB_StatusOperationCondition( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
  // request - всегда 0x01
  // Request ONLY
  return GPIB_StatusOperationEvent(udi, pQParameters, request);
}
*/
/*
// ==============================================================================================
// GPIB_StatusOperationEnable(...)
// Назначенная комманда - STATus:OPERation:ENABle?
// Параметры комманды - отсутствуют
// Возвращаемое значение - "0"
unsigned int GPIB_StatusOperationEnable( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{  // request - если пришел запрос Enable? то request - 0x01
   // Request and Command support
  return GPIB_StatusQuestionableEnable(udi, pQParameters, request);
}
*/
/*
// ==============================================================================================
// GPIB_StatusQuestionableEvent(...)
// Назначенная комманда - STATus:QUEStionable[:EVENt]?
// Параметры комманды - отсутствуют
// Возвращаемое значение - "0"
unsigned int GPIB_StatusQuestionableEvent( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{  // request равен 0x00 если комманда :STAT:QUES, а не :STAT:QUES? или :STAT:QUES:EVEN?
   // Request ONLY
  return GPIB_StatusOperationEvent(udi, pQParameters, request);
}
*/
/*
// ==============================================================================================
// GPIB_StatusQuestionableCondition(...)
// Назначенная комманда - STATus:QUEStionable:CONDition?
// Параметры комманды - отсутствуют
// Возвращаемое значение - "0"
unsigned int GPIB_StatusQuestionableCondition( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{ // request равен 0x00 если комманда :STAT:QUES, а не :STAT:QUES? или :STAT:QUES:EVEN?
  // Request ONLY
  return GPIB_StatusOperationEvent(udi, pQParameters, request);
}
*/
// ==============================================================================================
// GPIB_StatusQuestionableEnable(...)
// Назначенная комманда - STATus:QUEStionable:ENABle?
// Параметры комманды - отсутствуют
// Возвращаемое значение - "0"
/*
unsigned int GPIB_StatusQuestionableEnable( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
  // request - если пришел запрос Enable? то request - 0x01
  // Request and Command support
  size_t count = 0; 
  unsigned   err = 0;
  int MAX = udi->BulkRespondStatus.RespondBufferSize;
  BYTE * pBulkData_DeviceOut = (BYTE*)udi->usbtmcGpib.pData;
  // --------------------------------------------------
  if(!queue_getcount( pQParameters, &count ))
  {  
     err = ERROR_GPIB_INTERNAL;
  }  
  else
  {  
     if( count != ((request)?0:1) )
     {  
        err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
     }   
     else 
     {  
        if( !request ) 
        {  
            (void)0;
        }        
        else
        {  
            return snprintf(pBulkData_DeviceOut, MAX, "%d", 0 );            
        }        
     }        
  }   
  
  if( 0!=err )
  { 
      // 27/08/18
      // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
    
      GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
  }
return 0;
}  
*/
/*
// ==============================================================================================
// GPIB_StatusPreset(...)
// Назначенная комманда - *RST, STATus:PRESet
// Параметры комманды - отсутствуют
// Возвращаемое значение - отсутствует
unsigned int GPIB_StatusPreset( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{  // request  всегда 0x00
   // Command support only
  return GPIB_Reset(udi, pQParameters, request);
}
*/
// ==============================================================================================
// GPIB_InterfaceSwitch(...)
// Назначенная комманда - INTerface:SWitch? - переключение режима SCPI -> EZUSB
// Параметры комманды - отсутствуют
// Возвращаемое значение - строка результата операции
unsigned int GPIB_InterfaceSwitch( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{ 
  unsigned int rc = 0;
  BYTE * pBulkData_DeviceOut = (BYTE*)udi->usbtmcGpib.pData;
  int MAX = udi->BulkRespondStatus.RespondBufferSize;
  // -------------------------------------------------------
  if( InterfaceSwitch( udi, USB_INTERFACE_CONT )==0x01 )
  {
      if( request) 
      {  
          rc = snprintf( pBulkData_DeviceOut, MAX, "%s", "! The Interface will be switched at next connecting a USB cable!" );
      }  
      
      {
          SET_SYSTEM_CODE(CODE_REBOOT_ID, CODE_REBOOT );  // магическое число
          SET_SYSTEM_FLAG(FLAG_REBOOT);                   // флажок на перезагрузку
          DeviceBusy( 1 );  // device is busy by writing.
      }
  } 
  else 
  {
      if( request)
      {  
          rc = snprintf( pBulkData_DeviceOut, MAX, "%s", "# There was SPI flash error. The Interface will not be switched!" );
      }
      
      GPIB_RaiseStandartError( udi, ERROR_GPIB_INTERNAL, NULL, errClass_Autodetect );
  }
  
  return rc;
}
// ==============================================================================================
// GPIB_GetTemperature(...)
// Назначенная комманда - MEASure:TEMPerature? 
// Параметры комманды - отсутствуют
// Возвращаемое значение - значение температуры в градусах
unsigned int GPIB_GetTemperature( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
   BYTE * pBulkData_DeviceOut = (BYTE*)udi->usbtmcGpib.pData;
   int MAX = udi->BulkRespondStatus.RespondBufferSize;
   size_t count =0;
   int err = 0;
   // -------------------------------------------------
   if(!queue_getcount( pQParameters, &count))
   {  
       err = ERROR_GPIB_INTERNAL;
   }   
   else
   {  
       if( count != 0 ) 
       {  
           err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
       }  
       else 
       {  
           if( !request )
           {  
               err = ERROR_GPIB_REQUEST_ONLY_SUPPORT;
           }  
           else
           {  
               return temperature_convert( pBulkData_DeviceOut, MAX, get_average_temperature() );
           }  
       }  
  }
  
  if( 0 != err )
  {
     // 27/08/18
     // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
    
     GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
  }
  
  return 0;
}

// GPIB_SwitchCount(...)
// Назначенная комманда - [SYSTem:]SWITch:COUNt?
// Параметры комманды - НЕТ
// Возвращаемое значение - общее количество  состояний портов
unsigned int GPIB_SwitchCount( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{            
  size_t count;  
  unsigned int err = 0;
  unsigned int rc = 0;
  //-----------------------------------------------------------------------------------------
  if(!queue_getcount(pQParameters, &count))
  {  
      err = ERROR_GPIB_INTERNAL; 
      goto L_GPIB_SwitchList_EXIT;
  } 
  
  if(!request)
  {  
      err = ERROR_GPIB_REQUEST_ONLY_SUPPORT;
      goto L_GPIB_SwitchList_EXIT;
  }
  
  if( count != 0 )
  {  
      err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
      goto L_GPIB_SwitchList_EXIT;
  }         
   
  {
      BYTE * pBuffer = (BYTE*)udi->usbtmcGpib.pData;
      int freeSpace = udi->BulkRespondStatus.RespondBufferSize;
      int32_t states = (int32_t)ACMClass->properties.allowedStatesCount;
      
      rc = GPIB_Integer2Str( states, pBuffer, freeSpace, Format_Int2Str_N );
  }

L_GPIB_SwitchList_EXIT:  
  
  if( err != 0 ) 
  {
      // 27/08/18
      // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
      
      GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
      rc = 0;
  }

  return rc;
}

// GPIB_SwitchList(...)
// Назначенная комманда - [SWITch:]LIST?
// Параметры комманды - порт( A, B, AB )
// Возвращаемое значение - количество  состояний порта
unsigned int GPIB_SwitchList( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{              
  size_t count;  
  unsigned int err = 0;
  unsigned int rc = 0;
  //-----------------------------------------------------------------------------------------
  if(!queue_getcount(pQParameters, &count))
  {  
      err = ERROR_GPIB_INTERNAL; 
      goto L_GPIB_SwitchList_EXIT;
  } 
  
  if(!request)
  {  
      err = ERROR_GPIB_REQUEST_ONLY_SUPPORT;
      goto L_GPIB_SwitchList_EXIT;
  }
  
  if( count != 0 )
  {  
      err = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
      goto L_GPIB_SwitchList_EXIT;
  }         
   
  {
      BYTE * pBuffer = (BYTE*)udi->usbtmcGpib.pData;
      int freeSpace = udi->BulkRespondStatus.RespondBufferSize;
      
      rc = ACMClass->methods.getKeyStateList( pBuffer, freeSpace );      
  }

L_GPIB_SwitchList_EXIT:  
  
  if( err != 0 ) 
  {
      // 27/08/18
      // GPIB_RaiseStandartError( udi, err, NULL, GPIB_ERROR_ID_COR | request );
      GPIB_RaiseStandartError( udi, err, NULL, errClass_Autodetect );
      rc = 0;
  }

  return rc;
}

//------------------------------------------------------------------------------
// __Util_CheckParamTableId:
// Is used as a universal parameter checker for following functions:
//  * GPIB_MemoryTableTime
//  * GPIB_MemoryTableDate
//  etc.
// All the functions listed above have the same "command prototype" - MEM:TABLE:CMD [FACTory|USER1|USER2|USER3]
// This function checks the only parameter - characterization table id (factory/user1/user2/user3),
// parses it and converts into the [eChrz_t].
// The function returns the error code.
static unsigned int __Util_CheckParamTableId( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request, 
                                        eChrz_t * pId )
{   
    unsigned int errCode = 0;
    size_t       rc = 0;    
    char         textBuffer[16];      
    eChrz_t      id;             // Characterization Table Id    
    
    // check the requiest status
    if(!request )
    {
        // error: command must be a request
        errCode = ERROR_GPIB_REQUEST_ONLY_SUPPORT; 
        goto L__Util_CheckParamTableId_EXIT;
    }
    
    // Retrieve amount of parameters from the queue
    if( !queue_getcount(pQParameters, &rc) )
    {  
        // error: can't get parameters count
        errCode = ERROR_GPIB_INTERNAL; 
        goto L__Util_CheckParamTableId_EXIT;
    }
    // Check parameters count
    else if( 1 < rc )          // only one OPTIONAL parameter allowed for this command
    {
        // error: wrong parameters count ( must be 0 or 1 )
        errCode = ERROR_GPIB_WRONG_PARAMETERS_COUNT; 
        goto L__Util_CheckParamTableId_EXIT; 
    }
    
    if( 1 == rc ) // one parameter - table identifier (factory/user1/user2/user3)
    {  
        // optional parameter is specified
      
        // retrieve the first parameter: table identifier (factory/user1/user2/user3)
        if( !queue_get( pQParameters, (BYTE*)textBuffer, sizeof(textBuffer), &rc ))
        {
            errCode = ERROR_GPIB_INVALID_PARAMETER; 
            goto L__Util_CheckParamTableId_EXIT; 
        }
        // check the parameter type
        else if( eParTypeChar != textBuffer[0] )
        {
            errCode = ERROR_GPIB_INVALID_PARAMETER_TYPE; 
            goto L__Util_CheckParamTableId_EXIT;
        }
        
        // retrieve the parameter
        id = GPIB_GetChrzTableId( &textBuffer[1], rc - 1 );
        
        if( eCh_MAX == id )
        {
            errCode = ERROR_GPIB_INVALID_PARAMETER; 
            goto L__Util_CheckParamTableId_EXIT; 
        }
    }
    else
    {
        // optional parameter is absent
      
        // table identifier omitted, "FACTory" is supposed
        id = eChFactory;
    }
    
L__Util_CheckParamTableId_EXIT:
    if( 0 == errCode )
    {
        if( NULL != pId )
        {
            *pId = id;
        }
    }
  
    return errCode;
}
//------------------------------------------------------------------------------

// Descriptions for elements of [eChrz_t]
static const char * const aChrzDesc[] = 
{
   "Factory characterization data",  // eChFactory
   "USER1 characterization data",    // eChUser1
   "USER2 characterization data",    // eChUser2
   "USER3 characterization data",    // eChUser3
   NULL                         // eCh_MAX
};

const char pcThermoDesc[] = "Thermocompensation data";

// Descriptions for elements of [ePortComb_t]
static const char * const aPortComb[] = 
{
   "Port A"      , // ePortComb_A
   "Port B"      , // ePortComb_B
   "Port C"      , // ePortComb_C
   "Port D"      , // ePortComb_D
   "Thru AB"     , // ePortComb_AB
   "Thru AC"     , // ePortComb_AC
   "Thru AD"     , // ePortComb_AD
   "Thru BC"     , // ePortComb_BC
   "Thru BD"     , // ePortComb_BD
   "Thru CD"     , // ePortComb_CD
   "CHECK state" , // ePortComb_CHECK
   NULL                         // ePortComb_MAX
};

/*
// Descriptions for elements of [eChrz_t]
static const char * aPortDesc[] = 
{
   "port A",    // ePortId_A
   "port B",    // ePortId_B
   "port C",    // ePortId_C
   "port D",    // ePortId_D
   NULL                         // eCh_MAX
};*/

//------------------------------------------------------------------------------
// GPIB_MemoryTablePoints()
// Prints amount of characterization points of the characterization table: factory or user
size_t GPIB_MemoryTablePoints( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
    
    eChrz_t      id = eCh_MAX;
    unsigned int errCode = __Util_CheckParamTableId( udi, pQParameters, request, &id );
        
    size_t       rc = 0;    
    size_t       nPoints;
    
    if( 0 == errCode )
    {
        nPoints = ACMClass->methods.xCharacterization.getPointsCount( id );
         
        if( 0 != nPoints )
        {    
            rc = snprintf( (char*)pBuffer, freeSpace, "%d", nPoints );
        
            if( 0 == rc || rc == freeSpace )
            {
                errCode = ERROR_GPIB_BUFFER_OVERFLOW;
            }
        }
        else
        {
            errCode = ERROR_GPIB_DATA_NOT_FOUND;
        }
    }
    
// L_GPIB_MemoryTablePoints_EXIT:
  
    if( 0 != errCode )
    {       
        const char * pDescription = NULL;
        
        if( eCh_MAX != id )
        {
            pDescription = aChrzDesc[ (size_t)id ];
        }
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        rc = 0;
    }
    
    return rc;
}

//------------------------------------------------------------------------------
// GPIB_MemoryTableDate()
// Prints the characterization date
size_t GPIB_MemoryTableDate( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
    
    eChrz_t      id = eCh_MAX;
    unsigned int errCode = __Util_CheckParamTableId( udi, pQParameters, request, &id );
        
    size_t       rc = 0;    
    
    if( 0 == errCode )
    {
        if( !ACMClass->methods.xCharacterization.getDate( id, (char*)pBuffer, freeSpace, &rc ) )
        {    
            errCode = ERROR_GPIB_DATA_NOT_FOUND; 
        }        
    }
      
    if( 0 != errCode )
    {
        const char * pDescription = NULL;
        
        if( eCh_MAX != id )
        {
            pDescription = aChrzDesc[ (size_t)id ];
        }
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        rc = 0;
    }
    
    return rc;
}

// GPIB_MemoryTableTime()
// Prints the characterization time
size_t GPIB_MemoryTableTime( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
    
    eChrz_t      id = eCh_MAX;
    unsigned int errCode = __Util_CheckParamTableId( udi, pQParameters, request, &id );
        
    size_t       rc = 0;    
    
    if( 0 == errCode )
    {
        if( !ACMClass->methods.xCharacterization.getTime( id, (char*)pBuffer, freeSpace, &rc ) )
        {    
            errCode = ERROR_GPIB_DATA_NOT_FOUND; 
        }       
    }
      
    if( 0 != errCode )
    {
        const char * pDescription = NULL;
        
        if( eCh_MAX != id )
        {
            pDescription = aChrzDesc[ (size_t)id ];
        }
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        rc = 0;
    }
    
    return rc;
}

// GPIB_MemoryTableTemp()
// Prints the characterization temperature
size_t GPIB_MemoryTableTemp( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
    
    eChrz_t      id = eCh_MAX;
    unsigned int errCode = __Util_CheckParamTableId( udi, pQParameters, request, &id );
    double       temperature = 0;    
    size_t       rc = 0;    
    
    if( 0 == errCode )
    {
        if( !ACMClass->methods.xCharacterization.getChrzTemp( id, &temperature ) )
        {    
            errCode = ERROR_GPIB_DATA_NOT_FOUND; 
        }
        else
        {
            rc = GPIB_Double2Str( temperature, (char*)pBuffer, freeSpace, Format_Double2Str_Ndot3 );
        
            if( 0 == rc )
            {
                errCode = ERROR_GPIB_BUFFER_OVERFLOW; 
            }
        }    
    }
      
    if( 0 != errCode )
    {
        const char * pDescription = NULL;
        
        if( eCh_MAX != id )
        {
            pDescription = aChrzDesc[ (size_t)id ];
        }
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        rc = 0;
    }
    
    return rc;
}

// GPIB_MemoryTableFreqType()
// Prints the type of the characterization frequency scale
size_t GPIB_MemoryTableFreqType( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
    
    eChrz_t      id = eCh_MAX;
    unsigned int errCode = __Util_CheckParamTableId( udi, pQParameters, request, &id );
        
    size_t       rc = 0;    
    
    if( 0 == errCode )
    {
        eChrzScaleType_t scaleType;
        
        if( !ACMClass->methods.xCharacterization.getScaleType( id, &scaleType ) )
        {    
            errCode = ERROR_GPIB_DATA_NOT_FOUND; 
        }
        else
        {            
            if( eChScaleLinear == scaleType )
            {
                rc = snprintf( (char*)pBuffer, freeSpace, "%s", "LIN" );
            }
            else if( eChScaleSegment == scaleType )
            {
                rc = snprintf( (char*)pBuffer, freeSpace, "%s", "SEGM" );
            } 
            else rc = 0;
        }
    }
      
    if( 0 != errCode )
    {
        const char * pDescription = NULL;
        
        if( eCh_MAX != id )
        {
            pDescription = aChrzDesc[ (size_t)id ];
        }
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        
        rc = 0;
    }
    
    return rc;
}

// GPIB_MemoryTableFreqData()
// Prints all the frequency points of the characterization frequency scale
size_t GPIB_MemoryTableFreqData( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
// 1
    char    *    pBuffer        = (char*)udi->usbtmcGpib.pData;
    size_t       freeSpace      = udi->BulkRespondStatus.RespondBufferSize;  
    size_t       rc             = 0;         // full amount of bytes returned
    size_t       bytes          = 0;         // bytes returned
    unsigned int errCode        = 0;         // error identifier
    sEcalSegment_t segment;                  // scale segment data    
    
    size_t       iSeg = 0, nSeg = 0;         // amount of segments
    size_t       iPnt = 0, nPnt = 0;         // amount of points
    eChrz_t      id             = eCh_MAX;   // characterization id
    eChrzScaleType_t scaleType;              // scale type
    
// 2       
    // Check if the function context is saved and must be continued
    if( !udi->GPIBFunctionContext.bEnable )
    {
        // no context found
      
        // check input parameters        
        errCode = __Util_CheckParamTableId( udi, pQParameters, request, &id );
        
        if( 0 != errCode || eCh_MAX == id )
        {
            goto L_GPIB_MemoryTableFreqData_EXIT;
        }
        
        // retrieve the scale type
        if( !ACMClass->methods.xCharacterization.getScaleType( id, &scaleType ) )
        {    
            errCode = ERROR_GPIB_DATA_NOT_FOUND; 
            goto L_GPIB_MemoryTableFreqData_EXIT;
        }
             
        if( eChScaleLinear == scaleType )
        {  
            // Linear scale
          
            nSeg = 1;
            iSeg = 0;
            iPnt = 0;            
        }
        else
        {  
            // Segment scale
          
            // read amount of segments
            nSeg = ACMClass->methods.xCharacterization.getScaleSegment( id, NULL, 0 );
            iSeg = 0;
            iPnt = 0;
            
            if( 0 == nSeg )
            {
                errCode = ERROR_GPIB_DATA_NOT_FOUND; 
                goto L_GPIB_MemoryTableFreqData_EXIT;
            }
        }
    }
// 3
    else
    {
        // load previous context
        /*  */
        if( 
               queue_get( &udi->GPIBFunctionContext.Context, (char*)&id,    sizeof(id),   &bytes )  // load memory segment id
            && queue_get( &udi->GPIBFunctionContext.Context, (char*)&nSeg , sizeof(nSeg), &bytes )  // load amount of segments
            && queue_get( &udi->GPIBFunctionContext.Context, (char*)&iSeg , sizeof(iSeg), &bytes )  // load segment index
            && queue_get( &udi->GPIBFunctionContext.Context, (char*)&nPnt , sizeof(nPnt), &bytes )  // load amount of points
            && queue_get( &udi->GPIBFunctionContext.Context, (char*)&iPnt , sizeof(iPnt), &bytes )  // load point index
            && queue_get( &udi->GPIBFunctionContext.Context, (char*)&scaleType , sizeof(scaleType), &bytes )  // load the scale type
        )
        {
            // OK
        }
        else
        {
            errCode = ERROR_GPIB_INTERNAL;
            goto L_GPIB_MemoryTableFreqData_EXIT;
        }
    }
    
    rc = 0;
// 4
    // try to print frequencies list
    for( (void)iSeg; iSeg < nSeg; ++iSeg )  // for each segment
    {            
          // For segment scale: load the segment data
          if( eChScaleSegment == scaleType )
          {  
              // retrieve the segment information
              if( 1 != ACMClass->methods.xCharacterization.getScaleSegment( id, &segment, iSeg ) )
              {
                  errCode = ERROR_GPIB_DATA_NOT_FOUND;
                  goto L_GPIB_MemoryTableFreqData_EXIT;
              }
              
              // load the points count
              nPnt = segment.Points;
          }
          
          if( eChScaleLinear == scaleType )
          {
              // load the points count
              nPnt = ACMClass->methods.xCharacterization.getPointsCount( id );
              
              // load start and stop frequencies
              if(  !ACMClass->methods.xCharacterization.getStartFreq( id, &segment.Fstart ) 
                || !ACMClass->methods.xCharacterization.getStopFreq( id, &segment.Fstop ) )
              {
                  errCode = ERROR_GPIB_DATA_NOT_FOUND;
                  goto L_GPIB_MemoryTableFreqData_EXIT;
              }
              
              // store @nPnt into the @segment to unify the access
              segment.Points = nPnt;
          }
          
          if( 0 == nPnt )
          {
              errCode = ERROR_GPIB_DATA_NOT_FOUND; 
              goto L_GPIB_MemoryTableFreqData_EXIT;
          }
                    
          for( (void)iPnt; iPnt < nPnt; ++iPnt )  // for each point 
          {
              // One point has the following format:
              // <Freq_i>,<SPACE>
              // where @i is the point number,
              // @nPnt - amount of points.              
              // Total amount of numbers to be print: nPnt
              // One number has 16 symbols length plus 2 (comma and space)
              // So, 18 characters required to print one point.
              // To prevent overflowing by snprintf() with printing null-term,
              // it is required at lease 20 characters free 
              if( freeSpace >= 20 )
              {   
                  double dbl_freq_point = 0.0;
                  
                  if( segment.Points > 1 )
                  {  
                        // e.g. segment #1, (1.0e+5;2.0e+5,11 points)
                        // START_FREQ=100000; STOP_FREQ=200000; N=11
                        // 100000, 110000, 120000, 130000, 140000         } 5 points
                        // 150000, 160000, 170000, 180000, 190000, 20000  } 6 points
                        // Total: 11 points on (1.0e+5;2.0e+5)
                        // So, STEP is (STOP_FREQ - START_FREQ)/(N-1) = 10000
                    
                        #define SegmentFreqStep( seg )   ((seg.Fstop - seg.Fstart) / (seg.Points - 1))
                      
                        // calculate current frequency point (MAGNITUDE)
                        // print the frequency point
                        dbl_freq_point = segment.Fstart + SegmentFreqStep( segment ) * iPnt;
                        /*
                        bytes = GPIB_Double2Str( segment.Fstart + SegmentFreqStep( segment ) * iPnt, 
                                                 pBuffer, freeSpace, Format_Double2Str_Sci );*/
                  }
                  else
                  {
                        dbl_freq_point = segment.Fstart;
                        /*
                        bytes = GPIB_Double2Str( segment.Fstart, 
                                                 pBuffer, freeSpace, Format_Double2Str_Sci );*/                    
                  }
                  
                  {   // fix: 05/09/18
                      // round the value to [Hz]
                      dbl_freq_point = round( dbl_freq_point );
                        
                      // print rounded value
                      bytes = GPIB_Double2Str( dbl_freq_point, 
                                                 pBuffer, freeSpace, Format_Double2Str_Sci );
                  }
                  
                  pBuffer += bytes;
                  freeSpace -= bytes;
                  rc += bytes;
                                    
                  // print the delimeter
                  bytes = snprintf( pBuffer, freeSpace, "%s", ", " );
                      
                  pBuffer += bytes;
                  freeSpace -= bytes;
                  rc += bytes;                  
              }
              else
              {
                  // --------------------------------------------------------------------------
                  // Save context
                  usbtmc_create_function_context( udi, (void*)GPIB_MemoryTableFreqData ); 
                  
                  if(   !queue_add( &udi->GPIBFunctionContext.Context, (char*)&id,    sizeof(id) )      // Save memory segment id
                     || !queue_add( &udi->GPIBFunctionContext.Context, (char*)&nSeg , sizeof(nSeg) )    // Save amount of segments
                     || !queue_add( &udi->GPIBFunctionContext.Context, (char*)&iSeg , sizeof(iSeg) )    // Save segment index
                     || !queue_add( &udi->GPIBFunctionContext.Context, (char*)&nPnt , sizeof(nPnt) )    // Save amount of points
                     || !queue_add( &udi->GPIBFunctionContext.Context, (char*)&iPnt , sizeof(iPnt) )    // Save point index  
                     || !queue_add( &udi->GPIBFunctionContext.Context, (char*)&scaleType , sizeof(scaleType) ) // Save the scale type
                    )
                  {
                     udi->GPIBFunctionContext.LastFunction = NULL;
                     errCode = ERROR_GPIB_INTERNAL;
                     goto L_GPIB_MemoryTableFreqData_EXIT;
                  }
                  
                  udi->BulkRespondStatus.bIsLastTransfer = FALSE;
                  errCode = 0;
                  // --------------------------------------------------------------------------
                  
                  goto L_GPIB_MemoryTableFreqData_EXIT;
              }
              
              /*for( const char * p = __Buffer; p != pBuffer; ++p )
              {
                   if( *p == '\0' )
                   {
                     asm( "bkpt #0" );
                   }
              }*/
          }
          
          // For the last point of last segment
          if( iSeg >= nSeg-1 )
          if( iPnt >= nPnt )
          {
              if( rc > 2 )
                  rc -= 2;  // cut off the last comma separator and space
              
              udi->BulkRespondStatus.bIsLastTransfer = TRUE;
          }
          
          iPnt = 0;
    }
    
// 5    
L_GPIB_MemoryTableFreqData_EXIT:    
  
    if( 0 != errCode )
    {
        const char * pDescription = NULL;
        
        if( eCh_MAX != id )
        {
            pDescription = aChrzDesc[ (size_t)id ];
        }
        
        udi->BulkRespondStatus.bIsLastTransfer = TRUE;
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        
        rc = 0;
    }
    
    return rc;
}

// GPIB_MemoryTableFreqSegmData()
// Prints the scale segments parameters of the characterization frequency scale
size_t GPIB_MemoryTableFreqSegmData( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
// 1
    char    *    pBuffer       = (char*)udi->usbtmcGpib.pData;
    size_t       freeSpace     = udi->BulkRespondStatus.RespondBufferSize;  
    size_t       rc            = 0;         // full amount of bytes returned
    size_t       bytes         = 0;         // bytes returned
    size_t       i, n          = 0;         // amount of segments
    eChrz_t      id            = eCh_MAX;   // characterization id
    unsigned int errCode       = 0;         // error identifier
    sEcalSegment_t segment;                 // scale segment data
    eChrzScaleType_t scaleType;             // scale type
    
// 2       
    // Check if the function context is saved and must be continued
    if( !udi->GPIBFunctionContext.bEnable )
    {
        // no context found
      
        // check input parameters        
        errCode = __Util_CheckParamTableId( udi, pQParameters, request, &id );
        
        if( 0 != errCode || eCh_MAX == id )
        {
            goto L_GPIB_MemoryTableFreqSegmData_EXIT;
        }
        
        // retrieve the scale type
        // check the conditions: the scale must be a Segment Type Scale
        if(   !ACMClass->methods.xCharacterization.getScaleType( id, &scaleType ) 
           || eChScaleSegment != scaleType)
        {    
            errCode = ERROR_GPIB_DATA_NOT_FOUND; 
            goto L_GPIB_MemoryTableFreqSegmData_EXIT;
        }
        
        // read amount of segments
        n = ACMClass->methods.xCharacterization.getScaleSegment( id, NULL, 0 );
        i = 0;
        
        if( 0 == n )
        {
            errCode = ERROR_GPIB_DATA_NOT_FOUND; 
            goto L_GPIB_MemoryTableFreqSegmData_EXIT;
        }
        
        // print amount of segements
        bytes = snprintf( pBuffer, freeSpace, "%d%s", n, ",\n" /* ",\r\n" */);
        
        if( 0 == bytes || bytes == freeSpace )
        {
            errCode = ERROR_GPIB_BUFFER_OVERFLOW;
            goto L_GPIB_MemoryTableFreqSegmData_EXIT;
        }
        else
        {
            rc        += bytes;
            freeSpace -= bytes;
            pBuffer   += bytes;
        }
    }
// 3
    else
    {
        // load previous context
        /*  */
        if(    queue_get( &udi->GPIBFunctionContext.Context, (char*)&id, sizeof(id),&bytes )  // load memory segment id
            && queue_get( &udi->GPIBFunctionContext.Context, (char*)&n , sizeof(n), &bytes )  // load amount of segments
            && queue_get( &udi->GPIBFunctionContext.Context, (char*)&i , sizeof(i), &bytes )  // load segment number
        )
        {
            // OK
        }
        else
        {
            errCode = ERROR_GPIB_INTERNAL;
            goto L_GPIB_MemoryTableFreqSegmData_EXIT;
        }
    }
    
// 4
    // try to print the segment list
    for( /*@i is loaded from context*/; i < n; ++i )
    {
        // segment data format:
        // <StartFreq>, <StopFreq>, <NumberOfPoints><CR><LF>
        // e.g.
        // 1.0000000000e+5, 2.0000000000e+6, 13<CR><LF>
        // Minimum free space required to one segment: 36 + amount of chars in <NumberOfPoints>
        // Let it be 4.
        // Whole number of free bytes in the buffer: 40
        // To prevent overflowing by snprintf() with printing null-term,
        // it is required at lease 44 characters free 
        if( freeSpace >= 44 )
        {
            if( 1 == ACMClass->methods.xCharacterization.getScaleSegment( id, &segment, i ) )
            {
                // print the segment data
              
                bytes = GPIB_Double2Str( segment.Fstart, pBuffer, freeSpace, Format_Double2Str_Sci );
                
                pBuffer += bytes;
                freeSpace -= bytes;
                rc += bytes;
                
                bytes = snprintf( pBuffer, freeSpace, "%s", ", " );
                
                pBuffer += bytes;
                freeSpace -= bytes;
                rc += bytes;
                
                bytes = GPIB_Double2Str( segment.Fstop, pBuffer, freeSpace, Format_Double2Str_Sci );
                
                pBuffer += bytes;
                freeSpace -= bytes;
                rc += bytes;
                
                bytes = snprintf( pBuffer, freeSpace, "%s", ", " );
                
                pBuffer += bytes;
                freeSpace -= bytes;
                rc += bytes;
                
                bytes = snprintf( pBuffer, freeSpace, "%d%s", segment.Points, "\n"/*"\r\n"*/ );
                
                pBuffer += bytes;
                freeSpace -= bytes;
                rc += bytes;
            }
            else
            {
                errCode = ERROR_GPIB_DATA_NOT_FOUND;
                goto L_GPIB_MemoryTableFreqSegmData_EXIT;
            }           
        }
        else
        {
            // --------------------------------------------------------------------------
            // Save context
            usbtmc_create_function_context( udi, (void*)GPIB_MemoryTableFreqSegmData ); 
            
            if(   !queue_add( &udi->GPIBFunctionContext.Context, (char*)&id, sizeof(id) )   // Save memory segment id
               || !queue_add( &udi->GPIBFunctionContext.Context, (char*)&n , sizeof(n) )    // Save amount of segments
               || !queue_add( &udi->GPIBFunctionContext.Context, (char*)&i , sizeof(i) )    // Save segment number
              )
            {
               udi->GPIBFunctionContext.LastFunction = NULL;
               errCode = ERROR_GPIB_INTERNAL;
               goto L_GPIB_MemoryTableFreqSegmData_EXIT;
            }
            
            udi->BulkRespondStatus.bIsLastTransfer = FALSE;
            errCode = 0;
            // --------------------------------------------------------------------------
            
            break;
        }
    }
// 5  
L_GPIB_MemoryTableFreqSegmData_EXIT:
  
    if( 0 != errCode )
    {
        const char * pDescription = NULL;
        
        if( eCh_MAX != id )
        {
            pDescription = aChrzDesc[ (size_t)id ];
        }
        
        udi->BulkRespondStatus.bIsLastTransfer = TRUE;
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        
        rc = 0;
    }
    
    return rc;
}

// GPIB_MemoryTableFreqStart()
// Prints the start frequency of the characterization frequency scale
size_t GPIB_MemoryTableFreqStart( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
    
    eChrz_t      id = eCh_MAX;
    unsigned int errCode = __Util_CheckParamTableId( udi, pQParameters, request, &id );
        
    size_t       rc = 0;    
    
    double       startFreq = 0.0f;
    
    if( 0 == errCode )
    {
        if( !ACMClass->methods.xCharacterization.getStartFreq( id, &startFreq ) )
        {    
            errCode = ERROR_GPIB_DATA_NOT_FOUND; 
        }
        else
        {            
            rc = GPIB_Double2Str( startFreq, (char*)pBuffer, freeSpace, Format_Double2Str_Sci );
        }
    }
      
    if( 0 != errCode )
    {
        const char * pDescription = NULL;
        
        if( eCh_MAX != id )
        {
            pDescription = aChrzDesc[ (size_t)id ];
        }
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        
        rc = 0;
    }
    
    return rc;
}

// GPIB_MemoryTableFreqStop()
// Prints the stop frequency of the characterization frequency scale
size_t GPIB_MemoryTableFreqStop( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
    
    eChrz_t      id = eCh_MAX;
    unsigned int errCode = __Util_CheckParamTableId( udi, pQParameters, request, &id );
        
    size_t       rc = 0;    
    
    double       stopFreq = 0.0f;
    
    if( 0 == errCode )
    {
        if( !ACMClass->methods.xCharacterization.getStopFreq( id, &stopFreq ) )
        {    
            errCode = ERROR_GPIB_DATA_NOT_FOUND; 
        }
        else
        {            
            rc = GPIB_Double2Str( stopFreq, (char*)pBuffer, freeSpace, Format_Double2Str_Sci );
        }
    }
      
    if( 0 != errCode )
    {
        const char * pDescription = NULL;
        
        if( eCh_MAX != id )
        {
            pDescription = aChrzDesc[ (size_t)id ];
        }
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        
        rc = 0;
    }
    
    return rc;
}

// GPIB_MemTableAdapter()
// 
size_t GPIB_MemoryTableAdapter( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
        
    size_t       rc = 0;
    size_t       nParams = 0;
    unsigned int errCode = 0;
        
    eChrz_t      id = eCh_MAX;   // by default
    ePortId_t    pid;             // Port ID
    char         textBuffer[16];          
    
    // check the requiest status
    if(!request )
    {
        // error: command must be a request
        errCode = ERROR_GPIB_REQUEST_ONLY_SUPPORT; 
        goto L_GPIB_MemoryTableAdapter_EXIT;
    }
    
    // Retrieve amount of parameters from the queue
    if( !queue_getcount(pQParameters, &nParams) )
    {  
        // error: can't get parameters count
        errCode = ERROR_GPIB_INTERNAL; 
        goto L_GPIB_MemoryTableAdapter_EXIT;
    }
    // Check parameters count
    else if( 1 != nParams && 2 != nParams )  // this command must have 1 or 2 parameters
    {
        // error: wrong parameters count ( must be 1 )
        errCode = ERROR_GPIB_WRONG_PARAMETERS_COUNT; 
        goto L_GPIB_MemoryTableAdapter_EXIT; 
    }
    
    // first parameter - port identifier (A/B/C/D)
    {   
        // retrieve the first parameter: port identifier (A/B/C/D)
        if( !queue_get( pQParameters, (BYTE*)textBuffer, sizeof(textBuffer), &rc ))
        {
            errCode = ERROR_GPIB_INVALID_PARAMETER; 
            goto L_GPIB_MemoryTableAdapter_EXIT; 
        }
        // check the parameter type
        else if( eParTypeChar != textBuffer[0] )
        {
            errCode = ERROR_GPIB_INVALID_PARAMETER_TYPE; 
            goto L_GPIB_MemoryTableAdapter_EXIT;
        }
        
        // retrieve the parameter
        pid = GPIB_GetPortId( &textBuffer[1], rc - 1 );
        
        if( ePortId_MAX == pid )
        {
            errCode = ERROR_GPIB_INVALID_PARAMETER; 
            goto L_GPIB_MemoryTableAdapter_EXIT; 
        }
        
        if( 2 == nParams )
        {
            // retrieve second parameter: table identifier (factory/user1/user2/user3)
            if( !queue_get( pQParameters, (BYTE*)textBuffer, sizeof(textBuffer), &rc ))
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemoryTableAdapter_EXIT; 
            }
            // check the parameter type
            else if( eParTypeChar != textBuffer[0] )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER_TYPE; 
                goto L_GPIB_MemoryTableAdapter_EXIT;
            }
            
            // retrieve the parameter
            id = GPIB_GetChrzTableId( &textBuffer[1], rc - 1 );
            
            if( eCh_MAX == id )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemoryTableAdapter_EXIT; 
            }
        }
        else
        {
            id = eChFactory;
        }
    }
        
    if( !ACMClass->methods.xCharacterization.getAdapterDesc( id, pid, (char*)pBuffer, freeSpace, &rc ) )
    {
        errCode = ERROR_GPIB_DATA_NOT_FOUND;
    }
    
L_GPIB_MemoryTableAdapter_EXIT:
  
    if( 0 != errCode )
    {
        const char * pDescription = NULL;
        
        if( eCh_MAX != id )
        {
            pDescription = aChrzDesc[ (size_t)id ];
        }
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        
        rc = 0;
    }
    
    return 0;
}

// GPIB_MemTableConnector()
// 
size_t GPIB_MemoryTableConnector( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
        
    size_t       rc = 0;    
    unsigned int errCode = 0;
            
    ePortId_t    pid;             // Port ID
    char         textBuffer[16];          
    
    // check the requiest status
    if(!request )
    {
        // error: command must be a request
        errCode = ERROR_GPIB_REQUEST_ONLY_SUPPORT; 
        goto L_GPIB_MemoryTableAdapter_EXIT;
    }
    
    // Retrieve amount of parameters from the queue
    if( !queue_getcount(pQParameters, &rc) )
    {  
        // error: can't get parameters count
        errCode = ERROR_GPIB_INTERNAL; 
        goto L_GPIB_MemoryTableAdapter_EXIT;
    }
    // Check parameters count
    else if( 1 != rc )  // this command must have only one parameter
    {
        // error: wrong parameters count ( must be 1 )
        errCode = ERROR_GPIB_WRONG_PARAMETERS_COUNT; 
        goto L_GPIB_MemoryTableAdapter_EXIT; 
    }
    
    // parameter - port identifier (A/B/C/D)
    {   
        // retrieve the first parameter: port identifier (A/B/C/D)
        if( !queue_get( pQParameters, (BYTE*)textBuffer, sizeof(textBuffer), &rc ))
        {
            errCode = ERROR_GPIB_INVALID_PARAMETER; 
            goto L_GPIB_MemoryTableAdapter_EXIT; 
        }
        // check the parameter type
        else if( eParTypeChar != textBuffer[0] )
        {
            errCode = ERROR_GPIB_INVALID_PARAMETER_TYPE; 
            goto L_GPIB_MemoryTableAdapter_EXIT;
        }
        
        // retrieve the parameter
        pid = GPIB_GetPortId( &textBuffer[1], rc - 1 );
        
        if( ePortId_MAX == pid )
        {
            errCode = ERROR_GPIB_INVALID_PARAMETER; 
            goto L_GPIB_MemoryTableAdapter_EXIT; 
        }
    }
    
    if( !ACMClass->methods.xCharacterization.getConnectorType( pid, (char*)pBuffer, freeSpace, &rc ) )
    {
        errCode = ERROR_GPIB_DATA_NOT_FOUND;
    }
    
L_GPIB_MemoryTableAdapter_EXIT:
  
    if( 0 != errCode )
    {
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, aChrzDesc[ (size_t)eChFactory ], GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, aChrzDesc[ (size_t)eChFactory ], errClass_Autodetect );                
        
        rc = 0;
    }
    
    return rc;
}

// GPIB_MemTableAnalyzer()
// Prints the analyzer model of the characterization
size_t GPIB_MemoryTableAnalyzer( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
    
    eChrz_t      id = eCh_MAX;
    unsigned int errCode = __Util_CheckParamTableId( udi, pQParameters, request, &id );
    
    size_t       rc = 0;    
    
    if( 0 == errCode )
    {
        if( !ACMClass->methods.xCharacterization.getAnalyzer( id, (char*)pBuffer, freeSpace, &rc ) )
        {
            errCode = ERROR_GPIB_DATA_NOT_FOUND;
        }
    }
      
    if( 0 != errCode )
    {
        const char * pDescription = NULL;
        
        if( eCh_MAX != id )
        {
            pDescription = aChrzDesc[ (size_t)id ];
        }
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        
        rc = 0;
    }
    
    return rc;
}

// GPIB_MemTablePlace()
// Prints the location where the characterization has been created
size_t GPIB_MemoryTablePlace( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
    
    eChrz_t      id = eCh_MAX;
    unsigned int errCode = __Util_CheckParamTableId( udi, pQParameters, request, &id );
    
    size_t       rc = 0;    
    
    if( 0 == errCode )
    {        
        if( !ACMClass->methods.xCharacterization.getPlace( id, (char*)pBuffer, freeSpace, &rc ) )
        {
            errCode = ERROR_GPIB_DATA_NOT_FOUND;
        }
    }
      
    if( 0 != errCode )
    {
        const char * pDescription = NULL;
        
        if( eCh_MAX != id )
        {
            pDescription = aChrzDesc[ (size_t)id ];
        }
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        
        rc = 0;
    }
    
    return rc;
}

// GPIB_MemTableOperator()
// Prints the operator's name who created the characterization
size_t GPIB_MemoryTableOperator( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
    
    eChrz_t      id = eCh_MAX;
    unsigned int errCode = __Util_CheckParamTableId( udi, pQParameters, request, &id );
    
    size_t       rc = 0;    
    
    if( 0 == errCode )
    {
        if( !ACMClass->methods.xCharacterization.getOperator( id, (char*)pBuffer, freeSpace, &rc ) )
        {
            errCode = ERROR_GPIB_DATA_NOT_FOUND;
        }
    }
      
    if( 0 != errCode )
    {
        const char * pDescription = NULL;
        
        if( eCh_MAX != id )
        {
            pDescription = aChrzDesc[ (size_t)id ];
        }
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        
        rc = 0;
    }
    
    return rc;
}
// ==============================================================================================
// volatile size_t debug_call = 0;
// GPIB_MemoryTableData()
// MEM:TABLE:DATA? <PORT_COMBINATION>, <PORT_STATE>, [MEMORY_SECTOR]
// Prints the contents of the characterization table depending on the following parameters:
// - port combination (A, B, or CD, or CHECK, etc)
// - port state (Load, Short, Thru11=S11, Check12=S12, etc.)
// - memory sector (Factory/USER1/USER2/USER3), OPTIONAL, by default = FACTORY
size_t GPIB_MemoryTableData( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
// 1
    char    *      pBuffer        = (char*)udi->usbtmcGpib.pData;
    size_t         freeSpace      = udi->BulkRespondStatus.RespondBufferSize;  
    size_t         rc             = 0;         // full amount of bytes returned    
    unsigned int   errCode        = 0;         // error identifier          
    
    sACMGetPoints_t tableContext;  // table context to store the data between calls    
    eChrz_t         msid          = eCh_MAX;   // characterization id,  = memory sector id
    ePortComb_t     pcid          = ePortComb_UNDEFINED; // port combination id
    ePortStateId_t  psid          = ePortStateId_UNDEFINED; // port state id        
    
    // debug_call++;
// 2       
    // Check if the function context is saved and must be continued
    if( !udi->GPIBFunctionContext.bEnable )
    {
        // no context found
        size_t nParam = 0;         // amount of parameters
        
        if( !request )
        {
            // error: request only supported
            errCode = ERROR_GPIB_REQUEST_ONLY_SUPPORT; 
            goto L_GPIB_MemoryTableData_EXIT;
        }
        
        // Retrieve amount of parameters from the queue
        if( !queue_getcount(pQParameters, &nParam) )
        {  
            // error: can't get parameters count
            errCode = ERROR_GPIB_INTERNAL; 
            goto L_GPIB_MemoryTableData_EXIT;
        }
        // Check parameters count
        else if( 2 != nParam && 3 != nParam )  // this command must have 2 or 3 parameters
        {
            // error: wrong parameters count ( must be 2 or 3 )
            errCode = ERROR_GPIB_WRONG_PARAMETERS_COUNT; 
            goto L_GPIB_MemoryTableData_EXIT; 
        }
      
        // parameter #1 - port combination (A/B/C/D/AB/AC/AD/BC/BD/CD/CHECK)
        {
            char           textBuffer[16];
            
            // retrieve the first parameter: port combination
            if( !queue_get( pQParameters, (BYTE*)textBuffer, sizeof(textBuffer), &rc ))
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemoryTableData_EXIT; 
            }
            // check the parameter type
            else if( eParTypeChar != textBuffer[0] )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER_TYPE; 
                goto L_GPIB_MemoryTableData_EXIT;
            }
            
            // retrieve the parameter
            pcid = GPIB_GetPortComb( &textBuffer[1], rc - 1 );
            
            if( ePortComb_UNDEFINED == pcid )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemoryTableData_EXIT; 
            }
        }
        
        // parameter #2 - port state (SHORT, OPEN, LOAD, LOAD2, OPEN2, S11, S12, ..., S44)
        {
            char           textBuffer[16];
            
            // retrieve the first parameter: port state
            if( !queue_get( pQParameters, (BYTE*)textBuffer, sizeof(textBuffer), &rc ))
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemoryTableData_EXIT; 
            }
            // check the parameter type
            else if( eParTypeChar != textBuffer[0] )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER_TYPE; 
                goto L_GPIB_MemoryTableData_EXIT;
            }
            
            // retrieve the parameter
            psid = GPIB_GetPortStateId( &textBuffer[1], rc - 1 );
            
            if( ePortStateId_UNDEFINED == psid )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemoryTableData_EXIT; 
            }
        }
        
        // parameter #3 - memory sector (FACTORY/USER1/USER2/USER3)
        if( 3 == nParam ) // if third parameter is not omitted
        {
            char           textBuffer[16];
            
            // retrieve the first parameter: memory sector
            if( !queue_get( pQParameters, (BYTE*)textBuffer, sizeof(textBuffer), &rc ))
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemoryTableData_EXIT; 
            }
            // check the parameter type
            else if( eParTypeChar != textBuffer[0] )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER_TYPE; 
                goto L_GPIB_MemoryTableData_EXIT;
            }
            
            // retrieve the parameter
            msid = GPIB_GetChrzTableId( &textBuffer[1], rc - 1 );
            
            if( eCh_MAX == msid )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemoryTableData_EXIT; 
            }
        }
        else
        {
            msid = eChFactory;
        }
        
        // Validate given parameters: port state and port combination
        if( ! ACMClass->methods.checkTableParams( pcid, psid ) )
        {
            errCode = ERROR_GPIB_INVALID_PARAMETER; 
            goto L_GPIB_MemoryTableData_EXIT; 
        }
        
        // initialize the table context @.out just for order
        tableContext.out.max = 0;           // don't care
        tableContext.out.min = 0;           // don't care
        tableContext.out.TableAddress = 0;  // don't care
        tableContext.out.nPoints = 0;       // don't care
        
        // initialize @.in to make the @.getPoints() call to load
        // the table header.
        // - set @.nCount to 0 to load the table header        
        tableContext.in.nCount = 0;         // must be 0
        tableContext.in.nStartPoint = 0;    // don't care
        tableContext.in.pDataArray = dbPointsBuffer;  // must be initialized to pass internal checks
        
        // load table header and validate it
        // During this call the @tableContext.out will be initialized
        if( 1 != ACMClass->methods.xCharacterization.getPoints( 
                                                       msid,  // memory sector
                                                       pcid,  // port combination
                                                       psid,  // port state 
                                                       &tableContext )
        )
        {
            errCode = ERROR_GPIB_DATA_NOT_FOUND; 
            
            // failed
            switch( tableContext.svc.errCode )
            {
                case ERR_ACMGETPOINTS_INVAL : errCode = ERROR_GPIB_INVALID_PARAMETER; break;
                case ERR_ACMGETPOINTS_INVTBL: errCode = ERROR_GPIB_ARRAY_CORRUPTED;   break;
                case ERR_ACMGETPOINTS_INVHDR: errCode = ERROR_GPIB_DATA_NOT_FOUND;    break;
                case ERR_ACMGETPOINTS_IO    : errCode = ERROR_GPIB_INTERNAL;          break;               
            }
            
            goto L_GPIB_MemoryTableData_EXIT; 
        }
        
        // ok, now @tableContext is initialized
        // Let's initialize the rest variables
        tableContext.in.nStartPoint = 0;    // starting point = 0
        tableContext.in.pDataArray = dbPointsBuffer;  // initialize the temporary buffer
        tableContext.in.nCount = 0; // zero yet, see below
    }
// 3
    else
    {
        // load previous context     
        size_t bytes; 
        
        if(    queue_get( &udi->GPIBFunctionContext.Context, (char*)&msid , sizeof(msid), &bytes )  // load memory segment id
            && queue_get( &udi->GPIBFunctionContext.Context, (char*)&pcid , sizeof(pcid), &bytes )  // load port combination id
            && queue_get( &udi->GPIBFunctionContext.Context, (char*)&psid , sizeof(psid), &bytes )  // load port state id  
            && queue_get( &udi->GPIBFunctionContext.Context, (char*)&tableContext , sizeof(tableContext), &bytes )  // load table context              
        )
        {
            // OK
          
            // Note:
            // @tableContext.out.nPoints is remembered, amount of points
            // @tableContext.in.nStartPoint is remembered, starting point (the next after the lastest processed)
        }
        else
        {
            errCode = ERROR_GPIB_INTERNAL;
            goto L_GPIB_MemoryTableData_EXIT;
        }
    }
                                                       
    rc = 0;
// 4    
    {
        size_t nPoints = tableContext.out.nPoints;              // amount of points in the table
        size_t nStart  = tableContext.in.nStartPoint;           // starting point
        
        // do until all the points are processed
        while( nStart < nPoints )
        {  
            // specify the number of points to retrieve: @tableContext.in.nCount
            // Note: @tableContext.out.nPoints and @tableContext.in.nStartPoint are
            // already specified or remembered
            size_t nStepPoints = (nPoints - nStart);
          
            // check the points number for the limit
            if( nStepPoints >= DBPOINTS_BUFFER_NUM )
            {
                tableContext.in.nCount = DBPOINTS_BUFFER_NUM;  // (nPoints - nStart)
            }
            else
            {
                // not greater than it is required
                tableContext.in.nCount = nStepPoints;
            }
            
            // retrieve the points from the memory
            if( tableContext.in.nCount != ACMClass->methods.xCharacterization.getPoints( 
                                                       msid,  // memory sector
                                                       pcid,  // port combination
                                                       psid,  // port state 
                                                       &tableContext )
            )
            {
                // error
                // Strange... The table is already validated. Maybe, it is I/O error issue.
                udi->GPIBFunctionContext.LastFunction = NULL;
                errCode = ERROR_GPIB_INTERNAL;
                
                // failed
                switch( tableContext.svc.errCode )
                {
                    case ERR_ACMGETPOINTS_INVAL : errCode = ERROR_GPIB_INVALID_PARAMETER; break;
                    case ERR_ACMGETPOINTS_INVTBL: errCode = ERROR_GPIB_ARRAY_CORRUPTED;   break;
                    case ERR_ACMGETPOINTS_INVHDR: errCode = ERROR_GPIB_DATA_NOT_FOUND;    break;
                    case ERR_ACMGETPOINTS_IO    : errCode = ERROR_GPIB_INTERNAL;          break;               
                }
                
                goto L_GPIB_MemoryTableData_EXIT;
            }
            
            // Ok, the double-points are loaded...
            // Let's convert it!
            // @iPoint - just counts amount of converted points at this step.
            for( size_t iPoint = 0; iPoint < tableContext.in.nCount; ++iPoint )
            {
                // the point format is:
                // <Magnitude>,<SPACE><Phase>,<SPACE>
                // where <Magnitude> and <Phase> is a number in scientific format
                // with 10 fractional digits. Let's leave 2 digits for the exponent,
                // and one number takes 16 characters, plus the separator and the space.
                // So one point takes (16+2)*2 = 36 characters. To be in save we will
                // suppose the one point takes 48 characters.
                // e.g.
                // 1.0000000000e+5, 1.0000000000e+0,
                if( freeSpace >= 48 )
                {
                    size_t bytes = 0;
                    
                    // @dbPointsBuffer[iPoint] is one of points retrieved at this step.
                    
                    // Convert a magnitude
                    bytes = GPIB_Double2Str( dbPointsBuffer[ iPoint ].magn, pBuffer, freeSpace, Format_Double2Str_Sci );
                    
                    if( 0 == bytes )
                    {
                        errCode = ERROR_GPIB_BUFFER_OVERFLOW; 
                        goto L_GPIB_MemoryTableData_EXIT;
                    }
                    
                    pBuffer += bytes;
                    freeSpace -= bytes;
                    rc += bytes;
                    
                    // print the delimeter
                    bytes = snprintf( pBuffer, freeSpace, "%s", ", " );
                    
                    pBuffer += bytes;
                    freeSpace -= bytes;
                    rc += bytes;
                    
                    // Convert a phase
                    bytes = GPIB_Double2Str( dbPointsBuffer[ iPoint ].phase, pBuffer, freeSpace, Format_Double2Str_Sci );
                    
                    if( 0 == bytes )
                    {
                        errCode = ERROR_GPIB_BUFFER_OVERFLOW; 
                        goto L_GPIB_MemoryTableData_EXIT;
                    }
                    
                    pBuffer += bytes;
                    freeSpace -= bytes;
                    rc += bytes;
                    
                    // print the delimeter
                    bytes = snprintf( pBuffer, freeSpace, "%s", ", " );
                    
                    pBuffer += bytes;
                    freeSpace -= bytes;
                    rc += bytes;
                    
                    // move to the next point in the table
                    nStart++;
                }
                else
                {
                    // not enough free space in the text buffer
                  
                    (void)tableContext.out.nPoints;
                    tableContext.in.nStartPoint = nStart;         // modify the starting point for the next call
                    // --------------------------------------------------------------------------
                    // Save context
                    usbtmc_create_function_context( udi, (void*)GPIB_MemoryTableData ); 
                    
                    if(   !queue_add( &udi->GPIBFunctionContext.Context, (char*)&msid , sizeof(msid) )    // Save memory segment id
                       || !queue_add( &udi->GPIBFunctionContext.Context, (char*)&pcid , sizeof(pcid) )    // Save port combination id
                       || !queue_add( &udi->GPIBFunctionContext.Context, (char*)&psid , sizeof(psid) )    // Save port state id  
                       || !queue_add( &udi->GPIBFunctionContext.Context, (char*)&tableContext , sizeof(tableContext) )    // Save table context                     
                      )
                    {
                       udi->GPIBFunctionContext.LastFunction = NULL;
                       errCode = ERROR_GPIB_INTERNAL;
                       goto L_GPIB_MemoryTableData_EXIT;
                    }
                    
                    udi->BulkRespondStatus.bIsLastTransfer = FALSE;
                    errCode = 0;
                    // --------------------------------------------------------------------------
                    
                    goto L_GPIB_MemoryTableData_EXIT;
                }
            } // for           
            
            tableContext.in.nStartPoint = nStart;         // modify the starting point
            
        } // while( nStart < nPoints )     
        
        // Ok, all the points are converted here due to
        // otherwise, the program jumps to L_GPIB_MemoryTableData_EXIT label
        // after saving context.
        if( rc > 2 )
        {
            // cut off the last space and comma
            rc -= 2;
        }
        
        udi->BulkRespondStatus.bIsLastTransfer = TRUE;
    }          
// 5    
L_GPIB_MemoryTableData_EXIT:    
  
    if( 0 != errCode )
    {
        const char * pDescription = NULL;
        
        if( eCh_MAX != msid )
        {
            pDescription = aChrzDesc[ (size_t)msid ];
        }
        
        if(  ERROR_GPIB_ARRAY_CORRUPTED == errCode
          && ePortComb_MAX != pcid )
        {
           pDescription = aPortComb[ ((pcid - ePortComb_MIN)%ePortComb_MAX) ];
        } 
        
        udi->BulkRespondStatus.bIsLastTransfer = TRUE;
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        
        rc = 0;
    }
    
    return rc;
}


// ==============================================================================================
// ==============================================================================================
// GPIB_SwitchState(...)
// Назначенная комманда - [SYSTem:]SWITch:STATe,  [SYSTem:]SWITch:STATe?
// Параметры комманды - состояние (SALL, OALL, THBD, ...) или отстутствует (запрос)
// Возвращаемое значение - строка имени состояния порта и количество байт
unsigned int GPIB_SwitchState( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
  union
  {
      char stateRaw[ 16 ];
      struct
      {
          BYTE stateNameParamType;
          char stateName[ 15 ];
      };
  } = {
      .stateRaw = {0}
  };
  
  unsigned int errCode = 0;
  size_t stateNameLength = 0;
  size_t count = 0;
  BYTE * pBuffer = NULL;
  int nFreeSpace = 0;
  size_t bytes = 0;
  eACMKeyState_t keyState;
  
  // -------------------------------------------------
  pBuffer = (BYTE*)udi->usbtmcGpib.pData;
  nFreeSpace = udi->BulkRespondStatus.RespondBufferSize;
  // -------------------------------------------------
  
  if(!queue_getcount(pQParameters, &count))
  {  
     errCode = ERROR_GPIB_INTERNAL; 
     goto L_GPIB_SwitchState_EXIT;
  }  
  
  if( TernaryOperator(request,0,1) != count ) 
  {  
     errCode = ERROR_GPIB_WRONG_PARAMETERS_COUNT;
     goto L_GPIB_SwitchState_EXIT;
  }
  
  if( !request )  // == (count==1)
  {  
      if( !queue_get( pQParameters, stateRaw, sizeof(stateRaw), &stateNameLength ) )
      {  
         errCode = ERROR_GPIB_INVALID_PARAMETER;
         goto L_GPIB_SwitchState_EXIT;
      }
      
      if( stateNameLength < 2 || eParTypeChar != stateNameParamType )
      {
         errCode = ERROR_GPIB_INVALID_PARAMETER_TYPE;
         goto L_GPIB_SwitchState_EXIT;
      }
      
      stateNameLength--;  // decrease parameter length by 1 byte - 1 byte for parameter type
      
      keyState = ACMClass->methods.getKeyStateByName( stateName, stateNameLength );
  
      if( eKeyState_Undefined == keyState )
      {
          errCode = ERROR_GPIB_INVALID_PARAMETER;
          goto L_GPIB_SwitchState_EXIT;
      }
  
      // -------------------------------------------------
  
      if( ! ACMClass->methods.checkStateAvailable( keyState ) )
      {
          errCode = ERROR_GPIB_INVALID_PARAMETER;
          goto L_GPIB_SwitchState_EXIT;
      }
      
      /*
      bytes = ACMClass->methods.getKeyStateName( keyState, pBuffer, nFreeSpace );
      
      if( (0 == bytes) && ( bytes > nFreeSpace ) )
      {
          err = ERROR_GPIB_INTERNAL; 
          goto L_GPIB_SwitchState_EXIT;
      }
      */
      
      if( ! ACMClass->methods.setKeyStateCommon( keyState ) )
      {
          errCode = ERROR_GPIB_INVALID_PARAMETER; 
          goto L_GPIB_SwitchState_EXIT; 
      }
  }
  else
  {
      keyState = ACMClass->methods.getKeyStateCommon();
    
      bytes = ACMClass->methods.getKeyStateName( keyState, pBuffer, nFreeSpace );
      
      if( (0 == bytes) && ( bytes > nFreeSpace ) )
      {
          errCode = ERROR_GPIB_INTERNAL; 
          goto L_GPIB_SwitchState_EXIT;
      }
  }
  // -------------------------------------------------
  
L_GPIB_SwitchState_EXIT:
  
  if( 0 != errCode )
  {
      // 27/08/18
      // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
      GPIB_RaiseStandartError( udi, errCode, NULL, errClass_Autodetect );
        
      bytes = 0;
  }
    
  return bytes;
}


void GPIB_DevDepInit( USB_DEVICE_INFO * udi )
{
  //InitKeyState();     
  
  //SETKEY_LOADB(udi->CommandStatus.key_stat); 
  //SETKEY_LOADA(udi->CommandStatus.key_stat);
  //usbapp_SET_KEY_STAT(&udi->CommandStatus);
  
  /* ACMClass is uninitialized yet! */  
}

// GPIB_MemTableTherCorrFreqStart()
// Prints the start frequency of the thermocompensation data array
size_t GPIB_MemTableTherCorrFreqStart( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
    size_t       rc = 0;       
    double       startFreq = 0.0f;
    unsigned int errCode = 0;
    size_t       count = 0;

    if( !request )
    {        
        errCode = ERROR_GPIB_REQUEST_ONLY_SUPPORT;         
    }
    else
    {  
        if( !queue_getcount( pQParameters, &count) )
        {  
            errCode = ERROR_GPIB_INTERNAL;        
        }
        else
        {  
            if( 0 != count )
            {
                errCode = ERROR_GPIB_WRONG_PARAMETERS_COUNT; 
            }
            else
            {  
                if( !ACMClass->methods.xThermo.getStartFreq( &startFreq ) )
                {    
                    errCode = ERROR_GPIB_THERMDATA_NOT_FOUND; 
                }
                else
                {            
                    rc = GPIB_Double2Str( startFreq, (char*)pBuffer, freeSpace, Format_Double2Str_Sci );
                }
            }
        }
    }
    
      
    if( 0 != errCode )
    {
        const char * pDescription = pcThermoDesc;

        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        
        rc = 0;
    }
    
    return rc;
}

// GPIB_MemTableTherCorrFreqStop()
// Prints the stop frequency of the thermocompensation data array
size_t GPIB_MemTableTherCorrFreqStop( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
    size_t       rc = 0;    
    double       stopFreq = 0.0f;
    unsigned int errCode = 0;
    size_t       count = 0;
    
    if( !request )
    {        
        errCode = ERROR_GPIB_REQUEST_ONLY_SUPPORT;         
    }
    else
    {
        if( !queue_getcount( pQParameters, &count) )
        {  
            errCode = ERROR_GPIB_INTERNAL;        
        }
        else
        {  
            if( 0 != count )
            {
                errCode = ERROR_GPIB_WRONG_PARAMETERS_COUNT; 
            }
            else
            {  
                if( !ACMClass->methods.xThermo.getStopFreq( &stopFreq ) )
                {    
                    errCode = ERROR_GPIB_THERMDATA_NOT_FOUND; 
                }
                else
                {            
                    rc = GPIB_Double2Str( stopFreq, (char*)pBuffer, freeSpace, Format_Double2Str_Sci );
                }
            }
        }
    }    
      
    if( 0 != errCode )
    {
        const char * pDescription = pcThermoDesc;

        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        
        rc = 0;
    }
    
    return rc;
}

size_t GPIB_MemTableTherCorrPoints( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
    uint8_t *    pBuffer   = (uint8_t*)udi->usbtmcGpib.pData;
    size_t       freeSpace = udi->BulkRespondStatus.RespondBufferSize;
    size_t       rc = 0;    
    size_t       nPoints = 0;
    unsigned int errCode = 0;
    size_t       count   = 0;
    
    if( !request )
    {        
        errCode = ERROR_GPIB_REQUEST_ONLY_SUPPORT;         
    }
    else
    {  
        if( !queue_getcount( pQParameters, &count) )
        {  
           errCode = ERROR_GPIB_INTERNAL;        
        } 
        else
        {
            if( 0 != count )
            {
                errCode = ERROR_GPIB_WRONG_PARAMETERS_COUNT; 
            }
            else
            {
                nPoints = ACMClass->methods.xThermo.getPointsCount();
                
                if( 0 == nPoints )
                {    
                    errCode = ERROR_GPIB_THERMDATA_NOT_FOUND; 
                }
                else
                {            
                    rc = snprintf( (char*)pBuffer, freeSpace, "%d", nPoints );
                } 
            }
        }
    }  
    if( 0 != errCode )
    {
        const char * pDescription = pcThermoDesc;

        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        rc = 0;
    }
    
    return rc;
}


// GPIB_MemTableTherCorrMagniture()
// MEM:TABLE:THER:CORR:MAGN? <PORT_COMBINATION>, <PORT_STATE>
// Prints the contents of the characterization table depending on the following parameters:
// - port combination (A, B, or CD, or CHECK, etc)
// - port state (Load, Short, Thru11=S11, Check12=S12, etc.)
size_t GPIB_MemTableTherCorrMagniture( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
// 1
    char    *      pBuffer        = (char*)udi->usbtmcGpib.pData;
    size_t         freeSpace      = udi->BulkRespondStatus.RespondBufferSize;  
    size_t         rc             = 0;         // full amount of bytes returned    
    unsigned int   errCode        = 0;         // error identifier          
    
    sACMGetPoints_t tableContext;  // table context to store the data between calls        
    ePortComb_t     pcid          = ePortComb_UNDEFINED; // port combination id
    ePortStateId_t  psid          = ePortStateId_UNDEFINED; // port state id    
// 2       
    // Check if the function context is saved and must be continued
    if( !udi->GPIBFunctionContext.bEnable )
    {
        // no context found
        size_t nParam = 0;         // amount of parameters
        
        if( !request )
        {        
            errCode = ERROR_GPIB_REQUEST_ONLY_SUPPORT;
            goto L_GPIB_MemTableTherCorrMagniture_EXIT;
        }
    
        // Retrieve amount of parameters from the queue
        if( !queue_getcount(pQParameters, &nParam) )
        {  
            // error: can't get parameters count
            errCode = ERROR_GPIB_INTERNAL; 
            goto L_GPIB_MemTableTherCorrMagniture_EXIT;
        }
        // Check parameters count
        else if( 2 != nParam  )  // this command must have 2 parameters
        {
            // error: wrong parameters count ( must be 2 )
            errCode = ERROR_GPIB_WRONG_PARAMETERS_COUNT; 
            goto L_GPIB_MemTableTherCorrMagniture_EXIT; 
        }
      
        // parameter #1 - port combination (A/B/C/D/AB/AC/AD/BC/BD/CD/CHECK)
        {
            char           textBuffer[16];
            
            // retrieve the first parameter: port combination
            if( !queue_get( pQParameters, (BYTE*)textBuffer, sizeof(textBuffer), &rc ))
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemTableTherCorrMagniture_EXIT; 
            }
            // check the parameter type
            else if( eParTypeChar != textBuffer[0] )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER_TYPE; 
                goto L_GPIB_MemTableTherCorrMagniture_EXIT;
            }
            
            // retrieve the parameter
            pcid = GPIB_GetPortComb( &textBuffer[1], rc - 1 );
            
            if( ePortComb_UNDEFINED == pcid )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemTableTherCorrMagniture_EXIT; 
            }
        }
        
        // parameter #2 - port state (SHORT, OPEN, LOAD, LOAD2, OPEN2, S11, S12, ..., S44)
        {
            char           textBuffer[16];
            
            // retrieve the first parameter: port state
            if( !queue_get( pQParameters, (BYTE*)textBuffer, sizeof(textBuffer), &rc ))
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemTableTherCorrMagniture_EXIT; 
            }
            // check the parameter type
            else if( eParTypeChar != textBuffer[0] )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER_TYPE; 
                goto L_GPIB_MemTableTherCorrMagniture_EXIT;
            }
            
            // retrieve the parameter
            psid = GPIB_GetPortStateId( &textBuffer[1], rc - 1 );
            
            if( ePortStateId_UNDEFINED == psid )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemTableTherCorrMagniture_EXIT; 
            }
        }
        
        // Validate given parameters: port state and port combination
        if( ! ACMClass->methods.checkTableParams( pcid, psid ) )
        {
            errCode = ERROR_GPIB_INVALID_PARAMETER; 
            goto L_GPIB_MemTableTherCorrMagniture_EXIT; 
        }
        
        // initialize the table context @.out just for order
        tableContext.out.max = 0;           // don't care
        tableContext.out.min = 0;           // don't care
        tableContext.out.TableAddress = 0;  // don't care
        tableContext.out.nPoints = 0;       // don't care
        
        // initialize @.in to make the @.getPoints() call to load
        // the table header.
        // - set @.nCount to 0 to load the table header        
        tableContext.in.nCount = 0;         // must be 0
        tableContext.in.nStartPoint = 0;    // don't care
        tableContext.in.pDataArray = dbPointsBuffer;  // must be initialized to pass internal checks
        
        // load table header and validate it
        // During this call the @tableContext.out will be initialized
        if( 1 != ACMClass->methods.xThermo.getPointsMagn(  pcid,  // port combination
                                                       psid,  // port state 
                                                       &tableContext )
        )
        {
            // failed
            errCode = ERROR_GPIB_THERMDATA_NOT_FOUND; 
            
            switch( tableContext.svc.errCode )
            {
                case ERR_ACMGETPOINTS_INVAL : errCode = ERROR_GPIB_INVALID_PARAMETER;   break;
                case ERR_ACMGETPOINTS_INVTBL: errCode = ERROR_GPIB_ARRAY_CORRUPTED;     break;
                case ERR_ACMGETPOINTS_INVHDR: errCode = ERROR_GPIB_THERMDATA_NOT_FOUND; break;
                case ERR_ACMGETPOINTS_IO    : errCode = ERROR_GPIB_INTERNAL;            break;               
            }
            
            goto L_GPIB_MemTableTherCorrMagniture_EXIT; 
        }
        
        // ok, now @tableContext is initialized
        // Let's initialize the rest variables
        tableContext.in.nStartPoint = 0;    // starting point = 0
        tableContext.in.pDataArray = dbPointsBuffer;  // initialize the temporary buffer
        tableContext.in.nCount = 0; // zero yet, see below
    }
// 3
    else
    {
        // load previous context     
        size_t bytes; 
        
        if(    queue_get( &udi->GPIBFunctionContext.Context, (char*)&pcid , sizeof(pcid), &bytes )  // load port combination id
            && queue_get( &udi->GPIBFunctionContext.Context, (char*)&psid , sizeof(psid), &bytes )  // load port state id  
            && queue_get( &udi->GPIBFunctionContext.Context, (char*)&tableContext , sizeof(tableContext), &bytes )  // load table context              
        )
        {
            // OK
          
            // Note:
            // @tableContext.out.nPoints is remembered, amount of points
            // @tableContext.in.nStartPoint is remembered, starting point (the next after the lastest processed)
        }
        else
        {
            errCode = ERROR_GPIB_INTERNAL;
            goto L_GPIB_MemTableTherCorrMagniture_EXIT;
        }
    }
                                                       
    rc = 0;
// 4    
    {
        size_t nPoints = tableContext.out.nPoints;              // amount of points in the table
        size_t nStart  = tableContext.in.nStartPoint;           // starting point
        
        // do until all the points are processed
        while( nStart < nPoints )
        {  
            // specify the number of points to retrieve: @tableContext.in.nCount
            // Note: @tableContext.out.nPoints and @tableContext.in.nStartPoint are
            // already specified or remembered
            size_t nStepPoints = (nPoints - nStart);
          
            // check the points number for the limit
            if( nStepPoints >= DBPOINTS_BUFFER_NUM )
            {
                tableContext.in.nCount = DBPOINTS_BUFFER_NUM;  // (nPoints - nStart)
            }
            else
            {
                // not greater than it is required
                tableContext.in.nCount = nStepPoints;
            }
            
            // retrieve the points from the memory
            if( tableContext.in.nCount != ACMClass->methods.xThermo.getPointsMagn(                                                        
                                                       pcid,  // port combination
                                                       psid,  // port state 
                                                       &tableContext )
            )
            {
                // error
                // Strange... The table is already validated. Maybe, it is I/O error issue.
                udi->GPIBFunctionContext.LastFunction = NULL;
                errCode = ERROR_GPIB_INTERNAL;
                
                switch( tableContext.svc.errCode )
                {
                    case ERR_ACMGETPOINTS_INVAL : errCode = ERROR_GPIB_INVALID_PARAMETER;   break;
                    case ERR_ACMGETPOINTS_INVTBL: errCode = ERROR_GPIB_ARRAY_CORRUPTED;     break;
                    case ERR_ACMGETPOINTS_INVHDR: errCode = ERROR_GPIB_THERMDATA_NOT_FOUND; break;
                    case ERR_ACMGETPOINTS_IO    : errCode = ERROR_GPIB_INTERNAL;            break;               
                }
                
                goto L_GPIB_MemTableTherCorrMagniture_EXIT;
            }
            
            // Ok, the double-points are loaded...
            // Let's convert it!
            // @iPoint - just counts amount of converted points at this step.
            for( size_t iPoint = 0; iPoint < tableContext.in.nCount; ++iPoint )
            {
                // the point format is:
                // <Number>,<SPACE>
                // where <Number> is a number in scientific format
                // with 10 fractional digits. Let's leave 2 digits for the exponent,
                // and one number takes 16 characters, plus the separator and the space.
                // So one point takes (16+2) = 18 characters. To be in save we will
                // suppose the one point takes 24 characters.
                // e.g.
                // 1.0000000000e+5, 
                if( freeSpace >= 24 )
                {
                    size_t bytes = 0;
                    
                    // @dbPointsBuffer[iPoint] is one of points retrieved at this step.
                    
                    // Convert a magnitude
                    bytes = GPIB_Double2Str( dbPointsBuffer[ iPoint ].magn, pBuffer, freeSpace, Format_Double2Str_Sci );
                    
                    if( 0 == bytes )
                    {
                        errCode = ERROR_GPIB_BUFFER_OVERFLOW; 
                        goto L_GPIB_MemTableTherCorrMagniture_EXIT;
                    }
                    
                    pBuffer += bytes;
                    freeSpace -= bytes;
                    rc += bytes;
                    
                    // print the delimeter
                    bytes = snprintf( pBuffer, freeSpace, "%s", ", " );
                    
                    pBuffer += bytes;
                    freeSpace -= bytes;
                    rc += bytes;
                    
                    // move to the next point in the table
                    nStart++;
                }
                else
                {
                    // not enough free space in the text buffer
                  
                    (void)tableContext.out.nPoints;
                    tableContext.in.nStartPoint = nStart;         // modify the starting point for the next call
                    // --------------------------------------------------------------------------
                    // Save context
                    usbtmc_create_function_context( udi, (void*)GPIB_MemTableTherCorrMagniture ); 
                    
                    if(   !queue_add( &udi->GPIBFunctionContext.Context, (char*)&pcid , sizeof(pcid) )    // Save port combination id
                       || !queue_add( &udi->GPIBFunctionContext.Context, (char*)&psid , sizeof(psid) )    // Save port state id  
                       || !queue_add( &udi->GPIBFunctionContext.Context, (char*)&tableContext , sizeof(tableContext) )    // Save table context                     
                      )
                    {
                       udi->GPIBFunctionContext.LastFunction = NULL;
                       errCode = ERROR_GPIB_INTERNAL;
                       goto L_GPIB_MemTableTherCorrMagniture_EXIT;
                    }
                    
                    udi->BulkRespondStatus.bIsLastTransfer = FALSE;
                    errCode = 0;
                    // --------------------------------------------------------------------------
                    
                    goto L_GPIB_MemTableTherCorrMagniture_EXIT;
                }
            } // for
            
            tableContext.in.nStartPoint = nStart;         // modify the starting point
            
        } // while( nStart < nPoints )     
        
        // Ok, all the points are converted here due to
        // otherwise, the program jumps to L_GPIB_MemTableTherCorrMagniture_EXIT label
        // after saving context.
        if( rc > 2 )
        {
            // cut off the last space and comma
            rc -= 2;
        }
    }          
// 5    
L_GPIB_MemTableTherCorrMagniture_EXIT:    
  
    if( 0 != errCode )
    {
        const char * pDescription = pcThermoDesc;
        
        if(  ERROR_GPIB_ARRAY_CORRUPTED == errCode
          && ePortComb_MAX != pcid )
        {
           pDescription = aPortComb[ ((pcid - ePortComb_MIN)%ePortComb_MAX) ];
        } 
        
        udi->BulkRespondStatus.bIsLastTransfer = TRUE;
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        
        rc = 0;
    }
    
    return rc;
}


// GPIB_MemTableTherCorrPhase()
// MEM:TABLE:THER:CORR:PHAS? <PORT_COMBINATION>, <PORT_STATE>
// Prints the contents of the characterization table depending on the following parameters:
// - port combination (A, B, or CD, or CHECK, etc)
// - port state (Load, Short, Thru11=S11, Check12=S12, etc.)
size_t GPIB_MemTableTherCorrPhase( USB_DEVICE_INFO * udi, QUEUE * pQParameters, BOOL request )
{
// 1
    char    *      pBuffer        = (char*)udi->usbtmcGpib.pData;
    size_t         freeSpace      = udi->BulkRespondStatus.RespondBufferSize;  
    size_t         rc             = 0;         // full amount of bytes returned    
    unsigned int   errCode        = 0;         // error identifier          
    
    sACMGetPoints_t tableContext;  // table context to store the data between calls        
    ePortComb_t     pcid          = ePortComb_UNDEFINED; // port combination id
    ePortStateId_t  psid          = ePortStateId_UNDEFINED; // port state id    
// 2       
    // Check if the function context is saved and must be continued
    if( !udi->GPIBFunctionContext.bEnable )
    {
        // no context found
        size_t nParam = 0;         // amount of parameters
        
        if( !request )
        {        
            errCode = ERROR_GPIB_REQUEST_ONLY_SUPPORT;
            goto L_GPIB_MemTableTherCorrPhase_EXIT;
        }
        
        // Retrieve amount of parameters from the queue
        if( !queue_getcount(pQParameters, &nParam) )
        {  
            // error: can't get parameters count
            errCode = ERROR_GPIB_INTERNAL; 
            goto L_GPIB_MemTableTherCorrPhase_EXIT;
        }
        // Check parameters count
        else if( 2 != nParam  )  // this command must have 2 parameters
        {
            // error: wrong parameters count ( must be 2 )
            errCode = ERROR_GPIB_WRONG_PARAMETERS_COUNT; 
            goto L_GPIB_MemTableTherCorrPhase_EXIT; 
        }
      
        // parameter #1 - port combination (A/B/C/D/AB/AC/AD/BC/BD/CD/CHECK)
        {
            char           textBuffer[16];
            
            // retrieve the first parameter: port combination
            if( !queue_get( pQParameters, (BYTE*)textBuffer, sizeof(textBuffer), &rc ))
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemTableTherCorrPhase_EXIT; 
            }
            // check the parameter type
            else if( eParTypeChar != textBuffer[0] )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER_TYPE; 
                goto L_GPIB_MemTableTherCorrPhase_EXIT;
            }
            
            // retrieve the parameter
            pcid = GPIB_GetPortComb( &textBuffer[1], rc - 1 );
            
            if( ePortComb_UNDEFINED == pcid )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemTableTherCorrPhase_EXIT; 
            }
        }
        
        // parameter #2 - port state (SHORT, OPEN, LOAD, LOAD2, OPEN2, S11, S12, ..., S44)
        {
            char           textBuffer[16];
            
            // retrieve the first parameter: port state
            if( !queue_get( pQParameters, (BYTE*)textBuffer, sizeof(textBuffer), &rc ))
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemTableTherCorrPhase_EXIT; 
            }
            // check the parameter type
            else if( eParTypeChar != textBuffer[0] )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER_TYPE; 
                goto L_GPIB_MemTableTherCorrPhase_EXIT;
            }
            
            // retrieve the parameter
            psid = GPIB_GetPortStateId( &textBuffer[1], rc - 1 );
            
            if( ePortStateId_UNDEFINED == psid )
            {
                errCode = ERROR_GPIB_INVALID_PARAMETER; 
                goto L_GPIB_MemTableTherCorrPhase_EXIT; 
            }
        }
        
        // Validate given parameters: port state and port combination
        if( ! ACMClass->methods.checkTableParams( pcid, psid ) )
        {
            errCode = ERROR_GPIB_INVALID_PARAMETER; 
            goto L_GPIB_MemTableTherCorrPhase_EXIT; 
        }
        
        // initialize the table context @.out just for order
        tableContext.out.max = 0;           // don't care
        tableContext.out.min = 0;           // don't care
        tableContext.out.TableAddress = 0;  // don't care
        tableContext.out.nPoints = 0;       // don't care
        
        // initialize @.in to make the @.getPoints() call to load
        // the table header.
        // - set @.nCount to 0 to load the table header        
        tableContext.in.nCount = 0;         // must be 0
        tableContext.in.nStartPoint = 0;    // don't care
        tableContext.in.pDataArray = dbPointsBuffer;  // must be initialized to pass internal checks
        
        // load table header and validate it
        // During this call the @tableContext.out will be initialized
        if( 1 != ACMClass->methods.xThermo.getPointsPhase(  pcid,  // port combination
                                                       psid,  // port state 
                                                       &tableContext )
        )
        {
            // failed
            errCode = ERROR_GPIB_THERMDATA_NOT_FOUND; 
            
            switch( tableContext.svc.errCode )
            {
                case ERR_ACMGETPOINTS_INVAL : errCode = ERROR_GPIB_INVALID_PARAMETER;   break;
                case ERR_ACMGETPOINTS_INVTBL: errCode = ERROR_GPIB_ARRAY_CORRUPTED;     break;
                case ERR_ACMGETPOINTS_INVHDR: errCode = ERROR_GPIB_THERMDATA_NOT_FOUND; break;
                case ERR_ACMGETPOINTS_IO    : errCode = ERROR_GPIB_INTERNAL;            break;               
            }
            
            goto L_GPIB_MemTableTherCorrPhase_EXIT; 
        }
        
        // ok, now @tableContext is initialized
        // Let's initialize the rest variables
        tableContext.in.nStartPoint = 0;    // starting point = 0
        tableContext.in.pDataArray = dbPointsBuffer;  // initialize the temporary buffer
        tableContext.in.nCount = 0; // zero yet, see below
    }
// 3
    else
    {
        // load previous context     
        size_t bytes; 
        
        if(    queue_get( &udi->GPIBFunctionContext.Context, (char*)&pcid , sizeof(pcid), &bytes )  // load port combination id
            && queue_get( &udi->GPIBFunctionContext.Context, (char*)&psid , sizeof(psid), &bytes )  // load port state id  
            && queue_get( &udi->GPIBFunctionContext.Context, (char*)&tableContext , sizeof(tableContext), &bytes )  // load table context              
        )
        {
            // OK
          
            // Note:
            // @tableContext.out.nPoints is remembered, amount of points
            // @tableContext.in.nStartPoint is remembered, starting point (the next after the lastest processed)
        }
        else
        {
            errCode = ERROR_GPIB_INTERNAL;
            goto L_GPIB_MemTableTherCorrPhase_EXIT;
        }
    }
                                                       
    rc = 0;
// 4    
    {
        size_t nPoints = tableContext.out.nPoints;              // amount of points in the table
        size_t nStart  = tableContext.in.nStartPoint;           // starting point
        
        // do until all the points are processed
        while( nStart < nPoints )
        {  
            // specify the number of points to retrieve: @tableContext.in.nCount
            // Note: @tableContext.out.nPoints and @tableContext.in.nStartPoint are
            // already specified or remembered
            size_t nStepPoints = (nPoints - nStart);
          
            // check the points number for the limit
            if( nStepPoints >= DBPOINTS_BUFFER_NUM )
            {
                tableContext.in.nCount = DBPOINTS_BUFFER_NUM;  // (nPoints - nStart)
            }
            else
            {
                // not greater than it is required
                tableContext.in.nCount = nStepPoints;
            }
            
            // retrieve the points from the memory
            if( tableContext.in.nCount != ACMClass->methods.xThermo.getPointsPhase(                                                        
                                                       pcid,  // port combination
                                                       psid,  // port state 
                                                       &tableContext )
            )
            {
                // error
                // Strange... The table is already validated. Maybe, it is I/O error issue.
                udi->GPIBFunctionContext.LastFunction = NULL;
                errCode = ERROR_GPIB_INTERNAL;
                
                switch( tableContext.svc.errCode )
                {
                    case ERR_ACMGETPOINTS_INVAL : errCode = ERROR_GPIB_INVALID_PARAMETER;   break;
                    case ERR_ACMGETPOINTS_INVTBL: errCode = ERROR_GPIB_ARRAY_CORRUPTED;     break;
                    case ERR_ACMGETPOINTS_INVHDR: errCode = ERROR_GPIB_THERMDATA_NOT_FOUND; break;
                    case ERR_ACMGETPOINTS_IO    : errCode = ERROR_GPIB_INTERNAL;            break;               
                }
                
                goto L_GPIB_MemTableTherCorrPhase_EXIT;
            }
            
            // Ok, the double-points are loaded...
            // Let's convert it!
            // @iPoint - just counts amount of converted points at this step.
            for( size_t iPoint = 0; iPoint < tableContext.in.nCount; ++iPoint )
            {
                // the point format is:
                // <Number>,<SPACE>
                // where <Number> is a number in scientific format
                // with 10 fractional digits. Let's leave 2 digits for the exponent,
                // and one number takes 16 characters, plus the separator and the space.
                // So one point takes (16+2) = 18 characters. To be in save we will
                // suppose the one point takes 24 characters.
                // e.g.
                // 1.0000000000e+5, 
                if( freeSpace >= 24 )
                {
                    size_t bytes = 0;
                    
                    // @dbPointsBuffer[iPoint] is one of points retrieved at this step.
                    
                    // Convert a phase
                    bytes = GPIB_Double2Str( dbPointsBuffer[ iPoint ].phase, pBuffer, freeSpace, Format_Double2Str_Sci );
                    
                    if( 0 == bytes )
                    {
                        errCode = ERROR_GPIB_BUFFER_OVERFLOW; 
                        goto L_GPIB_MemTableTherCorrPhase_EXIT;
                    }
                    
                    pBuffer += bytes;
                    freeSpace -= bytes;
                    rc += bytes;
                    
                    // print the delimeter
                    bytes = snprintf( pBuffer, freeSpace, "%s", ", " );
                    
                    pBuffer += bytes;
                    freeSpace -= bytes;
                    rc += bytes;
                    
                    // move to the next point in the table
                    nStart++;
                }
                else
                {
                    // not enough free space in the text buffer
                  
                    (void)tableContext.out.nPoints;
                    tableContext.in.nStartPoint = nStart;         // modify the starting point for the next call
                    // --------------------------------------------------------------------------
                    // Save context
                    usbtmc_create_function_context( udi, (void*)GPIB_MemTableTherCorrPhase ); 
                    
                    if(   !queue_add( &udi->GPIBFunctionContext.Context, (char*)&pcid , sizeof(pcid) )    // Save port combination id
                       || !queue_add( &udi->GPIBFunctionContext.Context, (char*)&psid , sizeof(psid) )    // Save port state id  
                       || !queue_add( &udi->GPIBFunctionContext.Context, (char*)&tableContext , sizeof(tableContext) )    // Save table context                     
                      )
                    {
                       udi->GPIBFunctionContext.LastFunction = NULL;
                       errCode = ERROR_GPIB_INTERNAL;
                       goto L_GPIB_MemTableTherCorrPhase_EXIT;
                    }
                    
                    udi->BulkRespondStatus.bIsLastTransfer = FALSE;
                    errCode = 0;
                    // --------------------------------------------------------------------------
                    
                    goto L_GPIB_MemTableTherCorrPhase_EXIT;
                }
            } // for           
            
            tableContext.in.nStartPoint = nStart;         // modify the starting point
            
        } // while( nStart < nPoints )     
        
        // Ok, all the points are converted here due to
        // otherwise, the program jumps to L_GPIB_MemTableTherCorrMagniture_EXIT label
        // after saving context.
        if( rc > 2 )
        {
            // cut off the last space and comma
            rc -= 2;
        }
    }          
// 5    
L_GPIB_MemTableTherCorrPhase_EXIT:    
  
    if( 0 != errCode )
    {
        const char * pDescription = pcThermoDesc;
         
        if(  ERROR_GPIB_ARRAY_CORRUPTED == errCode
          && ePortComb_MAX != pcid )
        {
           pDescription = aPortComb[ ((pcid - ePortComb_MIN)%ePortComb_MAX) ];
        }        
        
        udi->BulkRespondStatus.bIsLastTransfer = TRUE;
        
        // 27/08/18
        // GPIB_RaiseStandartError( udi, errCode, pDescription, GPIB_ERROR_ID_COR | request );
        GPIB_RaiseStandartError( udi, errCode, pDescription, errClass_Autodetect );
        
        rc = 0;
    }
    
    return rc;
}

#endif



    
