Интернет-магазин

Просмотр корзины
В корзине:

товаров - 0 шт.



§ 13. Ethernet модуль Jerome. Общие принципы программирования

Дмитрий Иванов, 3 Июня 2011

Файлы к статье скачать
Имя: KA013.zip (ZIP архив)
Размер: 12 КБ

Команды управления

Помимо Web-интерфейса, модуль Jerome поддерживает командный интерфейс управления по TCP/IP с использованием текстовых команд управления (Ke-команды). Данная статья рассматривает конкретный пример взаимодейтсвия с модулем через командный интерфейс на языке программирования С++. Рассматриваются общие принципы и особенности практической реализации.

Итак, модуль Jerome имеет специальный интерфейс для управления по TCP/IP протоколу в виде текстовых команд управления (Ke-команды). Для того чтобы отправить команду модулю, необходимо открыть сетевое соединение по порту 2424. Подробности настройки соединения можно посмотреть в Техническом описании модуля. Описание поддерживаемых команд управления доступно в соответствующем документе.

Благодаря тому что команды управления текстовые, имеется возможность управлять модулем практически из любого терминала, поддерживающего подключение по TCP/IP. В качестве примера подобной широко распространенной программы можно привести программу HyperTerminal.

Управление модулем Jerome через HyperTerminal


Однако гораздо больший интерес представляет возможность разработки собственного софта на базе открытого командного интерфейса. В качестве примера рассмотрим код простой консольной программы, написанной на языке C++ в среде разработки Microsoft Visual Studio 6.0, которая открывает соединение с модулем с использованием так называемых сокетов, вводит пароль доступа (команда $KE,PSW,SET), запрашивает переодическую выдачу сводной информации по ресурсам модуля (команда $KE,DAT,ON), считывает результаты, проводит их декодирование, выделяет текущую метку времени модуля и выводит ее на экран. Готовый проект можно скачать одним архивом (см. "шапку" статьи).

#include <windows.h>
#include <process.h>
#include <conio.h>
#include "stdio.h"
#include "KeTCP_Client.h"

DWORD WINAPI ThreadRoutine( LPVOID pParams );
void ParseData( char* pBuf );

#define MODULE_PASSWORD "Jerome"
unsigned char bPasswordOk = false;

//****************************************************************
int main()
{
  KeTCP_Client KeTcp;
  KeTcp.SetConnectionParams( "192.168.0.101", 2424 );
   
  if( !KeTcp.Reconnect() )
  {
    printf( "ERROR: Can't conect to remote host!\r\n" );
    return 0;
  }
  else
  {
    printf( "Connected OK!\r\n" );
  }

  HANDLE hThread;
  DWORD dwThreadId;

  // Starting new thread for reading data from module
  hThread = CreateThread( NULL, 0, ThreadRoutine, (void*)(&KeTcp), 0, &dwThreadId );
  if( hThread == NULL )
  {
    printf( "ERROR: Can't create thread!\r\n" );
    return 0;
  }

  char buf[64];
  unsigned char len = 0;
    
  // Check the connection
  len = sprintf( buf, "$KE\r\n$KE\r\n" );  
  KeTcp.Send( buf, len );  

  // Entering password
  len = sprintf( buf, "$KE,PSW,SET,%s\r\n", MODULE_PASSWORD );  
  KeTcp.Send( buf, len );  

  // Waiting for 1 sec for password readiness
  Sleep( 1000 );

  if( bPasswordOk != true )
  {
    printf( "ERROR: Bad Password\r\n" );
    return 0;
  }
 
  // Start raw data outputting
  len = sprintf( buf, "$KE,DAT,ON\r\n");  
  KeTcp.Send( buf, len );  

  printf( "Press any key to exit...\r\n" ); 
  getch();
  
  KeTcp.Disconnect(); 
  return 0;
}

