"ОПИСАНИЕ протокола обмена между ПЭВМ и приборами ОВЕН" не совсем корректное, особенно это касается "СТРУКТУРНОЙ СХЕМЫ ПРОТОКОЛА ОВЕН", вот и претензий много
я, честно говоря, не первый раз такие спецификации читаю и работаю с ними, поэтому когда писал конфигуратор команд для протокола owen, делал ровно то, что написано в этих документах,
а получилось это только после того, как увидел 2 "живые" команды на форуме
вот чтобы такого не происходило, я бы порекомендовал сделать всего 3 дополнения:
1. перерисуйте эту схему так, чтобы был виден четкий порядок бит, раз уж там они есть
2. ваш CRC расчитывается с исключением из пакета кроме маркеров еще и самой контрольной суммы, т.е. не текущая, не нулевая, а вообще без этого члена
3. в документации приведите 1-2 готовые команды, чтобы не ломать мозг
получается, что заголовок имеет такую структуру:
Low - младший байт адресаКод:typedef struct tagOwenHeader{ WORD Low : 8; WORD Size : 4; WORD Query : 1; WORD High : 3; }OWEN_HEADER, *POWEN_HEADER;
Size - размер поля данных - 2 (2 - это размер хеша имени)
Query - признак запроса
High - старшие 3 бита адреса
перед расчетом CRC хеш команды записывается в пакет перевернутым байтами, т.е. сначала high, потом low,
после расчета CRC, сам CRC так же переворачивается, после чего, вся полученная последовательность кодируется тетрадами
на счет уровня приложения-представления - пока не проверял, будем смотреть дальше
на счет переворачиваний, я пока без самого девайса не понял, но если разбить пакет на 3 сущности: заголовок, команда, CRC, то можно в принципе перевернуть все это дело перед кодированием тетрадами, но тогда не будет сходиться CRC и в данных будет бардак, либо можно тетрады выводить старшим полубайтом вперед, тогда можно не переворачивать, (все зависит от конечной реализации авторов; сам склюняюсь к тому, что все таки напутали порядок полубайтов), вобщем я сделал пока как есть
в приложении мой конфигуратор команд, это консолька, которая позволяет просто собрать команду, в конечный вид, перед отправкой, так что сам девайс ей не нужен (если кто-то будет вводить имя команды, не забывайте про пробелы в конце этой команды, например "dev " или "ver ")
(завтра проверю на девайсе, если успею, а сегодня просто выкладываю, т.к. команды уже сходятся по текущей теме и еще по этой: http://www.owen.ru/forum/showthread.php?t=7784)
Последний раз редактировалось mega; 09.06.2010 в 14:18. Причина: подправил приложение: сдвиг старшей части адреса был на 4 бита, теперь как положено - на 8
а вот и мой исходник этой консольки,
кто желает встроить этот генератор себе в программу:
несколько похожий код я использовал у себя в трансляторе этого протокола в SCPI форму, пока все работаетКод:#include <windows.h> #include <tchar.h> #include <locale.h> #include <stdio.h> #include <conio.h> // BOOL ASCIIToCode( CHAR ch, CHAR*char_for_hash ){ ch = toupper( ch ); //преобразуем все в верхний регистр if( strchr( "0123456789", ch ) ){ *char_for_hash = ch - '0'; }else if( (ch >= 'A') && (ch <= 'Z') ){ *char_for_hash = ch - 'A' + 10; }else{ switch( ch ){ case '-': *char_for_hash = 10 + 26; break; case '_': *char_for_hash = 10 + 26 + 1; break; case '/': *char_for_hash = 10 + 26 + 2; break; case ' ': *char_for_hash = 10 + 26 + 3; break; default: return FALSE; } } *char_for_hash *= 2; return TRUE; } // USHORT Hash( BYTE Byte, BYTE nbit, USHORT CRC ){ for( INT i = 0; i < nbit; i++, Byte <<= 1){ if( ( Byte ^ ( CRC >> 8 ) ) & 0x80 ){ CRC <<= 1; CRC ^= 0x8F57; }else{ CRC <<= 1; } } return CRC; } // typedef union tagOwenHeader{ struct{ WORD Low : 8; WORD Size : 4; WORD Query : 1; WORD High : 3; }; BYTE Bytes[2]; }OWEN_HEADER, *POWEN_HEADER; // int _tmain( int argc, TCHAR *argv[] ) { INT addr, size, Byte; TCHAR query[2], cmd_fmt[2]; CHAR a_cmd[100]; BYTE Data[15]; BYTE Command[50]; OWEN_HEADER hdr; INT l, i; USHORT hash; CHAR chfh; // _tsetlocale( LC_ALL, TEXT("Russian") ); // loop: do{ fflush( stdin ); _tprintf_s( TEXT("Введите адрес устройства (0-2047): ") ); }while( _tscanf_s( TEXT("%i"), &addr ) != 1 ); // do{ fflush( stdin ); _tprintf_s( TEXT("Это запрос? (y/n): ") ); }while( _tscanf_s( TEXT("%1[yn]"), &query, sizeof(query)/sizeof(TCHAR) ) != 1 ); // do{ fflush( stdin ); _tprintf_s( TEXT("Введите размер блока данных (2-17): ") ); }while( (_tscanf_s( TEXT("%i"), &size ) != 1) || (size < 2) || (size > 17) ); // do{ fflush( stdin ); _tprintf_s( TEXT("команда будет введена строкой? (y-строка/n-HASH): ") ); }while( _tscanf_s( TEXT("%1[yn]"), &cmd_fmt, sizeof(cmd_fmt)/sizeof(TCHAR) ) != 1 ); // switch( cmd_fmt[0] ){ case TEXT('y'): cmd_loop: do{ fflush( stdin ); _tprintf_s( TEXT("Введите имя команды (допустимый набор символов: [a-zA-Z0-9-_/ .]): ") ); }while( _tscanf_s( TEXT("%h[a-zA-Z0-9-_/ .]"), &a_cmd, sizeof(a_cmd)/sizeof(CHAR) ) != 1 ); // _tprintf_s( TEXT("Имя команды: \"%hs\"\r\n"), a_cmd ); // for( hash = 0, l = strlen( a_cmd ), i = 0 ; l-- ; ++i ){ if( a_cmd[i] != '.' ){ if( !ASCIIToCode( a_cmd[i], &chfh ) ){ goto cmd_loop; } if( a_cmd[i + 1] == '.' ){ ++chfh; } hash = Hash( chfh << 1, 7, hash ); } } break; case TEXT('n'): do{ fflush( stdin ); _tprintf_s( TEXT("Введите HASH команды: ") ); }while( _tscanf_s( TEXT("%hi"), &hash ) != 1 ); break; } _tprintf_s( TEXT("HASH команды: 0x%.4hx\r\n"), hash ); // for( i = 0, l = size - 2 ; l-- ; ++i ){ do{ fflush( stdin ); _tprintf_s( TEXT("Введите %i-й байт команды (0-255): "), i + 3 ); if( _tscanf_s( TEXT("%i"), &Byte ) != 1 ){ continue; } if( Byte < 0 || Byte > 255 ){ continue; } Data[i] = Byte; break; }while( TRUE ); } // hdr.Low = addr; hdr.High = addr >> 8; hdr.Query = ( query[0] == TEXT('y') ) ? 1 : 0; hdr.Size = (BYTE)(size - 2); Command[0] = hdr.Bytes[0]; Command[1] = hdr.Bytes[1]; Command[2] = (BYTE)(hash >> 8); Command[3] = (BYTE)hash; for( i = 0, l = size - 2 ; l-- ; ++i ){ Command[4 + i] = Data[i]; } // for( hash = 0, i = 0, l = 2 + size ; l-- ; ++i ){ hash = Hash( Command[i], 8, hash ); } Command[2 + size] = (BYTE)(hash >> 8); Command[3 + size] = (BYTE)hash; // _tprintf_s( TEXT("--------\r\nКоманда:\r\n") ); for( i = 0, l = 4 + size ; l-- ; ++i ){ _tprintf_s( TEXT("%.2x"), Command[i] ); } _tprintf_s( TEXT("\r\n#") ); for( i = 0, l = 4 + size ; l-- ; ++i ){ _tprintf_s( TEXT("%c"), 'G' + ( Command[i] >> 4 ) ); _tprintf_s( TEXT("%c"), 'G' + ( Command[i] & 0x0f ) ); } _tprintf_s( TEXT("\\r\r\n--------\r\n") ); // do{ fflush( stdin ); _tprintf_s( TEXT("Продолжить? (y/n): ") ); }while( _tscanf_s( TEXT("%1[yn]"), &query, sizeof(query)/sizeof(TCHAR) ) != 1 ); // if( query[0] == TEXT('y') ){ goto loop; } return 0; }
т.е. для съема-установки параметров использую формат обмена в удобочитаемой форме, типа такого:
#devno:query:command\r
то, что вы языком C плохо владеете, было ясно еще по первому сообщению. совершенно не обязательно выставлять это напоказ.
интересное умозаключение :)
особенно интересно это слышать от человека, совершенно не понимающего битовую логику (я делаю такой вывод из посыла почитать тот самый документ, очевидные ошибки в котором я уже подчеркнул)
если это техподдержка "овен", сочуствую вашей администрации, т.к. ликбез в культуре общения и программирования тут неизбежен :))
это не умозаключение, а факт. ну ладно, я битовую логику не понимаю, вы-то вообще читать не умеете. посмотрите хотя бы раздел "3.3 структура кадра".
давайте читать вместе, как сказку на ночь.
--
"2.11 Каждое сообщение и квитанция передается старшими байтами вперед."
не полубайтами, не битами, а именно байтами. это относится как ко всему пакету (до кодирования в ASCII), так и к частям пакета -- локальному идентификатору параметра, полю данных и контрольной сумме.
это положение имеет забавное следствие: строки в поле данных лежат старшим байтом вперед, то есть задом наперед.
"структурная схема протокола овен"
здесь нужно понять два момента. во-первых, пакет кодируется в ASCII прямо перед посылкой, и дальше мы будем обсуждать пакет в двоичном виде. во-вторых, в младших четырех битах второго байта пакета находится размер поля данных. так проще.
о порядке битов менее затейливые умы даже не задумываются. как обычно, старший бит слева, младший -- справа.
"3.1 Метод передачи байта "Тетрада-в-ASCII-символ"
особо сказать нечего. один байт превращается в два. старший из них опять передается первым (сюрприз!). сказкам про избыточность не верьте -- эффективность такой проверки очень низкая.
"3.3 Структура кадра"
для 11-битных адресов в первом байте пакета находятся _старшие_ восемь бит адреса. младшие три бита находятся в _старших_ битах второго байта пакета.
проще всего забыть про 11-битную адресацию, и считать, что весь адрес помещается в первом байте пакета.
дальше все просто. признак удаленного запроса -- это пятый бит второго байта. младшие четыре бита остаются для размера поля данных. несложно догадаться, что поле данных не может быть больше 15 байт.
блок данных канального уровня -- это дань модели взаимодействия открытых систем, применение которой только запутывает описание протокола. проще думать об этом блоке как о двух сущностях -- локальном идентификаторе (hash-коде) параметра и поле данных.
локальный идентификатор вы худо-бедно посчитали, так что перейдем к CRC.
"4.5 Хеширование имен параметров и вычисление контрольной суммы сообщения"
контрольная сумма пакета -- это классический CRC16 с самопальным полиномом. аккумулятор инициализируется 0. почему в описании дана рекурсивная функция я не знаю -- видимо так она использовалась в доисторических приборах. более вменяемый алгоритм в этой ветке есть.
контрольная сумма помещается в пакет, (вы не поверите!) старшим байтом вперед.
--
истинному знатоку слова стандарт, виртуозу битовой логики и непревзойденному читателю RFC разобраться во всем этом будет несложно.
n'est ce pas?
Mega, огромное вам спасибо за прогу!!!!!!!!!! Замучился получать эту CRC Для СИ 8. Когда же в России кончится самопал!? И появятся нормальные описания протоколов с примерами.