Интеграция
This page is not translated yet. The text below is the Russian source. Facts are unchanged.
Страница описывает, как внешние системы — SCADA, диспетчеризация, облако, панели,
скрипты — обмениваются данными с vPLC на СА-02м. Как писать саму программу ПЛК —
программирование; ключи /etc/vplc.conf —
конфигурация; работа в веб-интерфейсе установки —
веб и настройка установки.
Обзор интерфейсов
Заголовок раздела «Обзор интерфейсов»| Интерфейс | Кто сервер | Порт по умолчанию | Включение | Аутентификация |
|---|---|---|---|---|
| REST API рантайма, WebSocket, SSE | служба vplc |
1234 (rest_port) |
всегда | токен Bearer для записи (api_token), см. ниже |
| Веб-интерфейс установки и его API | служба vplc-plant-ui (server.py) |
8088 | отдельная служба | через маршрут панели СА-02м /vplc/; напрямую — без пароля |
| Modbus TCP slave | рантайм | 502 (modbus_slave_port) |
modbus_slave_enabled |
нет |
| Modbus master (TCP/RTU) | рантайм — клиент | — | таблица modbus_devices проекта |
— |
| MQTT | рантайм — клиент брокера | брокер 127.0.0.1:1883 |
mqtt_enabled |
mqtt_username / mqtt_password |
| OPC UA | рантайм | 4840 (opcua_port) |
opcua_enabled |
нет (Security None, анонимный доступ) |
Порты на СА-02м. Порт 502 занят ведомым Modbus TCP шины, 4840 — зарезервирован, 4841 использует OPC UA-шлюз веб-панели. Для vPLC используйте
"modbus_slave_port": 1502и"opcua_port": 4842(или оставьте их выключенными).
REST API рантайма
Заголовок раздела «REST API рантайма»Базовый адрес: http://<IP-адрес СА-02м>:1234. Тела запросов и ответов — JSON
(кроме загрузки файлов). Ответы содержат Access-Control-Allow-Origin: *; ошибки —
{"error":"…"} с кодом 4xx/5xx.
Токен API
Заголовок раздела «Токен API»Если в /etc/vplc.conf задан api_token, рантайм требует заголовок
Authorization: Bearer <токен>| Запрос | Нужен токен |
|---|---|
все POST (запись, загрузка, пуск/останов, конфигурация) |
да |
GET /api/config, GET /api/logs |
да |
остальные GET (/api/status, /api/variables, …), встроенная страница / |
нет |
WebSocket /ws/…, SSE /api/events/… |
не проверяется |
- Без токена или с неверным —
401 {"error":"unauthorized"}. GET /api/configвозвращаетapi_tokenиmqtt_passwordмаской***.- Смена токена (
POST /api/config) вступает в силу после перезапуска службы. - Канал
/ws/variablesпринимает запись переменных без токена, а веб-интерфейс установки (порт 8088) обращается к рантайму без токена — при заданномapi_tokenего запись в рантайм будет отклонена. Если порт 1234 доступен из сети объекта, ограничьте доступ к нему межсетевым экраном.
Маршруты
Заголовок раздела «Маршруты»| Метод | Путь | Назначение |
|---|---|---|
GET |
/ |
встроенная страница: состояние, переменные, журнал |
GET |
/api/status |
состояние скана, статистика, версия |
GET |
/api/config |
действующая конфигурация (секреты маскируются) |
POST |
/api/config |
изменить конфигурацию (JSON, частично) |
GET |
/api/program/info |
загруженная программа |
POST |
/api/program/upload |
загрузить .so (двоичное тело) |
POST |
/api/project/upload |
загрузить проект .vplc (двоичное тело, до 8 МиБ) |
POST |
/api/program/reload |
перезагрузить программу с диска (program_path) |
POST |
/api/start |
запустить скан |
POST |
/api/stop |
остановить скан |
POST |
/api/restart |
перезапустить скан |
GET |
/api/variables |
переменные (все или по фильтру) |
POST |
/api/variables |
переменные по фильтру в теле запроса |
POST |
/api/variables/<имя> |
записать переменную |
GET |
/api/logs?limit=N |
последние записи журнала (по умолчанию 200, максимум 2000) |
POST |
/api/dump/save |
сохранить значения переменных в дамп |
POST |
/api/dump/restore |
восстановить значения из дампа |
GET |
/api/mqtt/status |
состояние клиента MQTT |
GET |
/api/modbus/status |
состояние Modbus slave и опрашиваемых устройств |
POST |
/api/scan/stats/reset |
сбросить статистику скана |
WS |
/ws/variables |
поток значений переменных; запись переменных |
WS |
/ws/logs |
поток записей журнала |
GET |
/api/events/variables |
SSE: значения переменных |
GET |
/api/events/status |
SSE: состояние (около 1 раза в секунду) |
В примерах ниже:
H=http://<IP-адрес СА-02м>:1234AUTH='Authorization: Bearer <токен>' # не нужен, если api_token пустСостояние
Заголовок раздела «Состояние»curl "$H/api/status"{"state":"RUNNING","program":"Fan","cycle_ms":100,"cycle_count":1520, "last_cycle_ms":0.02,"max_cycle_ms":0.35,"overrun_count":0,"p95_cycle_ms":0.04, "stats_window":1000,"stats_cycles":1515,"stats_age_s":151.6, "cycle_hist":{…},"scan_profile":{…},"scan_sched":"SCHED_OTHER", "var_count":8,"version": "<версия сборки>"}| Поле | Смысл |
|---|---|
state |
IDLE (нет программы), STOPPED, RUNNING, ERROR |
program |
имя программы |
cycle_ms |
базовый период скана, мс |
cycle_count |
сканов с запуска |
last_cycle_ms, max_cycle_ms, p95_cycle_ms |
длительность скана: последняя, максимум и 95-й процентиль (окно до 1000 сканов) |
overrun_count |
сколько раз скан не уложился в период |
stats_cycles, stats_age_s |
сканов и секунд с последнего сброса статистики |
cycle_hist, scan_profile, scan_sched |
гистограмма, профиль самого долгого скана, политика потока скана |
var_count, version |
число видимых переменных, версия рантайма |
curl "$H/api/program/info"# {"loaded":true,"name":"Fan","path":"/var/lib/vplc/program.so","var_count":8}Управление программой
Заголовок раздела «Управление программой»curl -X POST "$H/api/project/upload" -H "$AUTH" --data-binary @Fan.vplccurl -X POST "$H/api/program/upload" -H "$AUTH" --data-binary @Fan.so # программа загружена, скан STOPPEDcurl -X POST "$H/api/start" -H "$AUTH" # {"ok":true,"state":"RUNNING"}curl -X POST "$H/api/stop" -H "$AUTH" # {"ok":true,"state":"STOPPED"}curl -X POST "$H/api/restart" -H "$AUTH"curl -X POST "$H/api/program/reload" -H "$AUTH" # {"ok":true,"program":"Fan","state":"RUNNING"}| Код | Когда |
|---|---|
409 {"error":"already running"} |
start при работающем скане |
409 {"error":"not running"} |
stop при остановленном |
409 {"error":"no program loaded"} |
start/restart без программы |
422 |
.so не загрузился |
Подробности загрузки — программирование.
Чтение переменных
Заголовок раздела «Чтение переменных»curl "$H/api/variables"[{"name":"RoomTemp","type":"REAL","dir":"INPUT","value":21.5,"comment":""}, {"name":"FanRun","type":"BOOL","dir":"OUTPUT","value":false,"comment":""}]dir — INPUT, OUTPUT, INOUT, MEMORY. TIME отдаётся целыми секундами.
Фильтр — чтобы не читать тысячи переменных:
| Параметр | Смысл |
|---|---|
names=a,b |
точные имена (можно повторять параметр) |
prefix=Fan |
начало имени |
glob=Room* |
маска: *, ?, […] |
Критерии объединяются по ИЛИ; порядок ответа — порядок переменных в программе.
Ответ с фильтром содержит заголовки X-VPLC-Filter: 1 и X-VPLC-Missing: N (сколько
имён из names не найдено).
curl -i "$H/api/variables?names=StateCode,AlarmCount&prefix=StatusMask"
# длинные списки — телом POST (нужен токен, если он задан)curl -X POST "$H/api/variables" -H "$AUTH" -H 'Content-Type: application/json' \ -d '{"names":["StateCode","PlantType"],"glob":["Alarm*"]}'# или просто массив имён: -d '["StateCode","PlantType"]'Неверное тело — 400 {"error":"bad filter body: …"}.
Запись переменных
Заголовок раздела «Запись переменных»curl -X POST "$H/api/variables/TempOn" -H "$AUTH" \ -H 'Content-Type: application/json' -d '{"value": 27.5}'# {"ok":true}| Тип | value |
|---|---|
BOOL |
true / 1 — ИСТИНА; всё остальное — ЛОЖЬ |
целые, REAL, LREAL |
число |
TIME |
целые секунды |
Нет такой переменной или значение не разбирается — 404 {"error":"variable not found or type error"}. Значение действует со следующего скана; если программа сама
присваивает переменную, запись будет перезаписана.
Записанные значения сохраняются в дамп только по POST /api/dump/save (и при штатной
остановке службы):
curl -X POST "$H/api/dump/save" -H "$AUTH" # {"ok":true}Конфигурация
Заголовок раздела «Конфигурация»curl -H "$AUTH" "$H/api/config"curl -X POST "$H/api/config" -H "$AUTH" -H 'Content-Type: application/json' \ -d '{"log_level":"debug","cycle_ms":50}'# {"ok":true,"restart_required":false}Сразу применяются cycle_ms, log_level, log_ring_size, scan_rt_priority,
opcua_publish_ms. Изменение rest_port, api_token, project_path,
mqtt_enabled, mqtt_host, mqtt_port, ключей Modbus slave, opcua_enabled,
opcua_port, scan_mlock записывается в /etc/vplc.conf, но требует перезапуска
службы — ответ {"ok":true,"restart_required":true,"note":"…"}.
Состояние обмена
Заголовок раздела «Состояние обмена»curl "$H/api/mqtt/status"# {"connected":true,"broker":"127.0.0.1:1883","topics":3,"rx":120,"tx":45}
curl "$H/api/modbus/status"# {"slave":{"enabled":true,"listening":true,"port":1502,"mw_size":10240,"clients":1,# "map_mode":"mp02","last_error":"","mp02_rw_ranges":"4000-4015,…"},# "devices":[{"name":"Pump1","connected":true,"errors":0,"polls":5321,"last_error":""}]}topics — число входных привязок MQTT. slave.listening = false при включённом
slave — порт занят (причина в last_error).
curl -H "$AUTH" "$H/api/logs?limit=50"# [{"ts":"21:28:04.356","level":"INFO","module":"Main","msg":"Program uploaded and loaded: Fan"}, …]WebSocket и SSE
Заголовок раздела «WebSocket и SSE»| Канал | Что приходит |
|---|---|
ws://…:1234/ws/variables |
после скана — JSON-массив всех переменных (формат /api/variables без comment); при медленном клиенте отправляется только последний снимок |
ws://…:1234/ws/logs |
записи журнала уровня INFO и выше |
GET /api/events/variables |
SSE, событие variables, тот же массив |
GET /api/events/status |
SSE, событие status, тело как /api/status, около 1 раза в секунду |
Запись через WebSocket — сообщение в /ws/variables:
{"name":"TempOn","value":27.5}curl -N "$H/api/events/status"# event: status# data: {"state":"RUNNING","program":"Fan", …}Пока нет ни одного подписчика WebSocket/SSE на переменные, рантайм не тратит время скана на формирование снимка.
API веб-интерфейса установки (порт 8088)
Заголовок раздела «API веб-интерфейса установки (порт 8088)»Служба vplc-plant-ui (web/plant_ui/server.py) отдаёт страницы установки и
API, совместимое с REST прибора MP-02, поверх REST рантайма.
python3 server.py --port 8088 --vplc http://127.0.0.1:1234 \ [--host 0.0.0.0] [--plant-config /var/lib/vplc/plant_config.json] \ [--bindings bindings.json] [--contract vars_contract.json] [--mqtt 127.0.0.1:1883]- Через панель СА-02м (маршрут
/vplc/, заголовокX-Forwarded-Prefix): вход проверяет панель. КаждыйPOSTдолжен нести заголовокX-VPLC-Request: 1, иначе403 {"error":"csrf"}. Рекомендуемый режим: служба слушает только--host 127.0.0.1. - Напрямую на порт 8088 локальная установка доступна без пароля (уровень «Сервис»). Не открывайте 8088 в сеть объекта.
- Запись во внешние установки (Carel, MP-02 по TCP) при прямом доступе требует живой
сессии панели СА-02м (cookie проверяется у панели), иначе
401 {"ok":false,"reason":"auth"|"login"|"panel_unreachable"}.
В примерах: P=http://127.0.0.1:8088.
Данные и параметры локальной программы
Заголовок раздела «Данные и параметры локальной программы»| Метод и путь | Назначение |
|---|---|
GET /api/plant/snapshot[?names=a,b] |
плоский объект «имя → значение» всех (или указанных) переменных |
POST /api/plant/set {"name","value"} |
запись: параметры ahu_* (с проверкой диапазона и сохранением дампа); служебные PlantType (1…5), UnitType (0…2), RunReq, SimEnable, Cmd*, EcoReq, ServiceReq, ManualReq, SeasonReq — без сохранения дампа |
POST /api/plant/cmd {"cmd"} |
импульс: start, stop, ack, reset |
GET /api/params/schema[?group=N&lang=en] |
описание параметров (тип, пределы, единицы, группа) |
GET /api/params[?group=N] |
значения параметров по ключам ahu.<группа>.<имя> |
PUT /api/params {"values":{…}} |
запись параметров; ответ {ok, applied, failed, errors[], job} (200 или 207 при частичном отказе) |
POST /api/params/defaults {"group":N} |
заводские значения группы (255 — всех) |
POST /api/command {"cmd", "value"?, "arg"?} |
start, stop, ack, reset, mode, setpoint, season, eco, service, manual, uv_life_reset, param, param_defaults, param_save |
GET /api/status, /api/info, /api/time |
состояние в формате MP-02, паспорт, время |
GET /api/tags, /api/tags/values?ids=… |
словарь тегов и значения с кодом качества |
GET /api/alarms[?state=active|unacked|latched|all] |
аварии локальной установки |
POST /api/alarms/ack, /api/alarms/reset |
{"bit":N} или {"all":true} |
GET /api/alarms/config, PUT /api/alarms/config |
класс, реакция, задержки аварий (задержки — записываемые параметры) |
GET /api/events, POST /api/events/clear |
журнал событий веб-сервера |
GET /api/io/bindings |
привязки bindings.json |
GET /api/iomap/status, POST /api/iomap/apply, POST /api/iomap/autobind |
применение привязки I/O в ПЛК (см. Привязка I/O) |
curl "$P/api/plant/snapshot?names=PlantType,StateCode,rSupplyTemp"# {"PlantType":1,"StateCode":3,"rSupplyTemp":19.8}
curl -X PUT "$P/api/params" -H 'Content-Type: application/json' \ -d '{"values":{"ahu.sp.temp":21.0}}'# {"ok":true,"applied":1,"failed":0,"errors":[],"job":…}
curl -X POST "$P/api/command" -H 'Content-Type: application/json' -d '{"cmd":"start"}'После записи параметров сервер сам вызывает POST /api/dump/save рантайма.
Объекты, энергия, устройства
Заголовок раздела «Объекты, энергия, устройства»| Метод и путь | Назначение |
|---|---|
GET /api/site, PUT /api/site |
название объекта, контакты, тема, наличие систем |
GET /api/units |
список установок с состоянием, сводкой, энергией, расписанием |
POST /api/units |
добавить установку (внешнюю — {kind, source:{device_id}, name?}; локальную — {kind:"local-itp"|"local-water"|"local-light"}) |
PUT /api/units {"order":[…]} |
порядок установок |
GET / DELETE /api/units/<id> |
одна установка / удалить (кроме local) |
GET / PUT /api/units/<id>/settings |
имя, группа, состав, энергия, расписание, подписи |
PUT /api/units/<id>/params |
параметры локальной системы своего типа (itp.*, water.*, light.*, ahu.*) |
GET /api/units/<id>/snapshot |
значения в именах программы + _meta |
POST /api/units/<id>/command {cmd, value?, arg?, wait?} |
команда (см. Внешние установки) |
GET /api/units/<id>/alarms |
аварии установки |
GET / PUT /api/units/<id>/schedule |
недельное расписание |
GET /api/units/summary |
сводная таблица |
GET /api/alarms?all=1 |
аварии всех установок |
GET /api/io/devices[?type=…] |
устройства на шине MQTT СА-02м |
GET /api/energy/<id>/summary[?range=1h|6h|24h|7d|30d|mtd|month] |
энергия и стоимость |
GET /api/energy/<id>/series?period=day|week|month |
ряд кВт·ч по часам/дням |
Modbus TCP slave
Заголовок раздела «Modbus TCP slave»Включение (/etc/vplc.conf или настройки проекта; применяется после перезапуска
службы):
{"modbus_slave_enabled": true, "modbus_slave_port": 1502, "modbus_map_mode": "packed", "modbus_mw_size": 1024}Сервер принимает несколько клиентов одновременно. Адреса — с нуля, идентификатор устройства (unit id) не проверяется.
Режим packed (по умолчанию)
Заголовок раздела «Режим packed (по умолчанию)»| Область | Функции | Адреса | Содержимое |
|---|---|---|---|
| Holding registers | 3, 6, 16 | 0…1023 | образ программы PUT_4X/GET_4X; адрес 1024 и выше — исключение 02 |
| Input registers | 4 | с 0 | все видимые переменные не-BOOL подряд в порядке объявления |
| Coils | 1, 5, 15 | с 0 | видимые BOOL подряд; запись — только в VAR_OUTPUT/VAR_IN_OUT |
| Discrete inputs | 2 | с 0 | те же BOOL, только чтение |
Упаковка: переменная занимает ⌈размер/2⌉ регистров, младшее слово по меньшему
адресу, младший байт — в младшем байте регистра. Пример для программы из
программирование:
| Input register | Переменная | Coil / DI | Переменная | |
|---|---|---|---|---|
| 0–1 | RoomTemp (REAL) |
0 | RoomTemp_q |
|
| 2–3 | FanSpeed (REAL) |
1 | FanRun |
|
| 4–5 | TempOn (REAL) |
2 | Request |
|
| 6–7 | TempOff (REAL) |
|||
| 8–9 | Starts (UDINT) |
Адреса сдвигаются при добавлении переменных — для стабильной карты выкладывайте
значения в образ PUT_4X по фиксированным адресам, а уставки принимайте через
GET_4X. В режиме packed оставляйте modbus_mw_size = 1024 (значение по
умолчанию). [не проверено: работа режима packed при modbus_mw_size больше 1024]
Режим mp02 — карта регистров прибора MP-02
Заголовок раздела «Режим mp02 — карта регистров прибора MP-02»Режим для SCADA, уже настроенных на контроллер MP-02: программа universal
публикует данные по тем же адресам.
{"modbus_slave_enabled": true, "modbus_slave_port": 1502, "modbus_map_mode": "mp02", "modbus_mw_size": 10240}- Образ — 9216…10240 регистров (
modbus_mw_sizeприжимается к этим границам); функции 3/4 читают одни и те же адреса; запись — функции 6, 16, 22, 23. - Float, u32, u64 — старшее слово по меньшему адресу.
- Запись SCADA попадает в программу в начале следующего скана; программа проверяет значение и, если оно недопустимо, возвращает регистр к прежнему значению (ответ на запись при этом успешный).
- Защиты конфигурации (ключ в регистре 180) и авторизации источника нет: параметры может записать любой клиент.
Основные адреса (программа вентиляции, PlantType = 1):
| Адрес | Тип | Доступ | Содержимое |
|---|---|---|---|
| 20 | u16 | R | id программы = PlantType |
| 100 | u16 | RW | режим: 0 выкл, 1 ручной, 2 авто |
| 101 | u16 | RW | код команды (ниже); аргументы 102 (u16) и 103–104 (float) пишутся до кода |
| 105 | i16 | R | результат команды: 0, −2 диапазон, −4 нет такого, −10 не поддержано |
| 190–193 | i16 ×10 | RW | уставки: T притока, влажность, скорость в ручном, режим скорости |
| 1000–1003 | u64 | R | маска аварий «активна или защёлкнута» (1000 = биты 63…48) |
| 1004–1067 | u16 ×64 | R | состояние аварии: 0 нет, 1 активна, 2 защёлкнута |
| 4000–4093 | float | R | величины (температуры, давления, выходы) |
| 4100–4146 | i16 | R | те же величины ×10 |
| 4150–4156 | u16 | R | упакованные DI (80 бит) и DO (32 бита) |
| 4157 / 4158 / 4159 | u16 | R | состояние / режим / зима |
| 4160–4191 | u32 | R | наработка узлов, ч и мин |
| 4200 | u16 | R | код статуса |
| 4201–4204 | u64 | R | маска активных аварий |
| 4205–4206 | u32 | R | маска статуса |
| 4207–4209 | u16 | R | число аварий, худший класс, худшая реакция |
| 4210–4241 | u16 ×32 | R | коды активных аварий (код = бит + 1) |
| 4250–4252 | — | R | счётчик сканов, такт скана, мс |
| 4260–4299 | — | R | расширение vPLC: энергоучёт, расписание, маски аварий, УФ-стерилизатор, фильтр 3 |
| 4300 + id×2 | float | RW | параметры (id — номер параметра в контракте); время — в секундах (у параметров с шагом в целый час — в часах) |
Запись в 0–99, 1000–1067 и 4000–4299 (кроме командных окон ниже) — исключение 02.
Параметры (4300 + id×2). Функция 16 на оба регистра записывает float целиком.
Функция 6 в старший, затем в младший — то же. Одиночная функция 6 в младший
регистр записывает целое значение (удобно для BOOL и перечислений). Номер id
(gid) и адрес (modbus_addr) каждого параметра — в контракте программы
(vars_contract.json), например ahu.energy.meter — id 316, адрес 4932.
Катушки:
| Катушка | Запись | Чтение |
|---|---|---|
| 0 / 1 | 1 — пуск / стоп (импульс) | 0 |
| 2 / 3 | 1 — квитировать все / сбросить защёлки | 0 |
| 4 / 5 / 6 | 1/0 — эко / сервис / ручной | состояние |
| 7 | 1 — сохранить параметры | 0 |
| 8 | 1 — перезагрузка (не исполняется) | 0 |
| 16 | 1 — пуск, 0 — стоп («Работа») | RunReq |
Discrete inputs: 0…63 — биты активных аварий, 64…95 — биты статуса.
Команды (регистр 101): 1 пуск, 2 стоп, 3 режим (arg 0…2), 4 квитировать (arg 255 — все), 5 сброс, 6 записать параметр (arg = id, значение — 103–104 в единицах хранения, время — в мс), 8 сохранить параметры, 23 уставка (arg 0…3), 24 ручной (arg 0/1), 25 сезон (arg 0 авто, 1 зима, 2 лето), 26 эко, 27 сервис; прочие — результат −10.
Командные окна внутри 4000…4299 — записываемые адреса других типов установок
(ключ modbus_mp02_rw_ranges, перезапуск службы):
Тип (PlantType) |
Записываемые регистры |
|---|---|
| вентиляция (1) | нет (команды — 100…105, 190…193, катушки) |
| освещение (2) | 4000+z слово зоны, 4140+z ручной уровень, 4160+z команда зоне (z = 0…15); 4190+g мастер группы (g = 0…3); 4256 сцена, 4257 паника, 4258 «погасить всё», 4273 общий мастер |
| ИТП (3) | 4260 команда, 4261 сброс, 4262/4263 уставка ГВС (float), 4264 сезон, 4265 дезинфекция, 4266 сброс аварий, 4270 подпитка, 4271 ночное снижение |
| вода (4) | 4150 режим, 4151 пуск/останов, 4190 квитирование, 4191 промывка, 4192 контрольный пуск пожарных, 4193 останов пожарной станции, 4194…4199 ручные команды насосам 1…6 |
| холод (5) | 4150 режим, 4151 пуск, 4155 нагрев, 4158 эко, 4159 вторая уставка, 4169 разрешение принудительной производительности, 4190…4195 квитирование, уставка, производительность, оттайка, задание и разрешение BMS |
По умолчанию открыто объединение всех окон
(4000-4015,4140-4155,4158-4175,4190-4199,4256-4258,4260-4266,4270-4271,4273).
Для объекта с одним типом сузьте набор, например для ИТП:
"modbus_mp02_rw_ranges": "4260-4266,4270-4271"; "none" — всё окно только чтение.
Действующий набор — GET /api/modbus/status → slave.mp02_rw_ranges.
Полная карта (все 94 величины, 270 параметров вентиляции и расширение vPLC) —
документ MODBUS_MP02.md и контракты apps/universal/contract/*.json в дереве репозитория vPLC. Отдельной подписанной карты регистров в этой вики нет.
Modbus master
Заголовок раздела «Modbus master»Рантайм сам опрашивает устройства из таблицы modbus_devices проекта .vplc
(как объявлять переменные и шаблоны устройств — программирование).
| Поле | Смысл |
|---|---|
name |
имя устройства — префикс переменных <name>_IW<адрес>, <name>_MW<адрес> |
mode |
tcp или rtu |
host, port |
адрес и порт (TCP) |
serial, baud |
порт и скорость (RTU; формат кадра 8N1) |
unit_id |
адрес устройства |
read_start, read_count |
блок чтения (функция 3, holding) |
write_start, write_count |
блок записи (функция 16) |
template |
шаблон устройства (необязательно) |
Поведение:
- Опрос идёт в отдельном потоке и не задерживает скан. Проход по всем устройствам — каждые 50 мс плюс время обмена.
- За проход: чтение блока
read_*(запросами до 125 регистров), затем запись всего блокаwrite_*текущими значениями переменных_MW(каждый проход, не только при изменении). - Таймаут ответа — 500 мс. Любая ошибка обмена разрывает соединение; повторное подключение — через 1 с, затем 2, 4 … до 30 с.
- Состояние —
GET /api/modbus/status→devices[](connected,errors,polls,last_error). - Список устройств читается при старте службы: после изменения
modbus_devicesперезапустите службуvplc.
На СА-02м порты RS-485, которые опрашивает мост Modbus → MQTT СА-02м, занимать нельзя: модули на этих портах читайте через MQTT (ниже). Мастер vPLC можно использовать для устройств Modbus TCP и для свободных последовательных портов.
Подключение
Заголовок раздела «Подключение»Ключ (/etc/vplc.conf) |
По умолчанию | Смысл |
|---|---|---|
mqtt_enabled |
false |
включить клиента |
mqtt_host, mqtt_port |
127.0.0.1, 1883 |
брокер (на СА-02м — локальный) |
mqtt_client_id |
vplc |
идентификатор и имя собственного дерева топиков (проект из VS Code — vplc-sa02m) |
mqtt_username, mqtt_password |
пусто | <логин> / <пароль>, если брокер требует |
mqtt_retain_outputs |
true |
retain для публикаций в собственное дерево |
mqtt_stale_ms |
125000 |
время устаревания входа, мс (не меньше 5000) |
mqtt_bridge_id |
sa02m-bridge |
устройство моста для признака BRIDGE_ok |
Таблица mqtt_brokers проекта перекрывает адрес брокера, client_id и учётные
данные. Клиент переподключается сам (интервал 5…30 с).
vPLC работает с деревом устройств моста Modbus → MQTT СА-02м:
/devices/<id>/controls/<канал> значение (публикует мост, retain)/devices/<id>/controls/<канал>/on команда записи (без retain)/devices/<id>/meta/error "" или "r" — устройство не отвечаетКарта mqtt_io_map
Заголовок раздела «Карта mqtt_io_map»| Столбец | Смысл |
|---|---|
topic |
топик канала /devices/<id>/controls/<канал> (для выхода — без /on) |
var_name |
переменная программы |
is_input |
1 — вход (топик → переменная), 0 — выход (переменная → топик) |
scale, offset |
вход: перем = сырое × scale + offset; выход: сырое = (перем − offset) / scale |
publish_suffix |
выход: суффикс топика записи; NULL — автоматически (см. ниже), '' — ровно topic |
safe_value |
выход: значение при останове скана |
on_stop |
выход: safe (по умолчанию при заданном safe_value) или hold |
stale_ms |
вход: собственное время устаревания (NULL — mqtt_stale_ms) |
quality_var |
вход: имя BOOL-переменной достоверности (NULL — <var_name>_q) |
Карта загружается вместе с проектом: POST /api/project/upload, затем загрузка или
POST /api/program/reload (перезапуск службы не нужен).
INSERT INTO mqtt_io_map (topic, var_name, is_input, scale, offset, safe_value, stale_ms, quality_var) VALUES('/devices/mr02m-COM2-5/controls/di_1', 'xFilterClog', 1, 1.0, 0.0, NULL, NULL, NULL),('/devices/mr02m-COM2-5/controls/ai_1', 'rSupplyTemp', 1, 1.0, 0.0, NULL, 30000, 'q_t_supply'),('/devices/mr02m-COM2-6/controls/do_1', 'yFanRun', 0, 1.0, 0.0, 'FALSE', NULL, NULL),('/devices/mr02m-COM2-7/controls/ao_1', 'yValvePct', 0, 0.1, 0.0, '0', NULL, NULL);Аналоговый выход МР-02м принимает 0…1000 (0…10,00 В): при переменной в процентах
scale = 0.1 (100 % → 1000), в вольтах — scale = 0.01.
- Значение разбирается по типу переменной:
BOOL—true/false/on/offили число (≠ 0 — ИСТИНА); целые — с округлением и насыщением;REAL— число с точкой. Невалидная строка не меняет переменную и снимает признак качества. - Значение пишется в переменную только при новом сообщении.
- Качество. Если в программе объявлена
BOOL-переменная<вход>_q(или имя изquality_var), рантайм держит её ИСТИНОЙ, пока: сообщение валидно и моложеstale_ms, у устройства и канала нетmeta/error = "r", брокер на связи. При плохом качестве значение входа держится — программа должна проверять_q. - Дополнительные признаки (если объявлены в программе как
BOOL):
| Переменная | ИСТИНА, когда |
|---|---|
<устройство>_ok |
брокер на связи и у устройства нет meta/error = "r"; символы кроме [A-Za-z0-9_] заменяются на _: mr02m-COM2-5 → mr02m_COM2_5_ok |
MQTT_ok |
рантайм подключён к брокеру |
BRIDGE_ok |
мост СА-02м на связи (/devices/<mqtt_bridge_id>/controls/connection = 1) |
- Публикация при изменении значения (и заново всех выходов после переподключения), QoS 1.
- Формат:
BOOL—1/0;REAL— до 6 значащих цифр;LREAL— до 10; целые — как есть. NaN и бесконечность не публикуются. - Топик записи: для чужого устройства
/devices/<id>/controls/<канал>рантайм сам добавляет/on; в собственное дерево/devices/<mqtt_client_id>/…— без суффикса. - Retain: в
…/on— никогда; в собственное дерево — поmqtt_retain_outputs. - Безопасное состояние. При
POST /api/stop, ошибке скана и штатной остановке службы каждый выход сsafe_value(иon_stop≠hold) публикуется этим значением. Загрузка программы безопасные значения не публикует. При аварийном завершении процесса срабатывает только LWT (meta/error = "r") — безопасное состояние поля обеспечивают правила СА-02м или сторожевой таймер модуля.
Собственное дерево рантайма
Заголовок раздела «Собственное дерево рантайма»/devices/<mqtt_client_id>/meta/name "vPLC (…)"/devices/<mqtt_client_id>/meta/error ""; при обрыве (LWT) — "r"/devices/<mqtt_client_id>/controls/connection "1"; "0" при штатной остановке/devices/<mqtt_client_id>/controls/runtime_state RUNNING | STOPPED | IDLE | ERROR/devices/<mqtt_client_id>/controls/<переменная>/on ← запись любой видимой переменной# состояние рантаймаmosquitto_sub -h 127.0.0.1 -v -t '/devices/vplc-sa02m/#'# записать уставкуmosquitto_pub -h 127.0.0.1 -t '/devices/vplc-sa02m/controls/TempOn/on' -m '27.5'Запись через …/<переменная>/on разбирается как в REST: для BOOL — 1 или true.
Публиковать переменную в собственное дерево — строка карты с is_input = 0 и топиком
/devices/<mqtt_client_id>/controls/<имя>. Автоматически все переменные не
публикуются.
{"opcua_enabled": true, "opcua_port": 4842, "opcua_name": "vPLC OPC UA Server", "opcua_publish_ms": 500}| Свойство | Значение |
|---|---|
| Конечная точка | opc.tcp://<IP-адрес СА-02м>:<opcua_port>/ (только IPv4) |
| Безопасность | Security None, анонимный доступ |
| Узлы | каждая видимая переменная — ns=2;s=<Имя> в папке Objects (пространство имён urn:vplc:variables) |
| Доступ | чтение и запись |
| Обновление значений | собственным потоком сервера раз в opcua_publish_ms (по умолчанию 500 мс, 0 — как можно чаще); меняется без перезапуска |
| Перезагрузка программы | дерево узлов перестраивается автоматически |
Типы узлов:
| ST | OPC UA | ST | OPC UA | |
|---|---|---|---|---|
BOOL |
Boolean | DINT |
Int32 | |
BYTE, USINT |
Byte | LINT |
Int64 | |
SINT |
SByte | UDINT, DWORD, TIME |
UInt32 | |
INT |
Int16 | ULINT, LWORD |
UInt64 | |
UINT, WORD |
UInt16 | REAL |
Float | |
LREAL |
Double |
Записывать нужно значением точно этого типа: запись Double в узел REAL
отклоняется (в журнале OPC UA write ignored … unsupported variant).
Сервер работает, если рантайм собран с библиотекой open62541; иначе при
opcua_enabled: true в журнале будет OPC UA support not compiled in.
from asyncua.sync import Client # pip install asyncuawith Client("opc.tcp://<IP-адрес СА-02м>:4842/") as c: node = c.get_node("ns=2;s=TempOn") print(node.read_value()) from asyncua import ua node.write_value(ua.DataValue(ua.Variant(27.5, ua.VariantType.Float)))Программа universal
Заголовок раздела «Программа universal»universal — готовая программа vPLC, повторяющая программы прибора MP-02 для пяти
типов установок. В одном program.so активен один тип — его задаёт переменная
PlantType; переключение — без пересборки.
Типы установок
Заголовок раздела «Типы установок»PlantType |
Установка | Вид в plant_config.json |
|---|---|---|
| 1 | вентиляция (ПВУ) — по умолчанию | local-ahu |
| 2 | освещение | local-light |
| 3 | ИТП / отопление | local-itp |
| 4 | водоснабжение и насосы | local-water |
| 5 | холодоснабжение | — |
Внутри вентиляции тип установки — параметр ahu.unit.unit_type (переменная UnitType):
0 приточная, 1 вытяжная, 2 приточно-вытяжная.
Выбор типа (любой способ), затем сохранение дампа:
# через веб установки: тип берётся из kind установки, дамп сохраняетсяcurl -X POST "$P/api/units/<id>/command" -H 'Content-Type: application/json' \ -d '{"cmd":"set_plant_type"}'
# или запись переменной (через веб установки или напрямую) и сохранение дампаcurl -X POST "$P/api/plant/set" -H 'Content-Type: application/json' \ -d '{"name":"PlantType","value":3}'curl -X POST "$H/api/dump/save" -H "$AUTH"POST /api/plant/set сохраняет дамп только при записи параметров ahu_*; после
записи служебных переменных (PlantType, SimEnable, RunReq …) вызовите
POST /api/dump/save рантайма, иначе после перезапуска вернётся прежнее значение.
Смена типа снимает «Работу» (RunReq) и обнуляет выходы прежнего типа. Окно Modbus
4000…4299 и параметры 4300+ занимает активный тип.
Модель объекта (SimEnable)
Заголовок раздела «Модель объекта (SimEnable)»По умолчанию SimEnable = TRUE: входы программы ведёт встроенная модель объекта,
аварии неполной карты I/O и потери связи не формируются. На объекте установите
SimEnable = FALSE и сохраните дамп:
curl -X POST "$P/api/plant/set" -H 'Content-Type: application/json' \ -d '{"name":"SimEnable","value":false}'curl -X POST "$H/api/dump/save" -H "$AUTH"Выходы, привязанные в карте MQTT, публикуются и при SimEnable = TRUE.
Параметры
Заголовок раздела «Параметры»| Где | Как называется |
|---|---|
контракт, веб, /api/params |
<тип>.<группа>.<имя>: ahu.sp.temp, itp.common.season_mode, water.station.pump_count, light.gen.zone_count, chl.unit.machine_type |
| переменная рантайма (REST, OPC UA, дамп) | <тип>_<группа>_<имя>: ahu_sp_temp |
Modbus mp02 |
4300 + id×2 (float) |
Описания, пределы и заводские значения — GET /api/params/schema и контракты
apps/universal/contract/{vars,itp,water,light,chiller}_contract.json. Время в
параметрах хранится в миллисекундах, на шине Modbus — в секундах.
Параметры вентиляции пишет PUT /api/params; параметры ИТП, воды и освещения —
PUT /api/units/<id>/params; холодоснабжения — напрямую POST /api/variables/chl_…
рантайма. После записи напрямую в рантайм вызовите POST /api/dump/save.
Привязка ввода-вывода модулей МР-02м
Заголовок раздела «Привязка ввода-вывода модулей МР-02м»На СА-02м модули МР-02м опрашивает мост Modbus → MQTT СА-02м; vPLC читает и пишет их
каналы через MQTT. Привязка тегов программы вентиляции к каналам хранится в
bindings.json (рядом с server.py веб-интерфейса установки):
"rSupplyTemp": {"topic": "/devices/mr02m-COM2-5/controls/ai_1", "var": "rSupplyTemp", "is_input": true, "kind": "AI", "class": "ai", "iec": "REAL", "unit": "°C", "required": true, "scale": 1.0, "offset": 0.0, "label": "T приточного воздуха", "quality": "q_rSupplyTemp"}На объекте правят только topic, scale, offset. topic — путь канала без
/on; пустой — точка не привязана. Аналоговые входы мост отдаёт физической величиной
(scale = 1); аналоговые выходы (y* в процентах) — заводское scale = 0.1.
Порядок привязки:
-
Найдите имена устройств и каналов:
mosquitto_sub -h 127.0.0.1 -v -t '/devices/+/controls/+'. -
Впишите
topicу нужных тегов — в первую очередь у обязательных при составе установки (окно «Установка», таблица точек ввода-вывода, илиPOST /api/iomap/autobind— автопривязка по найденным модулям). -
Проверьте, что получится:
Окно терминала curl "$P/api/iomap/status"Ответ: строки будущей карты
mqtt_io_map, маски привязки, обязательные непривязанные точки,pending— ПЛК работает по старой карте. -
Примените в ПЛК:
Окно терминала curl -X POST "$P/api/iomap/apply" -H 'Content-Type: application/json' \-d '{"force": false, "reload": true, "dry_run": false}'Сервер записывает маски привязки
AhuIoBnd*в программу, сохраняет дамп, собирает проект.vplcс картойmqtt_io_map, загружает его (POST /api/project/upload) и перезагружает программу (POST /api/program/reload). Перезагрузка останавливает скан на время загрузки: при работающей установке без"force": true—409 plant_running. Программа старой сборки без масок —409 program_without_masks.
Как программа использует привязку:
- маски
AhuIoBnd*(бит = тег привязан) хранятся в дампе; раз в 30 с сервер сверяет их с применённой привязкой и восстанавливает, если дамп потерян; q_<тег>= качество MQTT-входа И бит привязки; непривязанный вход недостоверен;- обязательная при текущем составе точка без привязки поднимает аварию 61 «неполная карта ввода-вывода» (реакция — останов);
- при привязанных тегах потеря брокера или моста (
MQTT_ok AND BRIDGE_ok= ЛОЖЬ) поднимает аварию 54; - при останове скана выходы получают безопасные значения (DO —
FALSE, AO —0).
Частотный преобразователь, опрашиваемый Modbus master vPLC, в bindings.json не
привязывают (topic пустой), иначе команда уйдёт и в модуль, и в регистр привода.
Для ИТП, воды, освещения и холодоснабжения привязка задаётся при сборке программы
universal и через bindings.json не меняется. [не проверено: порядок привязки этих
типов на объекте]
Внешние установки
Заголовок раздела «Внешние установки»Кроме локальной программы веб установки показывает и управляет установками других контроллеров, которые мост СА-02м публикует в MQTT:
kind |
Устройство | Что даёт |
|---|---|---|
carel (carel-crst, carel-uaria) |
контроллеры Carel ПВУ | значения, состав, аварии с текстами, пуск/стоп, уставки, скорость вентилятора, сброс аварий |
mp02-tcp, vplc-tcp |
прибор MP-02 или второй vPLC по Modbus TCP (шаблон моста mp02-ahu) |
значения, состояние, аварии, пуск/стоп, уставки, режим |
wilo-sk712 |
шкаф дренажных насосов Wilo SK712 (шаблон) | уровень, перелив, насосы, режим, аварии |
Добавление:
curl "$P/api/io/devices" # найти device_id на шинеcurl -X POST "$P/api/units" -H 'Content-Type: application/json' \ -d '{"kind":"carel","source":{"device_id":"<id устройства>"},"name":"ПВУ-2"}'# {"ok":true,"id":"…"}Снимок внешней установки (GET /api/units/<id>/snapshot) отдаёт значения в тех же
именах, что и локальная программа (rSupplyTemp, StateCode, ahu_sp_temp …) плюс
_meta (online, stale_s, status_text, pending …). Устройство без публикаций
дольше 125 с или с meta/error = "r" — online:false, «НЕТ СВЯЗИ».
Команды (POST /api/units/<id>/command, {cmd, value?, wait?}):
cmd |
Carel | MP-02 по TCP | Локальная |
|---|---|---|---|
start / stop |
unit_on/on = 1/0 |
run/on = 1/0 |
CmdStart / CmdStop |
setpoint |
setpoint/on |
temp_setpoint/on (5…40) |
параметр ahu.sp.temp |
fan |
скорость или ступень вентилятора | fan_speed_manual/on (0…100) |
ahu.sp.fan_manual |
reset |
alarm_reset/on |
alarm_reset/on |
CmdReset |
ack |
отметка vPLC | alarm_ack/on |
CmdAck |
Запись — только в …/on, QoS 1, без retain, по белому списку адаптера. С
"wait": true ответ приходит после подтверждения устройством (до 10 с):
result — ok, refused, unconfirmed или timeout. Команды ИТП, воды и
освещения (season, disinfect, zone_on, pump_start …) — в таблице маршрута
POST /api/units/<id>/command документа UNITS_API.md.
Недельное расписание внешних установок — PUT /api/units/<id>/schedule
{"enable":true,"days":[[8,0,20,0], …×7]} (days[0] — понедельник; [вкл_ч, вкл_м, выкл_ч, выкл_м]); проверка — раз в 30 с, команда уходит только при смене требуемого
состояния.
Энергоучёт счётчиком СЭ-02м-3
Заголовок раздела «Энергоучёт счётчиком СЭ-02м-3»Счётчик (анализатор сети) СЭ-02м-3 опрашивает мост СА-02м; программа получает его показания по MQTT.
-
Укажите устройство счётчика у локальной установки:
Окно терминала curl -X PUT "$P/api/units/local/settings" -H 'Content-Type: application/json' \-d '{"energy":{"meter_id":"ce02m3-COM2-14","tariff":7.5}}'meter_id— id устройства на шине;tariff— ₽/кВт·ч (0…1000), сервер передаёт его в параметр<тип>.energy.tariff. -
Включите учёт в программе: параметр
<тип>.energy.meter= 1 («СЭ-02м-3»), напримерahu.energy.meter. -
Примените привязку (
POST /api/iomap/apply): в карту добавятся две строки:
| Топик | Переменная | Качество |
|---|---|---|
/devices/<meter_id>/controls/power_total (Вт) |
MeterPowerW (REAL) |
MeterPowerQ |
/devices/<meter_id>/controls/energy_active_import |
MeterEnergyWh (LREAL) |
MeterEnergyQ |
- Коэффициент
<тип>.energy.meter_scale(Вт·ч на единицу показания) сервер выставляет сам:1.0, если мост публикуетmeta/energy_unit = "Wh", иначе0.003125(1/320 Вт·ч); число вplant_config.json(energy.scale) его перекрывает. - Достоверность:
MeterOk = (meter = 1) AND MeterPowerQ AND MeterEnergyQ; приMeterOk = FALSEсуммы не растут. - Суммы программы:
EnergyRunWh(за пуск),EnergyTodayWh,EnergyYesterdayWh,EnergyTotalWh(UDINT, Вт·ч),PowerPeakTodayW,PowerAvgRunW; граница суток — по часам контроллера. - Те же величины — в Modbus
mp024260…4277 (кВт·ч, float) и вGET /api/energy/local/summary:
curl "$P/api/energy/local/summary?range=24h"# пример ответа (значения условные):# {"available":true,"meter_id":"ce02m3-COM2-14","power_w":4120.0,# "energy_today_kwh":38.2,"energy_yesterday_kwh":51.7,"cost_rub":286.5,"tariff":7.5,# "source":"plc","reliable":true, …}reliable:false и meter_stalled означают, что данные архива недостоверны (разрыв,
смена единиц, счётчик стоит при ненулевой мощности).
Рекомендации по безопасности
Заголовок раздела «Рекомендации по безопасности»- Не открывайте порты 1234, 8088, Modbus и OPC UA в сети за пределами щита/объекта.
- Задайте
api_token, если веб установки не используется; иначе ограничьте доступ к порту 1234 межсетевым экраном. - Modbus TCP slave и OPC UA не имеют аутентификации: любой клиент может писать переменные и параметры.
- Веб установки открывайте через маршрут панели СА-02м
/vplc/(--host 127.0.0.1). - Пароли брокера (
mqtt_password) и токен в ответах API маскируются, вplant_config.jsonне хранятся.