//****************************************************************
DWORD WINAPI ThreadRoutine( LPVOID pParams )
{
  KeTCP_Client* pKeTcp = (KeTCP_Client*)pParams;
  unsigned int i = 0;
  unsigned int uiSysTime = 0;

  // Data for storing replays from TCP
  #define BUF_SIZE 128
  char Buffer[BUF_SIZE];
  unsigned char offset = 0;
  char cLastChar;
  char ch;

  // Open file for saving output
  FILE* fl = fopen("KeOutput.txt", "wb");
  if( !fl )
  {
    printf( "ERROR: Can't create FILE!\r\n" );
    return 0;
  }

  char buf[256];
  while( pKeTcp->IsConnected() )
  {
    unsigned int len = pKeTcp->Recv( buf, 256 );
    // Save all the output from module into file
    if( len > 0 ) fwrite( buf, len, 1, fl );
    
    // Parsing....
    for( i = 0; i < len; i++ )
    {
      // protect from memory overflow
      if( offset >= BUF_SIZE ) offset = 0;	      
          
      ch = buf[i]; 
      Buffer[offset++] = ch;
  
      if( (ch == 0x0A) && (cLastChar == 0x0D) )
      {
        // End of replay has been found			  
        ParseData( Buffer ); 
        offset = 0;               
      }  
      // Save last char
      cLastChar = ch;
    }
  }

  fclose( fl );  
  return 0;
}

//****************************************************************
void ParseData( char* pBuf )
{
  if( !pBuf || *pBuf != '#' ) return;  
  pBuf++;

  // PSW command group
  if( memcmp(pBuf, "PSW,", 4) == 0 )  
  {
    pBuf += 4;
    if( memcmp(pBuf, "SET,OK", 6) == 0 )  
    {
      // Filter: #PSW,SET,OK
      // Global variable
      bPasswordOk = true; 
      printf( "Password OK!\r\n" );
    }
    else if( memcmp(pBuf, "SET,BAD", 7) == 0 )  
    {
      // Filter: #PSW,SET,BAD
      printf( "WARNING: Wrong Password!\r\n" );
    }	  
  }
  else if( memcmp(pBuf, "ERR", 3) == 0 )  
  {
    // Filter: #ERR
    printf( "WARNING: Syntax error!\r\n" );
  }
  else if( memcmp(pBuf, "TIME,", 5) == 0 )  
  {
    // Filter: #TIME,
    pBuf += 5;
    unsigned int uiSysTime = atoi( pBuf ); 
    printf( "System Time: %d sec\r\n", uiSysTime );
  }
}
//****************************************************************
//****************************************************************

Как же все это работает? В начале работы создаем объект класса KeTCP_Client - KeTcp. Этот класс содержит в себе кучу рутинных операций при работе с сокетами в каечтве TCP клиента. Желающие имеют возможность разобраться с его кодом. Далее производится установка связи с модулем. Указываются реквизиты для подключения - IP адрес модуля (по умолчанию 192.168.0.101) и TCP порт (2424).

Для обработки ответов модуля запускается отдельный программный поток, в который в качестве параметра передается адрес объекта KeTcp.

Возвращаемся обратно в главный поток - функцию main(). Здесь по сетевому соединению отправляются тестовые команды проверки - $KE (рекомендуется отправлять их для инициализации приемных буферов модуля). Далее вводится пароль доступа , поскольку остальные команды не будут обрабатываться модулем пока не будет введен верный пароль (защита от несанкционированного доступа). Спустя некоторое время производится проверка глобальной переменной bPasswordOk, сигнализирующей об успешности ввода пароля. Для понимания происходящего с этой переменной переносимся в функцию порожденного нами потока. Что здесь происходит? В бесконечном цикле производится чтение данных по ранее открытому сетевому соединению. Полученные данные записываются в файл. Кроме этого производится синтаксический разбор ответов модуля. Извесно, что все ответы выдаются в формате #<данные>\r\n. Основываясь наэтом производится выделение отдельных ответов модуля для дальнейшего синтаксического разбора в функции ParseData().

В случае нахождения соответствующего ответа модуля о верно введенном пароле переменная bPasswordOk взводится в активное состояние. Переносимся обратно в main(). Далее у модуля запрашивается переодическая выдача сводной информации по аппаратным ресурсам с помощью команды $KE,DAT,ON и собственно больше основной поток ни чего не делает. Единственное, он завершает работу программы по факту нажатия любой клавиши. Сводная информация по аппаратным ресурсам выдается модулем с частотой 1 Гц. Все что он выдал рано или поздо попадает в функцию синтаксического разбора ParseData(). Для примера, в данном случае выделяется информация о системном времени модуля и выводится на экран.



© Дмитрий Иванов
3 Июня 2011
http://www.kernelchip.ru



© KERNELCHIP 2006 - 2017