Есть у компании Болид прибор, называется С2000-ПП . Преобразователь протоколов
https://bolid.ru/production/orion/signal-transfer/s2000-pp.html
С одной стороны прибор работает внутри системы ИСО Орион (интегрированной системы охраны и пожарной сигнализации), с другой стороны из него можно вычитать события используя протокол Modbus.
Алгоритмов опроса данного прибора может быть множество,
предлагаемый ниже алгоритм вычитывания событий для версии прошивки прибора 1.31 следующий:
1) по таймеру
- запрос номера самого нового события [регистр 46160]
* сохранить в EvNew
- запрос номера самого старого события [регистр 46161]
* сохранить в EvOld
- если (EvNew + EvOld) = 0, то:
- выходим
иначе:
- запрос самого старого события: [регистр 46264]
- если в ответе ошибка, то:
- выходим
иначе:
- парсинг (28 байт ответа)
- отметить событие как прочитанное [регистр 46163]
* записать EvOld
После чего данные события стоит разобрать и правильно интерпретировать.
для версии прошивки прибора 1.31 предлагаю следующий алгоритм:
- если общая длина ответа меньше 3, то
- выходим
- байт 0 – адрес slave (Addr)
- байт 1 – функция модбас (Func)
- байт 2 – счётчик байт (Count)
- если общая длина ответа меньше 5, то
- выходим
- байты 3,4 – номер события
- собираем из них переменную (Index) = UInt16(3 и 4)
- если Index = 0, то
- выходим
- если общая длина ответа меньше 6, то
- выходим
- байт 5 – длина описания события (Len)
- если Len < 4, то
- выходим
- байт 6 – код события (Code)
n = 7
НАЧАЛО ЦИКЛА
- если n >= Len, то
- выходим
- байт n – код типа поля (Codetype)
- в зависимости от Codetype разбираем каждый блок
(стандартная реализация через case/switch, тут для универсальности, через условия)
- если Codetype = 1, то: (порядковый номер пользователя в базе данных С2000-ПП)
- Codelen := ОТВЕТ[n + 1];
- если Codelen < 2, то
- выходим
- иначе:
- User := UInt16(ОТВЕТ[n + 3], ОТВЕТ[n + 2])
- увеличиваем n на (Codelen + 2)
- если Codetype = 2, то: (раздел – номер раздела Modbus)
- Codelen := ОТВЕТ[n + 1];
- если Codelen < 2, то
- выходим
- иначе:
- Razdel := UInt16(ОТВЕТ[n + 3], ОТВЕТ[n + 2])
- увеличиваем n на (Codelen + 2)
- если Codetype = 3, то: (зона – порядковый номер зоны Modbus)
- Codelen := ОТВЕТ[n + 1];
- если Codelen < 2, то
- выходим
- иначе:
- Zone := UInt16(ОТВЕТ[n + 3], ОТВЕТ[n + 2])
- увеличиваем n на (Codelen + 2)
- если Codetype = 5, то: (реле – порядковый номер реле Modbus)
- Codelen := ОТВЕТ[n + 1];
- если Codelen < 2, то
- выходим
- иначе:
- RelayNum := UInt16(ОТВЕТ[n + 3], ОТВЕТ[n + 2])
- увеличиваем n на (Codelen + 2)
- если Codetype = 7, то: (реле – состояние реле)
- Codelen := ОТВЕТ[n + 1];
- если Codelen < 2, то
- выходим
- иначе:
- RelayStatus := UInt16(ОТВЕТ[n + 3], ОТВЕТ[n + 2])
- увеличиваем n на (Codelen + 2)
- если Codetype = 11, то: (время и дата – часы, минуты, секунды, день, месяц, год)
- Codelen := ОТВЕТ[n + 1];
- если Codelen < 6, то
- выходим
- иначе:
- Hour := ОТВЕТ[n + 2] and $3F;
- Min := ОТВЕТ[n + 3];
- Sec := ОТВЕТ[n + 4];
- Day := ОТВЕТ[n + 5];
- Month := ОТВЕТ[n + 6];
- Year := ОТВЕТ[n + 7];
- если четвёртый бит Hour = 0, то:
- ВРЕМЯ = Hour:Min:Sec
- если третий бит Hour = 0, то:
- ДАТА = Day.Month.Year
- увеличиваем n на (Codelen + 8)
- если Codetype = 24, то: (id раздела – идентификатор раздела Modbus)
- Codelen := ОТВЕТ[n + 1];
- если Codelen < 2, то
- выходим
- иначе:
- ID := UInt16(ОТВЕТ[n + 3], ОТВЕТ[n + 2])
- увеличиваем n на (Codelen + 2)
КОНЕЦ ЦИКЛА
Ну и в конце Собираем текст события из кусочков (в нужном порядке):
- Addr, Index, Code, User, Razdel, Zone, RelayNum, RelayStatus, ВРЕМЯ, ДАТА, ID.
По итогу, событие прочитано и его можно описать в удобочитаемом виде.