# Коммуникационный протокол JRBusTcp

Примеры сообщений

## Формат сообщения

Максимально допустимый размер сообщения составляет 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;
    }

При расчете хэш-кода используются значения символов строки в кодировке 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