====== Коммуникационный протокол JRBusTcp ====== [[doc:jroboplc:modules:jrbustcp-sample]] ===== Формат сообщения ===== Максимально допустимый размер сообщения составляет **16384** байт. Общий формат сообщений: size#2 header#2 [reqId#4 cmd#1 body#size-11] crc#4 ''**#**'' - размер параметра в байтах. ''**size**'' - количество байт в сообщении с ''header'' по ''crc'' включительно. ''**header**'' - всегда равно 0xABCD. ''**reqId**'' - идентификатор запроса. Любое значение из диапазона int32, в т.ч. отрицательного. Предназначено для проверки клиентом соответствия ответа запросу. Начальное значение клиент формирует генератором случайных чисел при подключении. В дальнейшем при каждом запросе значение увеличивается на 1. Сервер полученное значение ''reqId'' никак не проверяет и отсылает его без изменений. Клиент обязан убедиться, что принятое с ответом значение ''reqId'' равно тому, что было отослано с запросом. ''**cmd**'' - код команды. ''**body**'' - тело запроса произвольной длины, включая нулевую. ''**crc**'' - контрольная сумма CRC32, расчитанная по блоку **в квадратных скобках** (от ''reqId'' до ''body'' включительно). \\ \\ \\ Далее в описании команд для краткости изложения не указываются ''size'', ''header'', ''reqId'' и ''footer''. Направление сообщения обозначается следующим образом: **''request''** - запрос от клиента к серверу **''answer''** - ответ от сервера клиенту. Все строковые значения используют кодировку UTF-8. ===== 01 - INIT ===== Инициализация списка тегов. request: 0x01 flen#1 filter#flen clen#1 client#clen flags#2 answer: 0x81 listsize#3 **''flen''** - длина строки filter в байтах. ''**filter**'' - regex-фильтр для формирования списка тегов. Если пустая строка, то выбрать все теги. ''**clen**'' - длина строки описания клиента в байтах. ''**client**'' - любое текстовое описание клиента (не обязательное). ''**listsize**'' - количество выбранных тегов от 0 до 0xFFFFFF (16777215). ''**flags**'' - битовые параметры:\\ * ''b0'' - клиент поддерживает передачу описания тегов. Если b0 не установлен, то описания тегов в ответе команды ''LIST'' будут пустыми строками. * ''b1'' - клиент поддерживает передачу статусов тегов в ответе команды ''READ''. * ''b2'' - клиент требует не включать в список теги с флагом ''external'' . * ''b3'' - клиент требует включить в список теги с флагом ''hidden'' . ===== 02 - LIST ===== Запрос списка тегов. request: 0x02 index#3 answer: 0x82 index#3 quantity#3 next#3 [type#1 nlen#1 tagname#nlen dlen#1 descr#dlen] [...] ''**index**'' - стартовый индекс запроса списка ''**quantity**'' - количество переданных тегов ''**next**'' - индекс для следующего запроса, если не все данные были переданы в текущем сообщении. Если равно нулю, то дальнейшие запросы не требуются. **''nlen''** - длина строки имени тега в байтах **''tagname''** - имя тега. **''dlen''** - длина строки описания тега в байтах. **''descr''** - описание тега. **''type''** - тип данных: 1 - bool 2 - int32 3 - int64 4 - double 5 - string ===== 03 - UPDATE ===== Запрос произошедших изменений. request: 0x03 answer: 0x83 quantity#3 next#3 liststate#1 **''quantity''** - количество тегов, значение которых изменилось и их можно считать командой READ. ''**next**'' - индекс первого тега, значение которого изменилось. **''liststate''** - ''00'' - список тегов не изменился, ''FF'' - список тегов изменился, клиенту следует выполнить команды INIT и LIST. ===== 04 - READ ===== Чтение значений. request: 0x04 index#3 answer: 0x84 index#3 quantity#3 next#3 [data] [...] ''**index**'' - индекс тега для первого значения ''**quantity**'' - количество переданных тегов ''**next**'' - индекс для следующего запроса, если не все данные были переданы в текущем сообщении. Если равно нулю, то дальнейшие запросы не требуются. **''data''** - блок данных, содержащий один из вариантов: * индекс тега для значения в следующем блоке данных, * значение и статус тега. Индекс: 0xFE index#2 - индекс тега следующего значения (только для тегов с индексами < 65536) 0xFF index#3 - индекс тега следующего значения Значения краткие: 0xF0 - bool false, int32/64 0 0xF1 - bool true, int32/64 1 0xF2 byte#1 - int32/64 byte, 0..0xFF 0xF3 word#2 - int32/64 word, 0..0xFFFF Значения полные: 0xF8 int32#4 - integer 0xF9 int64#8 - long 0xFA double#8 - double 0xFB len#2 string#len - строка, где len - длина строки в байтах Краткие значения помогают существенно сократить трафик, поэтому отправителю сообщения следует по возможности использовать краткую нотацию. **Статус тега**\\ Совместно со значением тега сервер может отдавать клиенту информацию о статусе (актуальности значения) тега. Для этого при инициализации командой ''INIT'' клиент должен передать параметр ''flags.b1 = 1''. Информация о статусе тега содержится в ''b4'' первого байта блока данных: b4 = 1 - Good, значение тега актуально b4 = 0 - Bad, значение тега не актуально Статус тега передается только от сервера к клиенту. ===== 05 - WRITE ===== Запись значений. request: 0x05 index#3 quantity#3 [data] [...] answer: 0x85 ''**index**'' - индекс тега для первого значения ''**quantity**'' - количество переданных тегов ''**data**'' - аналогично READ. ===== 06 - CRC ===== Чтение CRC32 по всем текущим значениям тегов, зафиксированным командой UPDATE. request: 0x06 answer: 0x86 crc#4 ''**crc**'' - контрольная сумма CRC32 всех значений тегов Подсчет CRC выполняется последовательно по списку тегов, сформированному командой INIT. Значение каждого тега представляется в виде байтового массива, длина которого зависит от типа тега: * BOOL - 1 байт * INT - 4 байт * LONG - 8 байт * DOUBLE - 8 байт * STRING - 4 байт Старший байт значения суммируется первым. Для тегов типа STRING используется значение хэш-кода, расчитываемого следующим образом (java): public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; } При расчете хэш-кода используются значения символов строки в кодировке [[https://www.utf8-chartable.de/unicode-utf8-table.pl?start=1024|UNICODE]]. ===== 07 - AUTH_INIT ===== Инициализация процесса аутентификации. request: 0x07 klen#2 keyname#klen answer: 0x87 status#1 nlen#2 nonce#nlen **''klen''** - длина строки keyname в байтах. **''nlen''** - длина строки nonce в байтах. ''**keyname**'' - имя файла приватного ключа, который будет использоваться на стороне клиента. ''**status**'' - статус выполнения инициализации аутентификационного процесса на сервере: 0 - OK - процесс начат нормально, сформировано и зашифровано nonce (random-строка) 1 - FAILED - процесс не начат, в nonce находится описание проблемы 2 - DISABLED - аутентификация на сервере отключена, вход свободный ''**nonce**'' - зашифрованное публичным ключем сгенерированное значение (строка случайных символов) Сервер должен иметь у себя соответствующий публичный ключ с именем файла ''keyname.pub''. После того, как клиент получил зашифрованный nonce, он должен расшифровать его своим приватным ключем ''keyname'' и отправить на сервер последующей командой AUTH_SUBMIT. ===== 08 - AUTH_SUBMIT ===== Завершение процесса аутентификации. request: 0x08 nlen#2 nonce#nlen answer: 0x88 status#1 **''nlen''** - длина строки nonce в байтах. ''**nonce**'' - расшифрованная приватным ключем значение nonce, полученное от сервера командой AUTH_INIT ''**status**'' - результат аутентификации: 0 - ACCEPTED - успешно 0xFF - DENIED - отказано ===== FE - UNAUTHENTICATED ===== Ответ на любую команду, если сервер требует аутентификации, и она не выполнена. Не распространяется на команды AUTH_*. answer: 0xFE ===== FF - UNKNOWN ===== Ответ на неизвестную серверу команду. answer: 0xFF