// Test_kutrk.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include 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[] слева нулями. */ 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; SetConsoleTitle("Проверка КУ ТРК"); Pilot.com=1; /* Использовать порт COM1 */ n=1; /* КУ ТРК номер 1 */ i=Pilot.kom_pilot(n,0x37,0,0,0); /* Сброс */ Pilot.print(i); Sleep(200); 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; }