Статус устройств
Статус устройства — это заряд его батареи и запас по SNR (margin). Сервер запрашивает статус MAC-командой DevStatusReq, устройство отвечает командой DevStatusAns; у обеих команд CID 0x06. ONS спрашивает сам, включать ничего не нужно. Частоту запросов сервер подстраивает под то, как часто устройство выходит на связь и отвечает ли оно. Полученные значения сервер передаёт в каждом сообщении uplink, показывает в веб-интерфейсе и хранит в базе.
Когда сервер запрашивает статус
Каждый запрос — это кадр downlink: он занимает эфирное время шлюза и окно приёма устройства. Поэтому сервер спрашивает редко, а тех, кто не отвечает, — ещё реже. Срок следующего запроса он считает так:
срок = max(интервал устройства, K × период кадров) × 2^промахов, рост от промахов — не выше потолка
| Составляющая | Что это | По умолчанию |
|---|---|---|
| Интервал устройства | Настройка dev_status_req_interval, секунды. Нижняя граница: чаще запрос не уходит. 0 или пусто — не спрашивать никогда, см. «Как изменить частоту запросов» | 86400 (сутки) |
| K | Сколько кадров uplink приходится на один запрос | 20 |
| Период кадров | Как часто устройство выходит на связь: сервер сглаживает интервал между кадрами, вес нового интервала — 0,25. Оценка появляется со второго кадра; до этого множитель по кадрам не действует | — |
| Промахи | Сколько запросов подряд осталось без ответа. Каждый промах удваивает срок | 0 |
| Потолок | Предел роста срока от промахов | 30 суток |
Промах засчитывается, когда срок следующего запроса настал, а ответа на прошлый так и не было, — один раз на запрос. Любой DevStatusAns, в том числе пришедший без запроса, обнуляет промахи.
Что получается при K = 20 и интервале в сутки:
| Устройство | Как часто сервер спрашивает |
|---|---|
| Кадр раз в сутки, отвечает | Раз в 20 суток |
| Кадр раз в 5 минут, отвечает | Раз в сутки |
| Кадр раз в сутки, не отвечает | Через 20 суток, дальше раз в 30 суток |
| Кадр раз в 5 минут, не отвечает | Через сутки, 2, 4, 8, 16 суток, дальше раз в 30 суток |
| Интервал 0 или пусто | Не спрашивает |
Запрос по сроку сервер ставит только при приёме uplink — отдельных таймеров нет. На каждом uplink он обновляет оценку периода и, если срок настал, ставит DevStatusReq в очередь MAC-команд устройства. Запрос уходит в кадре downlink в окне приёма после этого uplink, вместе с данными приложения, если они есть в очереди. Ответ устройство присылает в ближайшем плановом uplink. Когда устройство какого класса принимает downlink — на странице «Классы устройств».
Попутная отправка
Если кадр downlink уже уходит по другой причине — например, данные приложения, другая MAC-команда или подтверждение (ACK) подтверждаемого uplink, — сервер добавляет в него DevStatusReq раньше срока. Условие: с прошлого запроса прошло не меньше половины срока и не меньше интервала устройства. Своего кадра попутная отправка не создаёт.
У часто выходящих на связь устройств, которые отвечают, срок равен интервалу, поэтому раньше срока попутный запрос у них не уходит. У неотвечающего устройства попутный запрос уходит в первом же кадре downlink после того, как засчитан промах. Если кадры вниз у такого устройства уходят часто, интервалы из таблицы выше становятся на шаг короче: при кадре раз в 5 минут — сутки, сутки, 2, 4, 8, 16 суток, дальше раз в 30 суток.
Попутной отправки не бывает:
- у мультикаст-групп и устройств класса B;
- если в кадре не осталось места под команду;
- если на прошлый запрос ещё не пришёл ответ, а срок следующего ещё не настал.
Когда запрос не уходит
- Интервал устройства 0 или пустой. Сервер не спрашивает статус никогда; флаги сервера этого не меняют. В прежних версиях ONS устройства, созданные через WebSocket API, получали пустой интервал — им статус не запрашивается.
- Устройство только появилось в кэше сервера — например, после подключения к сети (join): на каждом join сервер собирает запись устройства заново. С этого uplink начинается отсчёт срока, запрос не уходит.
- Прошлый DevStatusReq ещё стоит в очереди MAC-команд — не поместился в кадр. Второй сервер не ставит.
- Мультикаст-группа. Uplink группы не передают, поэтому запросов статуса не получают.
- Устройство класса B. ONS не передаёт таким устройствам кадры: запрос ставится в очередь, и в журнале появляется строка
due, но в эфир он не уходит. Ответа не будет, и срок растёт, как у неотвечающего устройства. См. «Классы устройств». - Сбой внутри механизма. В этом случае сервер запрос не шлёт и пишет предупреждение в свой журнал.
Что приходит в ответе
DevStatusAns несёт два байта: заряд батареи и margin.
| Байт заряда | Что значит | battery_level |
|---|---|---|
| 0 | Устройство на внешнем питании | 0 |
| 1–254 | Уровень заряда | Проценты: байт / 254 × 100, дробное число. 1 → 0,39 %, 254 → 100 % |
| 255 | Устройство не может измерить заряд | 255 |
Margin — запас по SNR, с которым устройство приняло запрос, в дБ, от −32 до 31. Сервер читает второй байт целиком: значения больше 31 переводит в отрицательные, вычитая 64. Два старших бита (RFU) он не отбрасывает: если устройство их выставит, margin получится неверным.
Последний ответ сервер держит в кэше устройства, а каждый ответ записывает в базу вместе со временем записи (UTC). После повторного подключения к сети (join) значения в кэше пустые, пока не придёт новый ответ.
Где видны значения
WebSocket API
В каждом сообщении realtime_data есть поля battery_level и margin — последние известные серверу значения. Они повторяются в каждом uplink, пока не придёт новый ответ. До первого ответа и после повторного подключения к сети (join) в них null. Время измерения в сообщении не передаётся.
Ответ попадает в realtime_data с задержкой на один uplink. Значения для сообщения сервер берёт, когда передаёт uplink дальше, а MAC-команды этого же uplink, включая DevStatusAns, разбирает примерно через 0,6 секунды. Поэтому ответ из uplink N появится в realtime_data только в uplink N+1. В базу — на график и в MCP — ответ попадает сразу при разборе.
Если у приложения есть JS-кодек, поля статуса не теряются: сервер дополняет сообщение результатом кодека и передаёт его дальше.
MQTT, HTTP и ThingsBoard
Интеграции MQTT и HTTP публикуют тело того же вида, что realtime_data, с полями battery_level и margin. Интеграция ThingsBoard передаёт battery_level, margin и join_request_time как телеметрию.
Веб-интерфейс
Блок «Состояние узла» — в карточке устройства в разделе «Узлы», на вкладке «Мониторинг»:
- в заголовке — последний margin («N dB», подсказка «Запас помехоустойчивости») и заряд («N%»);
- графики «Запас помехоустойчивости» (ось «Margin (dB)») и «Аккумулятор» (ось «Процент заряда»);
- период — поле у кнопки «Количество месяцев»: от 1 до 12 месяцев, по умолчанию 1. Графики перестраиваются по нажатию кнопки.
| Что показано вместо процента | Когда |
|---|---|
| «На внешнем питании», значок вилки; графика «Аккумулятор» нет | У самой старой записи за период заряд 0 |
| «Нет актуальных данных»; графика «Аккумулятор» нет | У последней записи заряд 255 |
| «Нет актуальных данных» | Последнее значение на графике заряда вне диапазона 1–100, например 0 |
Записи с зарядом 255 в график «Аккумулятор» не попадают. Margin больше 31 график показывает как 31.
Журнал MAC-команд
Каждый запрос по сроку и попутный, а также каждый ответ сервер записывает в журнал MAC-команд устройства. В веб-интерфейсе это блок «Журнал MAC-команд» на вкладке «Мониторинг», см. «Радиопараметры и журнал MAC-команд». Команда с CID 6 называется в нём DevStatus; по ней журнал можно отфильтровать.
| Направление | Статус | Содержимое | Что значит |
|---|---|---|---|
| Req | due | Пусто | Запрос по сроку. Строка пишется при постановке запроса в очередь MAC-команд |
| Req | free-ride | Пусто | Попутный запрос в кадре, который уходил по другой причине |
| Ans | answered | 2 байта ответа в hex | Устройство ответило |
Это не те статусы sent, acked, rejected и unsolicited, что описаны на странице «Узлы»; интерфейс показывает их серой меткой. Источник (source) у таких строк — dev_status.
REST API
Те же данные отдаёт REST API сервера, которым пользуется веб-интерфейс. Нужны вход в систему и право просмотра устройства.
POST /api/v1/chart/node_status/?dev_eui=<DevEUI>&period=<месяцев>— данные графика «Состояние узла» за последниеperiodмесяцев (по умолчанию 1), по возрастанию времени. В полеdataответа — массив с одним объектом{"name": "NODE_STATUS", "series": [...]}. Элементseries:name— время записи,value— margin (больше 31 урезается до 31),battery—battery_level,external—true, если заряд 0. Для неактивного устройства сервер ответит 404.GET /api/v1/node/<DevEUI>/mac_log/?cid=6— журнал MAC-команд, новые записи сверху, постранично. Фильтры:cidиstatus. Поля строки:time,cid,direction,payload,status,ack_bits,fcnt,source.
MCP
get_device_status— блокdev_status:battery_level,marginиtime— время последней записи в базе.get_mac_log— журнал MAC-команд с полемsource;limit— до 500, по умолчанию 50.send_mac_command— запросить статус вне расписания:data=06(DevStatusReq), команда уходит на FPort 0. Нужны скоупmcp:write, правоchange_nodeи флаг--allow_mac_writeу MCP-сервера. Ответ ищите вget_mac_log. Строки Req для такого запроса в журнале не будет, появится только строка Ansanswered.
Как проверить, отвечает ли устройство
- Откройте карточку устройства в разделе «Узлы», вкладку «Мониторинг».
- В блоке «Журнал MAC-команд» выберите команду DevStatus.
- Сравните строки: после каждой строки Req (
dueилиfree-ride) по времени должна появиться строка Ans со статусомanswered. Новые строки в журнале сверху, поэтому Ans окажется выше своей Req.
Если строк Ans нет, устройство не отвечает: интервал между запросами растёт вдвое, до 30 суток. Строка Ans без строки Req бывает, когда запрос отправили вручную, например через send_mac_command. Если строк нет вовсе, сервер статус не запрашивает — проверьте интервал устройства — или срок первого запроса ещё не настал. Тот же журнал отдаёт MCP-инструмент get_mac_log.
Как изменить частоту запросов
Нижнюю границу задаёт настройка устройства dev_status_req_interval, в секундах:
| Значение | Что делает сервер |
|---|---|
| Больше 0 | Спрашивает не чаще, чем раз в столько секунд |
| 0 или пусто | Не спрашивает никогда |
По умолчанию — 86400 (сутки). Это значение получают устройства, созданные в веб-интерфейсе, через WebSocket API и MCP; команда create_device_req этого поля не принимает.
Из API настройку меняет только MCP-инструмент update_device: поле dev_status_req_interval, целое от 0 до 2147483647 или null. Нужны скоуп mcp:write, право change_node и флаг --allow_node_write у MCP-сервера. После правки сервер сразу обновляет настройку в кэше устройства. В веб-интерфейсе, REST API и WebSocket API этого поля нет. Кроме того, её может изменить администратор сервера в панели администрирования сервера, и после сохранения сервер тоже сразу обновит кэш.
Остальные параметры общие для всего сервера. Их задаёт администратор инсталляции флагами запуска LWServer:
| Флаг | Что задаёт | По умолчанию |
|---|---|---|
--dsr_frames | K — кадров на один запрос. 0 снимает только множитель по кадрам | 20 |
--dsr_backoff_max | Потолок роста срока от промахов, секунды | 2592000 (30 суток) |
--dsr_free_ride | Доля срока для попутной отправки. 0 выключает попутную отправку | 0,5 |
Прежнее правило — запрос каждые dev_status_req_interval секунд без учёта ответов — возвращает набор --dsr_frames 0 --dsr_backoff_max 0 --dsr_free_ride 0. Он действует только для устройств с заданным интервалом: 0 и пусто всё равно значат «не спрашивать».
Что важно знать
- У редких устройств статус обновляется примерно раз в 20 кадров: у устройства с кадром раз в сутки — раз в 20 суток. Значение в
realtime_dataможет быть давним, а время измерения в нём не передаётся; время есть вget_device_statusи на графике. - После повторного подключения к сети (join)
battery_levelиmarginвrealtime_data—null, пока устройство не ответит. Первый запрос после join уйдёт не раньше, чем через интервал устройства. - Признак «На внешнем питании» график берёт из самой старой записи за период, а не из последней.
- В базе заряд хранится в целочисленной колонке, поэтому на графике и в MCP он может отличаться от дробного значения в
realtime_data. - Состояние механизма — оценка периода, время прошлого запроса, промахи — хранится в кэше сервера и в Redis (ключ живёт 30 суток с последней записи), в базе его нет. Если копии нет ни в памяти сервера, ни в Redis, отсчёт начинается заново, как у нового устройства: первый запрос уйдёт не раньше, чем через интервал устройства, промахи считаются с нуля.
См. также
- Подписаться на данные в реальном времени — поля
battery_levelиmarginв сообщенииrealtime_data. - Узлы — вкладка «Мониторинг» и журнал MAC-команд.
- MQTT — тело сообщения uplink с полями статуса.
- Классы устройств — когда устройство принимает downlink, в котором уходит запрос.
- Отправка данных на устройство — очередь downlink и статусы кадра.