При самостоятельной разработке систем управления АЗС или автоматизированных рабочих мест (АРМ) оператора АЗС требуется решить задачу управления оборудованием, в частности контроллерами управления (КУ) ТРК.
Обычно, прежде всего, начинаются поиски драйвера КУ ТРК. К сожалению, универсальных драйверов для этого оборудования нет. Основные причины этого следующие:
1. Из всего спектра протоколов оборудования для управления отпуском топлива на АЗС, только так называемый
"Универсальный протокол..." (СКБ ВТ "Искра") является общедоступным. Все остальные нужно покупать или "доставать". Почему это так - ведомо только производителям оборудования с закрытыми протоколами обмена. Вероятно, для того, чтобы покупатели, которые однажды приобрели оборудование данной фирмы, навсегда остались к ней привязаны.
Но более-менее универсальная программа АРМ должна работать с различными типами оборудования. А программисты, потратившие деньги и время на розыск протоколов не слишком спешат поделиться ими с коллегами.
Каждый разработчик АРМ АЗС вызывает драйверы различного оборудования из своей программы своим специфическим способом. Поэтому драйверы от одной программы не подойдут к другой, даже если они выполняют одинаковые функции.
2. Интерфейс взаимодействия программы с внешним драйвером будет не проще, чем прямое обращение к своим драйверам или напрямую к оборудованию.
Многие предлагают сделать взаимодействие с КУ ТРК через файл (обычно высказываются пожелания относительно DBF формата). Но работа с оборудованием через файл резко увеличит время обращения к нему. О работе же в реальном времени в таком случае даже и мечтать не приходится. Кроме того, появляется дополнительный источник ошибок и неопределенностей. Например, значительно повышается вероятность сбоев и потери данных при внезапном пропадании питания компьютера (аварийном останове программы).
При этом сам драйвер обязан уметь работать со всеми версиями DBF напрямую, или пользоваться СУБД. В первом случае - нет гарантии, что все возможные варианты будут корректно отработаны. Второй случай еще хуже - вызовы СУБД из драйвера оборудования, работающего в реальном времени - это нонсенс.
Более грамотным решением является обмен сообщениями между программами АРМ и драйвера. Но для этого программе АРМ все равно придется организовать корректный обмен данными с другим приложением.
Причины затруднений при связи с оборудованием через последовательный порт заключены в том, что разработкой АРМ обычно занимаются программисты, которые ориентированы на работу в СУБД. Разумеется, в желании решить задачу привычным инструментом, ничего особо плохого нет, но этот подход малоэффективен.
В принципе же нет ничего сложного в работе с КУ ТРК через интерфейс последовательной связи RS-232C напрямую из программы АРМ. Далее приведен пример обмена данными с КУ ТРК через последовательный порт в операционной системе (ОС) Windows на языке C++. Пример работоспособен под всеми версиями Windows от 95 до XP. Для работы с портом используется коммуникационный API Win32, поэтому данный пример с минимальными переделками можно перенести на другие языки, позволяющие использовать WinAPI, к примеру, тот же Visual Basic.
Приведенный пример будет корректно работать и в WINE под Linux.
Для упрощения примера обмен ведется в синхронном режиме. Это значит, что выполнение основной программы прерывается на время обмена, но во многих случаях это можно допустить.
В асинхронном режиме возврат из функции происходит немедленно, сам обмен происходит в фоновом режиме, а по окончании обмена ОС сама сообщает об этом приложению. Этот режим предпочтительнее, но чуть сложнее. С небольшими изменениями приведенный пример может быть использован и для работы в асинхронном режиме.
Кроме того, в примере открытие и закрытие коммуникационного порта происходит при каждом обмене данными. Это несколько ограничивает скорость обмена. Для ускорения работы открытие и закрытие порта следует выделить в отдельные функции. При этом желательно не забывать их вызывать после запуска программы и перед выходом из нее.
Пример работы с КУ ТРК
Пример намеренно упрощен. В реальных программах, например в САУ "Лайнер", используются более сложные алгоритмы. Но этот подход позволяет получить вполне работоспособную программу.
// Начало примера
/* При создании консольного приложения этот файл может автоматически не добавиться к проекту, придется добавить вручную */
#include <afxwin.h>
/* В программе должны быть локально или глобально объявлены hPort, dcb, cto */
HANDLE hPort=NULL; /* Дескриптор порта, создается при открытии и используется для всех операций с портом */
DCB dcb; /* Device Control Block - установки, управляющие работой порта */
COMMTIMEOUTS cto; /* Структура, задающая тайм-ауты последовательного порта */
/* Класс, который будет использоваться для управления КУ ТРК */
class CPilot
{
public:
CPilot(); // конструктор класса
int kom_pilot(int, int, long, long, long); /* функция обмена данными с КУ ТРК */
int print(int); /* печать буферов команды и ответа на экран - используется в примере и может быть использована при отладке */
int com; /* номер последовательного порта 1 - COM1 и т.д. до COM9 */
private:
BYTE kbuf[256]; // буфер передаваемой команды
BYTE rbuf[256]; // буфер принимаемого ответа
};
/* Далее приведен текст функций класса CPilot: */
/////////////////////////////////////////////////////////////////
CPilot::CPilot()
{
int i;
/* Инициализация буферов, хотя этого можно и не делать */
for (i=0; i<32; i++) rbuf[i]=kbuf[i]=0;
}
int CPilot::kom_pilot(int pnum, int pkom, long pcena, long pdoza, long pkod)
{
/* Используемые при вызове переменные
pnum - номер КУ ТРК (обычно соответствует номеру ТРК или номеру отдела ККМ)
pkom - код команды, см. "Универсальный протокол"
pcena - цена за 1 л топлива в копейках, от 0 до 999999
pdoza - доза топлива в миллилитрах, от 0 до 999999
pkod - соответствует числовому значению поля Status, см. "Универсальный протокол", от 0 до 0xFFFF (то есть до 65535 в десятичном виде).
Примечание. В исходной программе на Си данные хранятся и передаются в двоичном виде.
При вызове из СУБД, вероятно, было бы проще не преобразовывать данные и передавать их в виде строк, но тогда следует внимательно следить за форматом строки и заполнять поля буфера kbuf[] слева символами "нуль" (0x30).
*/
int re=0;
/* Код возврата из функции 0 - OK, 256-выход по тайм-ауту, 257-ошибочная контрольная сумма или формат команды, 258 - последовательный порт не открыт или не обнаружен.
0x31...0x33 - ошибки, возвращаемые КУ ТРК по "Универсальному протоколу":
0x31 - "Недопустимый номер ТРК" в версии протокола 1.72 реально не используется (некому его вырабатывать). Был введен в младших версиях, в которых номер контроллера и номер ТРК передавались раздельно;
0x32 - "Недопустимая команда ТРК при данном состоянии ТРК" - подана команда, которая не может быть исполнена в текущем состоянии КУ ТРК;
0x33 - "Неправильная контрольная сумма" - в принятой КУ ТРК команде не совпала контрольная сумма.
*/
int i,j; /* Вспомогательные переменные*/
CString strPort="\\\\.\\COM"; /* Строка, используемая при открытии порта */
char ptemp[32]; /* буфер для преобразования чисел*/
DWORD d; /* переменная для подсчета реально переданных и принятых байт*/
/* Начало формирования команды в буфере kbuf[], формат команды здесь подробно не рассматривается. См. в "Универсальном протоколе" */
kbuf[0]=1; // SOH
/* формирование номера КУ ТРК */
switch (pnum)
{ case 0: kbuf[1]=0x30; kbuf[2]=0x30; break; // "00"
case 1: kbuf[1]=0x30; kbuf[2]=0x31; break; // "01"
case 2: kbuf[1]=0x30; kbuf[2]=0x32; break; // "02"
case 3: kbuf[1]=0x30; kbuf[2]=0x33; break; // "03"
case 4: kbuf[1]=0x30; kbuf[2]=0x34; break; // "04"
case 5: kbuf[1]=0x30; kbuf[2]=0x35; break; // "05"
case 6: kbuf[1]=0x30; kbuf[2]=0x36; break; // "06"
case 7: kbuf[1]=0x30; kbuf[2]=0x37; break; // "07"
case 8: kbuf[1]=0x30; kbuf[2]=0x38; break; // "08"
case 9: kbuf[1]=0x30; kbuf[2]=0x39; break; // "09"
case 10: kbuf[1]=0x30; kbuf[2]=0x41; break; // "0A"
case 11: kbuf[1]=0x30; kbuf[2]=0x42; break; // "0B"
case 12: kbuf[1]=0x30; kbuf[2]=0x43; break; // "0C"
case 13: kbuf[1]=0x30; kbuf[2]=0x44; break; // "0D"
case 14: kbuf[1]=0x30; kbuf[2]=0x45; break; // "0E"
case 15: kbuf[1]=0x30; kbuf[2]=0x46; break; // "0F"
case 16: kbuf[1]=0x31; kbuf[2]=0x30; break; // "10"
default: kbuf[1]=0x30; kbuf[2]=0x30; /* "00" по умолчанию при некорректных входных данных */
}
/* Перенос кода команды */
kbuf[3]=(BYTE)pkom; /* Это позволяет использовать произвольные коды команд, в том числе
дополнительные команды КУ ТРК серии ПИЛОТ */
if (pnum==0) kbuf[3]=0x37; /* Для номера "00" код команды - всегда "Общий останов", впрочем, для КУ ТРК ПИЛОТ данная проверка не обязательна */
kbuf[4]=2; // STX
/* Заполнение поля "Price". Проверка цены на корректность */
if (pcena<0) pcena=0;
if (pcena>999999) pcena=999999;
ltoa(pcena,ptemp,10); /* Преобразование числа в строку по основанию системы счисления 10 */
/* Далее следует перенос в буфер kbuf[] из строки ptemp в зависимости от ее длины.
Это, разумеется, можно было сделать и короче, но гораздо менее наглядно.
Поскольку не это в примере главное, не следует отвлекаться на лишние головоломки. Пусть здесь и далее будет так.
*/
if (pcena==0)
{
kbuf[5]=0x30;
kbuf[6]=0x30;
kbuf[7]=0x30;
kbuf[8]=0x30;
kbuf[9]=0x30;
kbuf[10]=0x30;
goto jmp_doza;
}
if (pcena<10)
{
kbuf[5]=0x30;
kbuf[6]=0x30;
kbuf[7]=0x30;
kbuf[8]=0x30;
kbuf[9]=0x30;
kbuf[10]=ptemp[0];
goto jmp_doza;
}
if (pcena<100)
{
kbuf[5]=0x30;
kbuf[6]=0x30;
kbuf[7]=0x30;
kbuf[8]=0x30;
kbuf[9]=ptemp[0];
kbuf[10]=ptemp[1];
goto jmp_doza;
}
if (pcena<1000)
{
kbuf[5]=0x30;
kbuf[6]=0x30;
kbuf[7]=0x30;
kbuf[8]=ptemp[0];
kbuf[9]=ptemp[1];
kbuf[10]=ptemp[2];
goto jmp_doza;
}
if (pcena<10000)
{
kbuf[5]=0x30;
kbuf[6]=0x30;
kbuf[7]=ptemp[0];
kbuf[8]=ptemp[1];
kbuf[9]=ptemp[2];
kbuf[10]=ptemp[3];
goto jmp_doza;
}
if (pcena<100000)
{
kbuf[5]=0x30;
kbuf[6]=ptemp[0];
kbuf[7]=ptemp[1];
kbuf[8]=ptemp[2];
kbuf[9]=ptemp[3];
kbuf[10]=ptemp[4];
goto jmp_doza;
}
kbuf[5]=ptemp[0];
kbuf[6]=ptemp[1];
kbuf[7]=ptemp[2];
kbuf[8]=ptemp[3];
kbuf[9]=ptemp[4];
kbuf[10]=ptemp[5];
jmp_doza: /* Заполнение поля Volume. Метка для перехода из goto. Это не делает программу "структурированной" и в некоторых учебниках считается недопустимым, но зато избавляет от ненужных фигурных скобок, разобраться в которых тоже не просто. Тем более, что в откомпилированной программе все эти фигурные скобки все равно будут заменены на команды условного и безусловного перехода. */
/* Проверка дозы на корректность */
if (pdoza<0) pdoza=0;
if (pdoza>999999) pdoza=999999;
/* Перенос дозы в kbuf[]. Выполняется аналогично. */
ltoa(pdoza,ptemp,10);
if (pdoza==0)
{
kbuf[11]=0x30;
kbuf[12]=0x30;
kbuf[13]=0x30;
kbuf[14]=0x30;
kbuf[15]=0x30;
kbuf[16]=0x30;
goto jmp_kod;
}
if (pdoza<10)
{
kbuf[11]=0x30;
kbuf[12]=0x30;
kbuf[13]=0x30;
kbuf[14]=0x30;
kbuf[15]=0x30;
kbuf[16]=ptemp[0];
goto jmp_kod;
}
if (pdoza<100)
{
kbuf[11]=0x30;
kbuf[12]=0x30;
kbuf[13]=0x30;
kbuf[14]=0x30;
kbuf[15]=ptemp[0];
kbuf[16]=ptemp[1];
goto jmp_kod;
}
if (pdoza<1000)
{
kbuf[11]=0x30;
kbuf[12]=0x30;
kbuf[13]=0x30;
kbuf[14]=ptemp[0];
kbuf[15]=ptemp[1];
kbuf[16]=ptemp[2];
goto jmp_kod;
}
if (pdoza<10000)
{
kbuf[11]=0x30;
kbuf[12]=0x30;
kbuf[13]=ptemp[0];
kbuf[14]=ptemp[1];
kbuf[15]=ptemp[2];
kbuf[16]=ptemp[3];
goto jmp_kod;
}
if (pdoza<100000)
{
kbuf[11]=0x30;
kbuf[12]=ptemp[0];
kbuf[13]=ptemp[1];
kbuf[14]=ptemp[2];
kbuf[15]=ptemp[3];
kbuf[16]=ptemp[4];
goto jmp_kod;
}
kbuf[11]=ptemp[0];
kbuf[12]=ptemp[1];
kbuf[13]=ptemp[2];
kbuf[14]=ptemp[3];
kbuf[15]=ptemp[4];
kbuf[16]=ptemp[5];
jmp_kod: /* Заполнение поля Status. Метка для перехода по goto. */
/* Проверка поля Status */
if (pkod<0) pkod=0;
if (pkod>65535) pkod=65535; /* Поле Status - шестнадцатиричное с четырьмя цифрами. Максимальное значение 65535=0xFFFF */
ltoa(pkod,ptemp,16); /* Преобразование числа в строку по основанию 16 */
strupr(ptemp); /* Превращение цифр a-f в A-F */
/* Перенос в kbuf[] */
if (pkod==0)
{
kbuf[17]=0x30;
kbuf[18]=0x30;
kbuf[19]=0x30;
kbuf[20]=0x30;
goto jmp_exi;
}
if (pkod<0x10)
{
kbuf[17]=0x30;
kbuf[18]=0x30;
kbuf[19]=0x30;
kbuf[20]=ptemp[0];
goto jmp_exi;
}
if (pkod<0x100)
{
kbuf[17]=0x30;
kbuf[18]=0x30;
kbuf[19]=ptemp[0];
kbuf[20]=ptemp[1];
goto jmp_exi;
}
if (pkod<0x1000)
{
kbuf[17]=0x30;
kbuf[18]=ptemp[0];
kbuf[19]=ptemp[1];
kbuf[20]=ptemp[2];
goto jmp_exi;
}
kbuf[17]=ptemp[0];
kbuf[18]=ptemp[1];
kbuf[19]=ptemp[2];
kbuf[20]=ptemp[3];
jmp_exi: /* Заполнение остальных полей */
kbuf[21]=3; // ETX
/* Подсчет контрольной суммы CRC */
kbuf[22]=0;
for (i=1; i<22; i++) kbuf[22]^=kbuf[i];
/* Команда сформирована в буфере kbuf[] - далее открытие порта */
itoa(com,ptemp,10); /* Преобразование номера последовательного порта в строку */
strPort+=ptemp; /* Добавление номера к существующей строке */
/* Открытие коммуникационного порта для ввода - вывода */
hPort=::CreateFile(strPort, GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,NULL);
/*
Здесь
hPort - дескриптор
strPort - строка с наименованием порта COM1...COM9 к которому при помощи кабеля связи подключены КУ ТРК. Естественно, порт с указанным номером должен физически присутствовать в компьютере. Будтье внимательны, в "современных" материнских платах может оказаться всего один COM-порт или вовсе их не быть. В таком случае следует купить и установить в компьютер специальную плату с последовательными портами.
Если вместо FILE_ATTRIBUTE_NORMAL указать FILE_FLAG_OVERLAPPED порт будет открыт в асинхронном режиме, но это выходит за рамки данного примера.
*/
/* Если порт не открывается - выход с кодом ошибки 258 */
if((hPort==INVALID_HANDLE_VALUE)||(hPort==NULL)) {re=258; goto end;}
/* Установка параметров обмена */
memset(&dcb,0,sizeof(dcb)); /* Выделение места в памяти */
dcb.DCBlength=sizeof(dcb);
dcb.BaudRate=CBR_9600; /* Скорость обмена c КУ ТРК, может быть, например CBR_19200 для КУ ТРК ПИЛОТ-11.2 */
dcb.ByteSize=8;dcb.Parity=0;dcb.StopBits=ONESTOPBIT; /* 8 бит, 1 стоп бит, без контроля четности */
dcb.fBinary=TRUE; /* Обмен двоичными числами */
dcb.fOutxCtsFlow=FALSE; /* Сигнал CTS не отслеживать */
dcb.fOutxDsrFlow=FALSE; /* Сигнал DSR не отслеживать */
dcb.fDtrControl=DTR_CONTROL_DISABLE; /* Контроль с помощью сигнала DTR запретить */
dcb.fDsrSensitivity=FALSE; /* Сигнал DSR на коммуникационный драйвер не влияет */
dcb.fOutX=FALSE; /* Управление выходным потоком символами XON/XOFF отключить */
dcb.fInX=FALSE; /* Управление входным потоком символами XON/XOFF отключить */
dcb.fErrorChar=FALSE; /* Ошибочные байты не заменять */
dcb.fNull=FALSE; /* Нулевые байты не отбрасывать */
dcb.fRtsControl=RTS_CONTROL_DISABLE; /* Запретить RTS */
dcb.fAbortOnError=FALSE; /* Не прекращать ввод-вывод при ошибках */
dcb.wReserved=0; /* Зарезервировано - должен быть 0 */
::SetCommState(hPort,&dcb); /* Установить параметры порта с дескриптором hPort из структуры dcb */
cto.ReadIntervalTimeout=40; /* Максимальное время между чтением байт, мс */
cto.ReadTotalTimeoutMultiplier=0; /* Множитель для вычисления общего времени чтения */
cto.ReadTotalTimeoutConstant=200; /* Время ожидания начала приема, мс */
cto.WriteTotalTimeoutMultiplier=10; /* Множитель для вычисления общего времени записи, мс */
cto.WriteTotalTimeoutConstant=100; /* Время ожидания начала записи, мс */
::SetCommTimeouts(hPort,&cto); /* Установить тайм-ауты из структуры cto */
::SetupComm(hPort,23,23); /* Очистить внутренние буферы порта и установить их длину 23 байта */
for (i=0; i<32; i++) rbuf[i]=0; /* Очистить входной буфер */
if(hPort!=INVALID_HANDLE_VALUE&&hPort!=NULL)
{
/* Если порт успешно открыт */
/* Передать 23 байта из буфера kbuf[] */
::WriteFile(hPort,(LPVOID)kbuf,23,&d,NULL);
/* Прочитать 23 байта в буфер rbuf[] */
::ReadFile(hPort,(LPVOID)rbuf,23,&d,NULL);
/* Закрыть порт */
CloseHandle(hPort);
}
/* Проверка количества принятых байт */
if (d<23) {re=256; goto end;}
/* Проверка буфера rbuf на ненулевые значения, требуется для подтверждения выполнения операции чтения, может не выполняться */
j=0;
for (i=0; i<23; i++)
if (rbuf[i]==0) j++;
if (j>22) {re=256; goto end;}
/* Проверка контрольной суммы принятого от КУ ТРК ответа */
j=0;
for (i=1; i<22; i++) j^=rbuf[i];
if (rbuf[22]!=(BYTE)j) {re=257; goto end;}
/* Проверка формата команды - наличие в ответе обязательных полей SOH, STX, ETX */
if ((rbuf[0]!=1)||(rbuf[4]!=2)||(rbuf[21]!=3)) {re=257; goto end;}
/* Проверка того, что на команду ответил запрошенный КУ ТРК*/
if ((rbuf[1]!=kbuf[1]) || (rbuf[2]!=kbuf[2])) {re=257; goto end;}
/* Перенос кода ошибки из поля Error в переменную re.
При подаче некоторых дополнительных команд поле Error может содержать другие данные, поэтому производится проверка на
соответствие принятой команды набору команд "Универсального протокола" версии 1.72 или команде
"Расширенный тест"
*/
if ((rbuf[3]==0x31) || (rbuf[3]==0x33) || (rbuf[3]==0x34) || (rbuf[3]==0x35) || (rbuf[3]==0x36) || (rbuf[3]==0x37)
|| (rbuf[3]==0x39) || (rbuf[3]==0x54))
{
/* Код команды известен и старший байт ошибки нулевой */
if ((rbuf[17]==0x30)&&(rbuf[18]!=0x30)) re=rbuf[18];
}
/* Выход из функции */
end: return(re);
}
/*
Приведенная ниже функция требуется для печати содержимого буферов на экран.
Может использоваться при отладке программы.
*/
int CPilot::print(int err)
{
int i;
if (err==0)
{
/* Если нет ошибок - распечатать буферы в шестнадцатиричном виде (HEX) */
for (i=0; i<23; i++)
{
if (i!=0) printf(",");
else printf("> ");
if (kbuf[i]<16) printf("0");
printf("%X",kbuf[i]);
}
printf("\r\n");
for (i=0; i<23; i++)
{
if (i!=0) printf(",");
else printf("< ");
if (rbuf[i]<16) printf("0");
printf("%X",rbuf[i]);
}
printf("\r\n\n");
}
else
/* Если есть код ошибки - напечатать сообщение */
printf ("Error %d \r\n\n",err);
return 0;
}
/*
Эта функция - главная. Из нее происходит обращение к подпрограмме обмена с КУ ТРК
*/
int main(int argc, char* argv[])
{
int i; /* вспомогательная переменная */
int n; /* Здесь хранится номер КУ ТРК */
CPilot Pilot; /* Объект класса CPilot */
SetConsoleTitle("Проверка КУ ТРК"); /* Заголовок окна */
Pilot.com=1; /* Использовать порт COM1 */
n=1; /* КУ ТРК номер 1 */
/* Далее передаются команды "Универсального протокола" */
i=Pilot.kom_pilot(n,0x37,0,0,0); /* Сброс */
Pilot.print(i); /* Распечатка буферов или кода ошибки */
Sleep(200); /* Задержка 0,2 с */
i=Pilot.kom_pilot(n,0x31,1500,100000,0); /* Задать дозу 100 л по цене 15 руб/литр */
Pilot.print(i);
Sleep(200);
i=Pilot.kom_pilot(n,0x35,0,0,0); /* Пуск */
Pilot.print(i);
Sleep(200);
i=Pilot.kom_pilot(n,0x34,0,0,0); /* Тест */
Pilot.print(i);
Sleep(200);
i=Pilot.kom_pilot(n,0x36,0,0,0); /* Останов */
Pilot.print(i);
Sleep(200);
i=Pilot.kom_pilot(n,0x37,0,0,0); /* Сброс */
Pilot.print(i);
return 0;
}
// Конец примера
Для компиляции программы следует создать консольное приложение (Win32 Console Application) с именем, например, Test1. Для упрощения работы следует создать заготовку приложения (A simple application).
Затем скопировать в файл test1.cpp приведенный выше текст и откомпилировать его.
Если все сделано правильно - получится файл test1.exe.
Для записи результатов в файл перенаправьте вывод программы, для этого наберите в командной строке: "test1.exe > test1.txt". После запуска с подключенным к порту COM1 КУ ТРК с номером 1 в каталоге программы появится файл
test1.txt. В него будут записаны команды и ответы:
Начало файла
> 01,30,31,37,02,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,03,37
< 01,30,31,37,02,30,30,31,35,30,30,30,30,30,30,30,30,30,30,30,35,03,36
> 01,30,31,31,02,30,30,31,35,30,30,31,30,30,30,30,30,30,30,30,30,03,34
< 01,30,31,31,02,30,30,31,35,30,30,31,30,30,30,30,30,30,30,30,31,03,35
> 01,30,31,35,02,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,03,35
< 01,30,31,35,02,30,30,31,35,30,30,31,30,30,30,30,30,30,30,30,33,03,33
> 01,30,31,34,02,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,03,34
< 01,30,31,34,02,30,30,31,35,30,30,31,30,30,30,30,30,30,30,30,33,03,32
> 01,30,31,36,02,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,03,36
< 01,30,31,36,02,30,30,31,35,30,30,31,30,30,30,30,30,30,30,30,31,03,32
> 01,30,31,37,02,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,03,37
< 01,30,31,37,02,30,30,31,35,30,30,30,30,30,30,30,30,30,30,30,35,03,36
Конец файла
Здесь рассмотрена организация обмена на уровне работы с последовательным портом. Организация обмена с КУ ТРК на уровне команд протокола описана на странице:
"Особенности использования Универсального протокола".
|