poe-svelte-ui-lib 1.79.2 → 1.81.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/WidgetPFD/README.md +39 -26
- package/dist/WidgetPFD/WidgetPFD.svelte +122 -2
- package/dist/WidgetPFD/WidgetPFDProps.svelte +2 -0
- package/dist/WidgetStackInfo/README.md +5 -5
- package/dist/WidgetStackInfo/WidgetStackInfo.svelte +17 -2
- package/dist/locales/translations.js +60 -6
- package/dist/protocols/PROTOCOL.md +1 -1
- package/dist/protocols/dictionary.yaml +0 -1
- package/dist/protocols/dictionaryCodes.js +0 -1
- package/dist/protocols/index.js +1 -1
- package/dist/types.d.ts +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
- **DevCloud** — облако: конструктор GUI устройства, панель управления, документация;
|
|
6
6
|
- **WebApp** — локальный веб-интерфейс прошивок ProdFactory-ESP, встроенный в устройство;
|
|
7
|
-
- **
|
|
7
|
+
- **DevTerminal** — настольный терминал разработчика.
|
|
8
8
|
|
|
9
9
|
Витрина с живыми примерами: [oleg-perevyshin.github.io/ElementsUI](https://oleg-perevyshin.github.io/ElementsUI/).
|
|
10
10
|
|
|
@@ -61,7 +61,7 @@ npm install poe-svelte-ui-lib
|
|
|
61
61
|
|
|
62
62
|
**Битовые поля конфигурации** — `bitFieldGet(word, start, end)`/`bitFieldSet(word, start, end, value)`: поле `start..end` 32-битного слова `{GROUP}.Cfg` (Select/Input с `bitMode`) обычной арифметикой, без знаковых 32-битных операций JS — бит 31 разбирается и собирается корректно, результат всегда 0..2³²−1; тест против эталона на BigInt — `tests/bitField.test.mjs`.
|
|
63
63
|
|
|
64
|
-
**Ядро протоколов** — `poe-svelte-ui-lib/protocols`, описание протокола — [`src/lib/protocols/PROTOCOL.md`](src/lib/protocols/PROTOCOL.md) (без Svelte, работает и под Node без Vite): общий для DevCloud, WebApp и
|
|
64
|
+
**Ядро протоколов** — `poe-svelte-ui-lib/protocols`, описание протокола — [`src/lib/protocols/PROTOCOL.md`](src/lib/protocols/PROTOCOL.md) (без Svelte, работает и под Node без Vite): общий для DevCloud, WebApp и DevTerminal CBOR-кодек (RFC 8949); `valueToCbor`/`cborToValue` — обычные значения, двоичные данные как `Uint8Array` (WebSocket, хранилище, телеметрия); `jsonToCbor` — JSON-совместимое значение в CBOR, строки base64 под ключами с `BIN` — байты (поля ввода терминала и отладки). Пакет `poe_cbor_pkt` — indefinite-массив объектов `[[TrgBusID, SrcBusID, HAR, Payload, CRC16], …]` (CAN, RS485, UART-консоль), `Payload` — одно CBOR item (сырые данные — `encodePacketBytesPayload`, пустой массив — `null`): `encodePacket`/`decodePacket` (разбор с начала буфера — `complete` с длиной, `incomplete` для потока или `garbage`; объекты одному адресату не склеиваются, порядок сохраняется, неверный CRC — флаг `crcOk`), `BUS_ID_REGEXP`/`parseBusId`/`formatBusId` — поля BusID в HEX и вывод `0x0000`, `makeHar(header, argument, reply = true)`/`splitHar` — `HAR`: заголовок в битах 14-15, аргумент в битах 1-13 (0..8191), флаг ответа в бите 0 (как `HAR`/`HAR_NR` прошивки), `packetObjectCrc`; контрольная сумма `crc16Kermit` — строго побайтная, как в `common.c` прошивок. Байты совпадают с `poe_cbor_pkt.c` прошивки — проверка `npm run TestProtocols` (`tests/protocols.test.mjs`, эталонные пакеты собраны прошивочным кодом). Протокол xPOE-Debug (UART-консоль `system_console`) — тот же пакет `poe_cbor_pkt` вперемешку с текстом загрузчика: `encodeXPoeDebugFrame(header, argument, payload, srcId, trgId, reply)` кодирует запрос, `xPoeDebugHar` — HAR по именам словаря, `createXPoeDebugParser().push(байты)` разбирает поток пакетов (каждая запись — отдельный элемент) и внешний текст (`raw`), логи `OK! DebugLog` — `[уровень, тег, текст, FHS]` (`DEBUG_LOG_ARGUMENT`, FHS — в `freeHeap`). Строка таблицы — `xPoeDebugRow`, колонки — `xPoeDebugTableColumns` (основной вход библиотеки): общие для отладки облака и терминала. Ячейка Value — `formatXPoeDebugValue` (CBOR одной строкой, `DebugLog` — уровень · тег · текст), окно по клику — `formatXPoeDebugModal`: Value в развёрнутом виде (`formatXPoeDebugPretty` — то же чистым текстом; карты и массивы с отступами, `DebugLog` — уровень и тег, ниже текст или дамп и FHS) в цветах палитры темы — ключи `--pink-color`, строки `--purple-color`, числа `--red-color`, логические `--orange-color`, `null` — `--cyan-color`, байты `--green-color`, HEX — исходные байты `Payload` сплошной строкой; числа float32 выводятся кратчайшей записью (`0.12`). CAN/CAN FD через slcan-адаптер (`can_service` прошивки): `canFragments`/`encodeCanPacket` — нарезка пакета на кадры как `CAN_Send` (бит 10 ID — «последний», биты 0..9 — `packetId`, по 8 / 64 байта), `encodeSlcanFrame`, `createCanParser().push(байты)` — разбор кадров slcan со сборкой фрагментов и обрезкой паддинга CAN FD. RS485 — сырой поток пакетов: `createPacketStreamParser().push(байты)` (пауза обрывает недособранное, мусор пропускается по байту). Открытие slcan-адаптера — `slcanOpenCommands(скорость, скоростьДанных?)`. Поле Value команды xPOE-Debug из ввода — `xPoeDebugValueFromText` (hex как есть, JSON → CBOR, пусто — пустой Payload). Графики отладки — пакет `OK! DebugPlot` (`LOG_PLOT` прошивки, `DEBUG_PLOT_ARGUMENT`): `debugPlotValues(value)` — пары `[имя, число]` из HEX CBOR-карты `Payload`, `createPlotGroups().update(пары)` раскладывает по группам (префикс до точки) в порядке карты и возвращает обновлённые группы, `data(группа)` — последние значения. Словарь протокола — `HEADER_CODES`, `DEFAULT_ARGUMENT_CODES` (генерируется из мастер-словаря `src/lib/protocols/dictionary.yaml` — `npm run Dictionary`, перед упаковкой автоматически; сам файл поставляется как `poe-svelte-ui-lib/protocols/dictionary.yaml`, `.framework/Dictionary.yaml` прошивок — его справочные копии, сборка прошивок сверяет с ними `api_har.h`), `parseArgumentDictionary` (строки `- [Строка, Enum, Число]`), `nameByCode`. Своих копий протоколов и словаря в приложениях не держим: терминал и отладка облака читают и пишут порт сырыми байтами, разбор и сборка — здесь.
|
|
65
65
|
|
|
66
66
|
**Шапка смарт-виджета** (`WidgetHeader`): значок, заголовок и сворачивание слева, «?» в правом краю; правая часть (режимы, «Обновить» и т. п.) стоит в той же строке, а если не помещается — уходит второй строкой под заголовок и там переносится.
|
|
67
67
|
|
package/dist/WidgetPFD/README.md
CHANGED
|
@@ -16,22 +16,26 @@
|
|
|
16
16
|
|
|
17
17
|
Поток телеметрии (`Telemetry_SendObject`, ключ `Data`) — облако раскладывает поля в значения группы:
|
|
18
18
|
|
|
19
|
-
| Поле | Что | Источник MAVLink
|
|
20
|
-
| ---------------------------------------- | ------------------------------------------------------------------ |
|
|
21
|
-
| `MAV.Roll`, `MAV.Pitch`, `MAV.Heading` | крен, тангаж, курс (°) | ATTITUDE
|
|
22
|
-
| `MAV.
|
|
23
|
-
| `MAV.
|
|
24
|
-
| `MAV.
|
|
25
|
-
| `MAV.
|
|
26
|
-
| `MAV.
|
|
27
|
-
| `MAV.
|
|
28
|
-
| `MAV.
|
|
29
|
-
| `MAV.
|
|
30
|
-
| `MAV.
|
|
31
|
-
| `MAV.
|
|
32
|
-
| `MAV.
|
|
33
|
-
| `MAV.
|
|
34
|
-
| `MAV.
|
|
19
|
+
| Поле | Что | Источник MAVLink | Частота |
|
|
20
|
+
| ---------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ | ------------- |
|
|
21
|
+
| `MAV.Roll`, `MAV.Pitch`, `MAV.Heading` | крен, тангаж, курс (°) | ATTITUDE | 20 Гц |
|
|
22
|
+
| `MAV.Load` | нормальная перегрузка (g), `NaN` — нет данных | SCALED_IMU (`-zacc / 1000`), у ArduPilot — поток `SRx_RAW_SENSORS` | 10 Гц |
|
|
23
|
+
| `MAV.Slip` | боковая перегрузка (g, вправо +), `NaN` — нет данных | SCALED_IMU (`yacc / 1000`) | 10 Гц |
|
|
24
|
+
| `MAV.GSpeed`, `MAV.ASpeed`, `MAV.Climb` | путевая, воздушная, вертикальная скорость (м/с) | VFR_HUD | 2 Гц |
|
|
25
|
+
| `MAV.Thr` | газ (%) | VFR_HUD | 2 Гц |
|
|
26
|
+
| `MAV.WindDir`, `MAV.WindSpd` | ветер: откуда дует (°) и скорость (м/с), `NaN` — нет данных | WIND (ardupilotmega) | 2 Гц |
|
|
27
|
+
| `MAV.Alt`, `MAV.AltMSL` | высота над взлётом и над морем (м) | GLOBAL_POSITION_INT, VFR_HUD | 2 Гц |
|
|
28
|
+
| `MAV.Lat`, `MAV.Lon`, `MAV.Vx`, `MAV.Vy` | координаты, скорость на север/восток | GLOBAL_POSITION_INT | 2 Гц |
|
|
29
|
+
| `MAV.Fix`, `MAV.Sat` | тип фиксации, спутники | GPS_RAW_INT | 2 Гц |
|
|
30
|
+
| `MAV.BV`, `MAV.BC`, `MAV.BRem` | батарея: В, А (-1 нет), % (-1 нет) | SYS_STATUS | 2 Гц |
|
|
31
|
+
| `MAV.Mode`, `MAV.VType`, `MAV.Armed` | custom_mode, MAV_TYPE, взведён | HEARTBEAT | 2 Гц |
|
|
32
|
+
| `MAV.HomeLat`, `MAV.HomeLon` | точка взлёта (0 — нет) | HOME_POSITION | 2 Гц |
|
|
33
|
+
| `MAV.Press`, `MAV.Temp` | давление (гПа), температура (°C) | SCALED_PRESSURE | 2 Гц |
|
|
34
|
+
| `MAV.WPNum`, `MAV.WPDist`, `MAV.WPBrg` | номер точки маршрута, расстояние (м) и курс на неё (°) | MISSION_CURRENT, NAV_CONTROLLER_OUTPUT | 2 Гц |
|
|
35
|
+
| `MAV.AltErr`, `MAV.SpdErr`, `MAV.XTE` | ошибка высоты и скорости (цель − текущее), боковое отклонение (м) | NAV_CONTROLLER_OUTPUT | 2 Гц |
|
|
36
|
+
| `MAV.RSSI`, `MAV.RemRSSI`, `MAV.Drop` | сигнал своей/удалённой стороны (%), потери пакетов (%) | RADIO_STATUS, SYS_STATUS | 2 Гц |
|
|
37
|
+
| `MAV.HDOP`, `MAV.EKF`, `MAV.SensErr` | точность GPS, наибольшая дисперсия EKF, число неисправных датчиков | GPS_RAW_INT, EKF_STATUS_REPORT, SYS_STATUS | 2 Гц |
|
|
38
|
+
| `MAV.Msg`, `MAV.MsgSev`, `MAV.MsgSeq` | сообщение автопилота, важность, номер | STATUSTEXT | при появлении |
|
|
35
39
|
|
|
36
40
|
Расстояние и пеленг на дом, трек и возраст данных виджет считает сам.
|
|
37
41
|
|
|
@@ -44,6 +48,10 @@
|
|
|
44
48
|
| `GSpeed`, `ASpeed` | числа, м/с: путевая и воздушная скорость |
|
|
45
49
|
| `Alt` | число, м: высота над точкой взлёта; `AltMSL` — над уровнем моря |
|
|
46
50
|
| `Climb` | число, м/с: вертикальная скорость (вверх +) |
|
|
51
|
+
| `Load` | число, g: нормальная перегрузка (1 — горизонтальный полёт); не число — плашка G скрыта |
|
|
52
|
+
| `Slip` | число, g: боковая перегрузка (вправо +), шарик скольжения смещается в обратную сторону; не число — шарика нет |
|
|
53
|
+
| `Thr` | число, %: газ; не число — плашки THR нет |
|
|
54
|
+
| `WindDir`, `WindSpd` | числа: ветер — откуда дует, 0–360°, и скорость, м/с; не число — плашки ветра нет |
|
|
47
55
|
| `Lat`, `Lon` | числа, °: координаты |
|
|
48
56
|
| `Vx`, `Vy` | числа, м/с: скорость на север и восток — для трека |
|
|
49
57
|
| `Fix` | число: тип фиксации GPS, `0`–`1` нет, `2` 2D, `3` 3D, `4`–`6` DGPS/RTK; `Sat` — число спутников |
|
|
@@ -69,9 +77,13 @@
|
|
|
69
77
|
- **Строка состояния** — группы «Система» (ARMED/DISARMED, режим по типу аппарата, индикатор «НАВ» по дисперсии EKF и неисправным датчикам), «GPS» (фиксация, спутники, HDOP), «Связь» (сигнал своей/удалённой стороны, потери пакетов), «Батарея» (В, А, %).
|
|
70
78
|
- **Авиагоризонт** — небо и земля по крену и тангажу, лестница тангажа через 5°, дуга крена (10/20/30/45/60°), символ самолёта. Ориентация сглаживается между пакетами (Tween 120 мс).
|
|
71
79
|
- **Лента скорости** (слева, путевая над землёй) и **лента высоты** (справа, над точкой взлёта) с вариометром у края, **лента курса** (снизу): розовый ▲ — трек (куда аппарат реально движется, по `Vx`/`Vy`), зелёный ● — направление на точку взлёта.
|
|
80
|
+
- **Перегрузка и газ** — одной строкой в верхних углах горизонта: слева `G` (нормальная перегрузка, 1 g — горизонтальный полёт; вне `gMin`/`gMax` значение красное, минимум и максимум за время просмотра — в подсказке), справа `THR` (газ, %). Без данных `Load`/`Thr` плашки нет.
|
|
81
|
+
- **Ветер** — плашка под `G`: стрелка показывает, куда дует ветер относительно носа аппарата (вниз — встречный, вверх — попутный, вбок — боковой), рядом скорость, м/с. Без `WindDir`/`WindSpd` плашки нет.
|
|
82
|
+
- **Шарик скольжения** — под указателем крена и поворачивается вместе с ним: шарик в центре — полёт без скольжения, в стороне — скольжение или занос (руль направления в сторону шарика), больше 0.1 g — оранжевый. Без `Slip` шарика нет.
|
|
83
|
+
- **Тренд скорости** — жёлтая стрелка от окна скорости: куда придёт путевая скорость через 10 с при текущем ускорении (по изменению `GSpeed`, сглажено); короткий тренд не рисуется.
|
|
72
84
|
- **Углы под лентами** — воздушная скорость `AS m/s` (относительно воздуха, отличается от путевой на ветер) и вертикальная скорость `VS m/s` (+ набор зелёным, − снижение оранжевым).
|
|
73
85
|
- **Подсказки** — наведите курсор на любой элемент прибора, строки состояния или значений: расшифровка появляется в информационной строке внизу горизонта: первая строка — величина, единица и текущее значение (обновляется, пока курсор стоит на месте), ниже по строке — что означают знаки, цвета и пороги. Элемент помечен `data-tip` с ключом, тексты собраны в `tips` в скрипте виджета, поэтому её не обрезают края и прокрутка. Без наведения в той же строке — последнее сообщение автопилота (10 с), наведение на неё показывает историю. Включаются и выключаются кнопкой «?» в шапке; выбор общий для всех PFD в браузере (`hintsSetting("WidgetPFD")` из `hints.ts`, ключ `localStorage` `WidgetHints.WidgetPFD`, по умолчанию выключены; прежний выбор `WidgetPFD.tips` переносится).
|
|
74
|
-
- **Под прибором** — группы «Маршрут» (точка, расстояние, боковое
|
|
86
|
+
- **Под прибором** — группы «Маршрут» (точка, расстояние, боковое отклонение, время до точки при текущей путевой скорости), «Дом» (расстояние и направление), «Трек», «Позиция» (координаты, MSL), «Воздух» (давление, температура); каждая величина показана один раз.
|
|
75
87
|
- **Метки целей** (бирюзовые) — целевая скорость и высота текущей точки на лентах, курс на точку ◆ на ленте курса; цель за пределами ленты прижимается к краю.
|
|
76
88
|
- **Сообщения автопилота** (`STATUSTEXT`) — в информационной строке внизу горизонта на 10 с, цвет по важности; история последних пяти — при наведении на строку.
|
|
77
89
|
- **Нет данных** — если пакеты не приходили дольше `limits.staleMs` (по умолчанию 2 с), прибор перечёркивается и помечается «NO DATA»; в шапке всегда виден возраст данных.
|
|
@@ -93,21 +105,22 @@
|
|
|
93
105
|
|
|
94
106
|
### IWidgetPFDLimits
|
|
95
107
|
|
|
96
|
-
| Поле
|
|
97
|
-
|
|
|
98
|
-
| `speedMin`
|
|
99
|
-
| `speedMax`
|
|
100
|
-
| `bankMax`
|
|
101
|
-
| `altMax`
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
108
|
+
| Поле | Описание |
|
|
109
|
+
| -------------- | ------------------------------------------------------------ |
|
|
110
|
+
| `speedMin` | м/с — ниже красная зона на ленте скорости |
|
|
111
|
+
| `speedMax` | м/с — выше красная зона |
|
|
112
|
+
| `bankMax` | ° — за пределом дуга крена красная, указатель крена краснеет |
|
|
113
|
+
| `altMax` | м — выше красная зона на ленте высоты |
|
|
114
|
+
| `gMin`, `gMax` | g — вне диапазона перегрузка на плашке G красная |
|
|
115
|
+
| `battLow` | % — ниже батарея в строке состояния красная |
|
|
116
|
+
| `staleMs` | мс без новых данных до флага «NO DATA», по умолчанию 2000 |
|
|
104
117
|
|
|
105
118
|
Незаданная граница не рисуется.
|
|
106
119
|
|
|
107
120
|
## Примеры
|
|
108
121
|
|
|
109
122
|
```svelte
|
|
110
|
-
<UI.WidgetPFD value={mav} limits={{ speedMin: 8, speedMax: 25, bankMax: 30, altMax: 150, battLow: 20 }} />
|
|
123
|
+
<UI.WidgetPFD value={mav} limits={{ speedMin: 8, speedMax: 25, bankMax: 30, altMax: 150, gMin: -1, gMax: 3.8, battLow: 20 }} />
|
|
111
124
|
```
|
|
112
125
|
|
|
113
126
|
## Конструктор свойств (WidgetPFDProps.svelte)
|
|
@@ -66,6 +66,48 @@
|
|
|
66
66
|
let gs = $derived(num(value?.GSpeed))
|
|
67
67
|
let as = $derived(num(value?.ASpeed))
|
|
68
68
|
let climb = $derived(num(value?.Climb))
|
|
69
|
+
/* Перегрузка (SCALED_IMU): плашка G только при наличии данных, минимум и максимум — за время просмотра */
|
|
70
|
+
let load = $derived(typeof value?.Load === "number" && Number.isFinite(value.Load) ? value.Load : null)
|
|
71
|
+
let loadMin = $state(Infinity)
|
|
72
|
+
let loadMax = $state(-Infinity)
|
|
73
|
+
$effect(() => {
|
|
74
|
+
const g = load
|
|
75
|
+
if (g === null) return
|
|
76
|
+
untrack(() => {
|
|
77
|
+
loadMin = Math.min(loadMin, g)
|
|
78
|
+
loadMax = Math.max(loadMax, g)
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
/* Газ (VFR_HUD), %; нет числа — плашки нет */
|
|
82
|
+
let thr = $derived(typeof value?.Thr === "number" && Number.isFinite(value.Thr) ? value.Thr : null)
|
|
83
|
+
/* Боковая перегрузка (SCALED_IMU), g, вправо +; шарик смещается в обратную сторону, как в трубке указателя скольжения */
|
|
84
|
+
let slip = $derived(typeof value?.Slip === "number" && Number.isFinite(value.Slip) ? value.Slip : null)
|
|
85
|
+
const SLIP_PPG = 100 /* пикселей на 1 g, шарик упирается в край трубки на ±0.15 g */
|
|
86
|
+
let slipX = $derived(slip === null ? 0 : Math.max(-15, Math.min(15, -slip * SLIP_PPG)))
|
|
87
|
+
/* Тренд путевой скорости: ускорение по новым значениям GSpeed, сглаженное; стрелка — куда придёт скорость через TREND_S */
|
|
88
|
+
const TREND_S = 10
|
|
89
|
+
let accel = $state(0)
|
|
90
|
+
let lastGs: { v: number; t: number } | null = null
|
|
91
|
+
$effect(() => {
|
|
92
|
+
const v = value?.GSpeed
|
|
93
|
+
if (typeof v !== "number" || !Number.isFinite(v)) return
|
|
94
|
+
const t = Date.now()
|
|
95
|
+
untrack(() => {
|
|
96
|
+
const dt = lastGs ? (t - lastGs.t) / 1000 : Infinity
|
|
97
|
+
if (lastGs && v === lastGs.v && dt < 1.5) return
|
|
98
|
+
accel = dt < 3 ? accel + ((v - lastGs!.v) / dt - accel) * 0.3 : 0
|
|
99
|
+
lastGs = { v, t }
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
let trend = $derived(stale ? 0 : accel * TREND_S)
|
|
103
|
+
/* Ветер (WIND): откуда дует и скорость; стрелка нарисована остриём вниз (встречный) и поворачивается на «откуда дует − курс» */
|
|
104
|
+
let wind = $derived(
|
|
105
|
+
typeof value?.WindDir === "number" && Number.isFinite(value.WindDir) && typeof value?.WindSpd === "number" && Number.isFinite(value.WindSpd)
|
|
106
|
+
? { dir: wrap360(value.WindDir), spd: value.WindSpd }
|
|
107
|
+
: null,
|
|
108
|
+
)
|
|
109
|
+
let windRel = $derived(wind ? wrap360(wind.dir - heading.current) : 0)
|
|
110
|
+
let loadBad = $derived(load !== null && ((limits?.gMax !== undefined && load > limits.gMax) || (limits?.gMin !== undefined && load < limits.gMin)))
|
|
69
111
|
let alt = $derived(num(value?.Alt))
|
|
70
112
|
let altMsl = $derived(num(value?.AltMSL))
|
|
71
113
|
let lat = $derived(num(value?.Lat))
|
|
@@ -162,6 +204,12 @@
|
|
|
162
204
|
|
|
163
205
|
/* Навигация к точке маршрута: метки целей на лентах (flight director); нет маршрута — меток нет */
|
|
164
206
|
let wpNum = $derived(num(value?.WPNum))
|
|
207
|
+
/* Время до точки при текущей путевой скорости; почти на месте — не считается */
|
|
208
|
+
const fmtEta = (s: number) => {
|
|
209
|
+
const t = Math.round(s)
|
|
210
|
+
const mmss = `${Math.floor((t % 3600) / 60)}:${String(t % 60).padStart(2, "0")}`
|
|
211
|
+
return t >= 3600 ? `${Math.floor(t / 3600)}:${mmss.padStart(5, "0")}` : mmss
|
|
212
|
+
}
|
|
165
213
|
let wp = $derived(
|
|
166
214
|
wpNum > 0
|
|
167
215
|
? {
|
|
@@ -174,6 +222,7 @@
|
|
|
174
222
|
}
|
|
175
223
|
: null,
|
|
176
224
|
)
|
|
225
|
+
let eta = $derived(wp && gs > 1 ? fmtEta(wp.dist / gs) : null)
|
|
177
226
|
|
|
178
227
|
/* Связь, точность GPS, исправность навигации */
|
|
179
228
|
let rssi = $derived(num(value?.RSSI, -1))
|
|
@@ -320,6 +369,7 @@
|
|
|
320
369
|
(limits?.speedMin !== undefined || limits?.speedMax !== undefined) &&
|
|
321
370
|
$T("pfd.tip.speed.range", { min: limits?.speedMin ?? "…", max: limits?.speedMax ?? "…" }),
|
|
322
371
|
wp && $T("pfd.tip.speed.wp"),
|
|
372
|
+
Math.abs(trend) >= 0.1 && $T("pfd.tip.trend", { v: fmt(gs + trend) }),
|
|
323
373
|
),
|
|
324
374
|
speedBox: lines($T("pfd.tip.gs", { v: fmt(gs) }), $T("pfd.tip.gs.note")),
|
|
325
375
|
speedLabel: lines($T("pfd.tip.speed.label"), $T("pfd.tip.speed.label.note")),
|
|
@@ -344,10 +394,27 @@
|
|
|
344
394
|
$T("pfd.tip.alt.bug.down"),
|
|
345
395
|
)
|
|
346
396
|
: "",
|
|
347
|
-
compactSpeed: lines(
|
|
397
|
+
compactSpeed: lines(
|
|
398
|
+
$T("pfd.tip.gs", { v: fmt(gs) }),
|
|
399
|
+
$T("pfd.tip.as", { v: fmt(as) }),
|
|
400
|
+
Math.abs(trend) >= 0.1 && $T("pfd.tip.trend", { v: fmt(gs + trend) }),
|
|
401
|
+
),
|
|
348
402
|
compactAlt: lines($T("pfd.tip.alt", { v: Math.round(alt) }), $T("pfd.tip.vs", { v: signed(climb) }), $T("pfd.tip.vs.up"), $T("pfd.tip.vs.down")),
|
|
349
403
|
airspeed: lines($T("pfd.tip.as", { v: fmt(as) }), $T("pfd.tip.as.note"), $T("pfd.tip.as.wind"), $T("pfd.tip.as.head"), $T("pfd.tip.as.tail")),
|
|
350
404
|
vs: lines($T("pfd.tip.vs", { v: signed(climb) }), $T("pfd.tip.vs.up"), $T("pfd.tip.vs.down")),
|
|
405
|
+
wind: wind ? lines($T("pfd.tip.wind", { v: fmt(wind.spd), dir: Math.round(wind.dir) }), $T("pfd.tip.wind.note"), $T("pfd.tip.wind.dirs")) : "",
|
|
406
|
+
thr: thr !== null ? lines($T("pfd.tip.thr", { v: Math.round(thr) }), $T("pfd.tip.thr.note")) : "",
|
|
407
|
+
slip: slip !== null ? lines($T("pfd.tip.slip", { v: fmt(slip, 2) }), $T("pfd.tip.slip.note"), $T("pfd.tip.slip.step")) : "",
|
|
408
|
+
trend: lines($T("pfd.tip.trend", { v: fmt(gs + trend) }), $T("pfd.tip.trend.note")),
|
|
409
|
+
load:
|
|
410
|
+
load !== null
|
|
411
|
+
? lines(
|
|
412
|
+
$T("pfd.tip.load", { v: fmt(load, 2) }),
|
|
413
|
+
$T("pfd.tip.load.note"),
|
|
414
|
+
$T("pfd.tip.load.range", { min: fmt(loadMin, 2), max: fmt(loadMax, 2) }),
|
|
415
|
+
(limits?.gMin !== undefined || limits?.gMax !== undefined) && $T("pfd.tip.load.limit", { min: limits?.gMin ?? "…", max: limits?.gMax ?? "…" }),
|
|
416
|
+
)
|
|
417
|
+
: "",
|
|
351
418
|
heading: lines($T("pfd.tip.heading", { v: String(hdg % 360).padStart(3, "0") }), $T("pfd.tip.heading.note"), $T("pfd.tip.heading.dirs")),
|
|
352
419
|
track: track !== null ? lines($T("pfd.tip.track", { v: Math.round(track) }), $T("pfd.tip.track.note"), $T("pfd.tip.track.drift")) : "",
|
|
353
420
|
wpBug: wp ? lines($T("pfd.tip.wp.bearing", { n: wp.num, v: Math.round(wp.bearing) }), $T("pfd.distance", { d: fmtDistance(wp.dist) })) : "",
|
|
@@ -358,6 +425,7 @@
|
|
|
358
425
|
$T("pfd.tip.route", { n: wp.num }),
|
|
359
426
|
$T("pfd.tip.route.dist", { d: fmtDistance(wp.dist) }),
|
|
360
427
|
$T("pfd.tip.route.xte", { d: fmtDistance(Math.abs(wp.xte)) }),
|
|
428
|
+
eta && $T("pfd.tip.route.eta", { t: eta }),
|
|
361
429
|
)
|
|
362
430
|
: "",
|
|
363
431
|
home: home ? lines($T("pfd.tip.home"), homeLine, $T("pfd.tip.home.dir", { v: Math.round(home.bearing) })) : "",
|
|
@@ -470,6 +538,15 @@
|
|
|
470
538
|
fill={limits?.bankMax !== undefined && Math.abs(roll.current) > limits.bankMax ? "#ef4444" : "#facc15"}
|
|
471
539
|
/>
|
|
472
540
|
</g>
|
|
541
|
+
<!-- Шарик скольжения под указателем крена, поворачивается вместе с ним; больше 0.1 g — оранжевый -->
|
|
542
|
+
{#if slip !== null}
|
|
543
|
+
<g transform="rotate({-roll.current} {CX} {CY})" data-tip="slip">
|
|
544
|
+
<rect x={CX - 21} y={CY - ROLL_R + 17} width="42" height="11" rx="5.5" fill="#000" fill-opacity="0.45" stroke="#fff" stroke-width="1" />
|
|
545
|
+
<line x1={CX - 6} y1={CY - ROLL_R + 17} x2={CX - 6} y2={CY - ROLL_R + 28} stroke="#fff" stroke-width="1" />
|
|
546
|
+
<line x1={CX + 6} y1={CY - ROLL_R + 17} x2={CX + 6} y2={CY - ROLL_R + 28} stroke="#fff" stroke-width="1" />
|
|
547
|
+
<circle cx={CX + slipX} cy={CY - ROLL_R + 22.5} r="4" fill={Math.abs(slip) > 0.1 ? "#f97316" : "#fff"} />
|
|
548
|
+
</g>
|
|
549
|
+
{/if}
|
|
473
550
|
|
|
474
551
|
<!-- Символ самолёта -->
|
|
475
552
|
<g stroke="#facc15" stroke-width="4" fill="none" stroke-linecap="round">
|
|
@@ -478,6 +555,36 @@
|
|
|
478
555
|
</g>
|
|
479
556
|
<circle cx={CX} cy={CY} r="3" fill="#facc15" />
|
|
480
557
|
|
|
558
|
+
<!-- Перегрузка (вне gMin/gMax — красная) и газ: одной строкой в верхних углах горизонта -->
|
|
559
|
+
{#if load !== null}
|
|
560
|
+
<g data-tip="load">
|
|
561
|
+
<rect x={HX + 8} y="8" width="64" height="22" rx="6" fill="#000" fill-opacity="0.55" />
|
|
562
|
+
<text x={HX + 16} y="24" font-size="13" font-weight="600"
|
|
563
|
+
><tspan fill="#fff" fill-opacity="0.7" font-size="10">G</tspan><tspan dx="5" fill={loadBad ? "#ef4444" : "#fff"}>{fmt(load)}</tspan></text
|
|
564
|
+
>
|
|
565
|
+
</g>
|
|
566
|
+
{/if}
|
|
567
|
+
{#if wind}
|
|
568
|
+
<g data-tip="wind">
|
|
569
|
+
<rect x={HX + 8} y={load !== null ? 34 : 8} width="76" height="22" rx="6" fill="#000" fill-opacity="0.55" />
|
|
570
|
+
<g transform="rotate({windRel} {HX + 20} {load !== null ? 45 : 19})">
|
|
571
|
+
<line x1={HX + 20} y1={load !== null ? 39 : 13} x2={HX + 20} y2={load !== null ? 51 : 25} stroke="#fff" stroke-width="2" />
|
|
572
|
+
<polygon points="{HX + 16},{load !== null ? 47 : 21} {HX + 24},{load !== null ? 47 : 21} {HX + 20},{load !== null ? 52 : 26}" fill="#fff" />
|
|
573
|
+
</g>
|
|
574
|
+
<text x={HX + 32} y={load !== null ? 50 : 24} fill="#fff" font-size="13" font-weight="600"
|
|
575
|
+
>{fmt(wind.spd)}<tspan dx="3" fill-opacity="0.7" font-size="10">m/s</tspan></text
|
|
576
|
+
>
|
|
577
|
+
</g>
|
|
578
|
+
{/if}
|
|
579
|
+
{#if thr !== null}
|
|
580
|
+
<g data-tip="thr">
|
|
581
|
+
<rect x={HX + HW - 84} y="8" width="76" height="22" rx="6" fill="#000" fill-opacity="0.55" />
|
|
582
|
+
<text x={HX + HW - 76} y="24" font-size="13" font-weight="600"
|
|
583
|
+
><tspan fill="#fff" fill-opacity="0.7" font-size="10">THR</tspan><tspan dx="5" fill="#fff">{Math.round(thr)} %</tspan></text
|
|
584
|
+
>
|
|
585
|
+
</g>
|
|
586
|
+
{/if}
|
|
587
|
+
|
|
481
588
|
<!-- Компактный режим: числа вместо лент -->
|
|
482
589
|
{#if compact}
|
|
483
590
|
<g font-size="15" font-weight="600" fill="#fff">
|
|
@@ -517,6 +624,16 @@
|
|
|
517
624
|
<rect x="4" y={CY - 14} width="58" height="28" rx="4" fill="#000" stroke="var(--accent-color)" stroke-width="1.5" />
|
|
518
625
|
<text x="33" y={CY + 6} fill="#fff" font-size="15" font-weight="600" text-anchor="middle">{fmt(gs)}</text>
|
|
519
626
|
</g>
|
|
627
|
+
<!-- Тренд скорости: от окна скорости до значения через TREND_S при текущем ускорении -->
|
|
628
|
+
{#if Math.abs(trend) * SPEED_PPU > 16}
|
|
629
|
+
{@const y0 = trend > 0 ? CY - 14 : CY + 14}
|
|
630
|
+
{@const y1 = clampTape(CY - trend * SPEED_PPU)}
|
|
631
|
+
{@const tip = trend > 0 ? 7 : -7}
|
|
632
|
+
<g data-tip="trend">
|
|
633
|
+
<line x1="66" y1={y0} x2="66" y2={y1 + tip} stroke="#facc15" stroke-width="2" />
|
|
634
|
+
<polygon points="61,{y1 + tip} 71,{y1 + tip} 66,{y1}" fill="#facc15" />
|
|
635
|
+
</g>
|
|
636
|
+
{/if}
|
|
520
637
|
<g data-tip="speedLabel">
|
|
521
638
|
<rect x="0" y="0" width="70" height="20" rx="8" fill="#000" />
|
|
522
639
|
<text x="35" y="14" fill="#fff" font-size="10" text-anchor="middle" fill-opacity="0.7">SPD m/s</text>
|
|
@@ -637,7 +754,10 @@
|
|
|
637
754
|
{#if wp}
|
|
638
755
|
<div class="flex min-w-0 items-center gap-2" data-tip="route">
|
|
639
756
|
<span class="shrink-0 text-[10px] font-semibold tracking-wide whitespace-nowrap text-(--faint-color) uppercase">{$T("pfd.route")}</span>
|
|
640
|
-
<span
|
|
757
|
+
<span
|
|
758
|
+
><span style="color:#22d3ee">◆</span>
|
|
759
|
+
{$T("pfd.route.line", { n: wp.num, d: fmtDistance(wp.dist), x: fmtDistance(Math.abs(wp.xte)) })}{eta ? ` · ${eta}` : ""}</span
|
|
760
|
+
>
|
|
641
761
|
</div>
|
|
642
762
|
{/if}
|
|
643
763
|
{#if home}
|
|
@@ -43,6 +43,8 @@
|
|
|
43
43
|
<UI.Input label={{ name: $T("pfd.props.speed_max") }} type="number" value={limits.speedMax} onUpdate={(v) => setLimit("speedMax", v)} />
|
|
44
44
|
<UI.Input label={{ name: $T("pfd.props.bank_max") }} type="number" value={limits.bankMax} onUpdate={(v) => setLimit("bankMax", v)} />
|
|
45
45
|
<UI.Input label={{ name: $T("pfd.props.alt_max") }} type="number" value={limits.altMax} onUpdate={(v) => setLimit("altMax", v)} />
|
|
46
|
+
<UI.Input label={{ name: $T("pfd.props.g_min") }} type="number" value={limits.gMin} onUpdate={(v) => setLimit("gMin", v)} />
|
|
47
|
+
<UI.Input label={{ name: $T("pfd.props.g_max") }} type="number" value={limits.gMax} onUpdate={(v) => setLimit("gMax", v)} />
|
|
46
48
|
<UI.Input label={{ name: $T("pfd.props.batt_low") }} type="number" value={limits.battLow} onUpdate={(v) => setLimit("battLow", v)} />
|
|
47
49
|
<UI.Input label={{ name: $T("pfd.props.stale") }} type="number" value={limits.staleMs ?? 2000} onUpdate={(v) => setLimit("staleMs", v)} />
|
|
48
50
|
</div>
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
{ "Name": "Internal", "Total": 327680, "Free": 118000, "MinFree": 92000 },
|
|
20
20
|
{ "Name": "PSRAM", "Total": 4194304, "Free": 3980000, "MinFree": 3920000 }
|
|
21
21
|
],
|
|
22
|
-
"Tasks": [{ "Name": "api_rx_task", "FreeMin": 1240, "Prio": 5, "Core": 1, "State": 2 }]
|
|
22
|
+
"Tasks": [{ "Name": "api_rx_task", "FreeMin": 1240, "Prio": 5, "Core": 1, "State": 2, "CPU": 3.5 }]
|
|
23
23
|
}
|
|
24
24
|
```
|
|
25
25
|
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
|
|
28
28
|
## Ключи
|
|
29
29
|
|
|
30
|
-
| Ключ | Описание
|
|
31
|
-
| ------- |
|
|
32
|
-
| `Heap` | массив `[{ "Name", "Total", "Free", "MinFree" }]`, байт: кучи памяти, `MinFree` — минимум свободного с момента старта
|
|
33
|
-
| `Tasks` | массив `[{ "Name", "State", "Prio", "Core", "FreeMin" }]`: задачи; `State` — `0` выполняется, `1` готова, `2` ждёт, `3` приостановлена, `4` удалена; `Core` — ядро (только ESP32, `-1` без привязки); `FreeMin` — минимальный запас стека, байт |
|
|
30
|
+
| Ключ | Описание |
|
|
31
|
+
| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
32
|
+
| `Heap` | массив `[{ "Name", "Total", "Free", "MinFree" }]`, байт: кучи памяти, `MinFree` — минимум свободного с момента старта |
|
|
33
|
+
| `Tasks` | массив `[{ "Name", "State", "Prio", "Core", "CPU", "FreeMin" }]`: задачи; `State` — `0` выполняется, `1` готова, `2` ждёт, `3` приостановлена, `4` удалена; `Core` — ядро (только ESP32, `-1` без привязки); `CPU` — загрузка задачи, % одного ядра с прошлого ответа (в первом ответе — с запуска), от 50 % оранжевым, от 80 % красным (кроме `IDLE` — это свободное время ядра), колонки нет, если прошивка поле не присылает; `FreeMin` — минимальный запас стека, байт |
|
|
34
34
|
|
|
35
35
|
## Особенности
|
|
36
36
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!-- $lib/WidgetStackInfo/WidgetStackInfo.svelte — отладочный смарт-виджет памяти и задач FreeRTOS.
|
|
2
2
|
Данные — один объект StackInfo в том виде, в каком его отдают ProdFactory-ESP и ProdFactory-STM
|
|
3
|
-
(API_GetStackInfo): Heap[{Name, Total, Free, MinFree}] и Tasks[{Name, FreeMin, Prio, Core?, State}].
|
|
3
|
+
(API_GetStackInfo): Heap[{Name, Total, Free, MinFree}] и Tasks[{Name, FreeMin, Prio, Core?, State, CPU?}].
|
|
4
4
|
Ничего не знает про DeviceStore/WebSocket — value/onRefresh, автоопрос лишь периодически зовёт onRefresh. -->
|
|
5
5
|
<script lang="ts">
|
|
6
6
|
import { T } from "../locales/i18n"
|
|
@@ -86,20 +86,25 @@
|
|
|
86
86
|
let STATES = $derived(["running", "ready", "blocked", "suspended", "deleted"].map((state) => $T(`widget.stack.state.${state}`)))
|
|
87
87
|
const stackColor = (bytes: number) => (bytes < warnBytes ? "var(--red-color)" : bytes < warnBytes * 2 ? "var(--orange-color)" : "")
|
|
88
88
|
|
|
89
|
-
type TaskRow = { Name: string; StateText: string; Prio: number; CoreText: string; FreeMinText: string }
|
|
89
|
+
type TaskRow = { Name: string; StateText: string; Prio: number; CoreText: string; CpuText: string; FreeMinText: string }
|
|
90
90
|
let tasks = $derived([...(value?.Tasks ?? [])].sort((a, b) => a.FreeMin - b.FreeMin))
|
|
91
91
|
let hasCore = $derived(tasks.some((t) => t.Core !== undefined))
|
|
92
|
+
/* Загрузка задачи, % одного ядра с прошлого ответа: от 50 % — оранжевая, от 80 % — красная; IDLE — это свободное время ядра, не подсвечивается */
|
|
93
|
+
let hasCpu = $derived(tasks.some((t) => t.CPU !== undefined))
|
|
94
|
+
const cpuColor = (cpu: number) => (cpu >= 80 ? "var(--red-color)" : cpu >= 50 ? "var(--orange-color)" : "")
|
|
92
95
|
let lowCount = $derived(tasks.filter((t) => t.FreeMin < warnBytes).length)
|
|
93
96
|
|
|
94
97
|
/* Порядок фиксированный — по возрастанию запаса стека: при автоопросе сортировка кликом сбрасывалась бы каждый ответ */
|
|
95
98
|
let rows: TaskRow[] = $derived(
|
|
96
99
|
tasks.map((t: IWidgetStackInfoTask) => {
|
|
97
100
|
const color = stackColor(t.FreeMin)
|
|
101
|
+
const cpu = t.CPU === undefined ? "" : t.CPU.toFixed(1)
|
|
98
102
|
return {
|
|
99
103
|
Name: escapeHtml(t.Name),
|
|
100
104
|
StateText: STATES[t.State] ?? "—",
|
|
101
105
|
Prio: t.Prio,
|
|
102
106
|
CoreText: t.Core === undefined ? "" : t.Core < 0 ? $T("widget.stack.core_any") : String(t.Core),
|
|
107
|
+
CpuText: cpu && !t.Name.startsWith("IDLE") && cpuColor(t.CPU!) ? `<span style="color: ${cpuColor(t.CPU!)}; font-weight: 600">${cpu}</span>` : cpu,
|
|
103
108
|
FreeMinText: color ? `<span style="color: ${color}; font-weight: 600">${t.FreeMin}</span>` : String(t.FreeMin),
|
|
104
109
|
}
|
|
105
110
|
}),
|
|
@@ -118,6 +123,16 @@
|
|
|
118
123
|
} as ITableHeader<TaskRow>,
|
|
119
124
|
]
|
|
120
125
|
: []),
|
|
126
|
+
...(hasCpu
|
|
127
|
+
? [
|
|
128
|
+
{
|
|
129
|
+
label: { name: $T("widget.stack.cpu") },
|
|
130
|
+
content: [{ type: "text", data: { key: "CpuText" } }],
|
|
131
|
+
width: "5.5rem",
|
|
132
|
+
align: "right",
|
|
133
|
+
} as ITableHeader<TaskRow>,
|
|
134
|
+
]
|
|
135
|
+
: []),
|
|
121
136
|
{ label: { name: $T("widget.stack.free_min") }, content: [{ type: "text", data: { key: "FreeMinText" } }], width: "6rem", align: "right" },
|
|
122
137
|
])
|
|
123
138
|
</script>
|
|
@@ -118,9 +118,9 @@ const translations = {
|
|
|
118
118
|
zh: "🎯 **用途**\n控制可寻址灯带:颜色、效果、速度以及灯带本身的参数。\n\n📦 **数据包**\n- `RGB_Strip` 驱动的 `LST` 分组字段(可通过设置中的前缀更改),无需绑定变量\n- 颜色、效果和速度立即发送\n- 「硬件」区块在本地编辑,点击「保存」一次性发送 —— 灯带重启\n\n🔑 **键**\n- `Color` —— 数组 `[R, G, B, W]`,0–255;`W` 仅用于 SK6812\n- `Effect` —— 数字:`0` 静态、`1` 彩虹、`2` 呼吸、`3` 调色板、`4` 彗星、`5` 闪烁、`6` 火焰、`7` 跑马灯\n- `Speed` —— 0–10 的数字(非线性),`0` —— 暂停\n- `Count` —— LED 数量,1–`MaxCount`\n- `Model` —— 数字:`0` WS2812、`1` SK6812(RGBW)、`2` WS2811\n- `Order` —— 颜色顺序:`0` GRB、`1` RGB、`2` BRG、`3` RBG、`4` GBR、`5` BGR\n- `MaxCount` —— 数字:固件中的容量,只读\n\n✨ **特点**\n- 彩虹、调色板和火焰不使用颜色\n- 颜色不对(红色变绿色)—— 更改顺序:WS2812 通常为 GRB,部分 WS2811 为 RGB 或 BRG\n- 数据引脚和传输方式(RMT/SPI)在固件中设定\n\n⚙️ **设置**\n- 分组前缀(ID)、主色",
|
|
119
119
|
},
|
|
120
120
|
"component.info.WidgetPFD": {
|
|
121
|
-
ru: "🎯 **Назначение**\nПилотажный дисплей (PFD) — главный прибор контроля полёта: пространственное положение, скорость, высота, курс и состояние систем.\n\n📦 **Пакет данных**\n- Поля группы `MAV` — телеметрия автопилота MAVLink (меняется префиксом в настройках), привязывать переменные не нужно\n- Поток телеметрии: `Roll`, `Pitch`, `Heading` — ~20 раз в секунду, остальное — 1–2 раза в секунду\n- Только отображение, ничего не отправляет\n\n🔑 **Ключи**\n- `Roll`, `Pitch` — числа, °: крен (вправо +) и тангаж (вверх +)\n- `Heading` — число, 0–360°: курс\n- `GSpeed`, `ASpeed` — числа, м/с: путевая и воздушная скорость\n- `Alt` — число, м: высота над точкой взлёта; `AltMSL` — над уровнем моря\n- `Climb` — число, м/с: вертикальная скорость (вверх +)\n- `Lat`, `Lon` — числа, °: координаты\n- `Vx`, `Vy` — числа, м/с: скорость на север и восток — для трека\n- `Fix` — число: тип фиксации GPS, `0`–`1` нет, `2` 2D, `3` 3D, `4`–`6` DGPS/RTK; `Sat` — число спутников\n- `BV` — В; `BC` — А (`-1` не измеряется); `BRem` — % (`-1` неизвестно)\n- `Mode` — число: режим автопилота; `VType` — тип аппарата (по нему — названия режимов); `Armed` — `1` взведён\n- `HomeLat`, `HomeLon` — числа, °: точка взлёта, `0` — неизвестна\n- `Press` — гПа, `Temp` — °C\n- `WPNum` — номер точки маршрута (`0` — маршрута нет); `WPDist`, `WPBrg` — расстояние, м, и курс на неё, °\n- `AltErr`, `SpdErr` — цель минус текущее: высота, м, и воздушная скорость, м/с; `XTE` — боковое отклонение от маршрута, м\n- `RSSI`, `RemRSSI` — сигнал своей и удалённой стороны, % (`-1` неизвестно); `Drop` — потери пакетов, %\n- `HDOP` — точность GPS (до 1 отлично, больше 2 неточно); `EKF` — дисперсия навигационного фильтра (от 0.5 внимание, от 0.8 тревога); `SensErr` — неисправных датчиков\n- `Msg`, `MsgSev`, `MsgSeq` — сообщение автопилота, важность (`0`–`3` ошибка, `4` предупреждение, `5`–`7` информация), номер\n\n✨ **Особенности**\n- Ориентация сглаживается между пакетами\n- Наведите курсор на любой элемент прибора или строки — расшифровка с текущим значением в строке внизу горизонта; выключается кнопкой «?» в шапке\n- Углы под лентами: воздушная скорость (относительно воздуха) и вертикальная скорость (+ набор, − снижение)\n- Розовый ▲ на ленте курса — трек: куда аппарат реально движется над землёй (по `Vx`, `Vy`); зелёный ● — направление на точку взлёта; легенда с градусами — под прибором\n- Нет данных дольше заданного времени — прибор перечёркнут и помечен «NO DATA»\n- В узкой ячейке ленты скорости и высоты заменяются числами поверх горизонта\n- Строки сгруппированы: сверху «Система / GPS / Связь / Батарея» — состояние, снизу «Маршрут / Дом / Трек / Позиция / Воздух» — куда движемся и где находимся\n- Бирюзовые метки — цели текущей точки маршрута: скорость и высота на лентах, курс ◆ на ленте курса\n- Сообщения автопилота — в той же строке внизу горизонта на 10 с, цвет по важности; наведение на строку — история\n- Ключи `Lat`, `Lon`, `Alt`, `Heading`, `Sat` общие с «Картой» — одна группа питает оба виджета\n\n⚙️ **Настройки**\n- Префикс группы (ID), мастер-цвет\n- Границы: мин./макс. скорость и макс. высота — красные зоны на лентах; предельный крен — красная дуга; низкий заряд; время до «NO DATA»",
|
|
122
|
-
en: '🎯 **Purpose**\nPrimary flight display (PFD) — the main flight instrument: attitude, speed, altitude, heading and system status.\n\n📦 **Data packet**\n- Fields of the `MAV` group — MAVLink autopilot telemetry (changed by the prefix in settings), no variable binding needed\n- Telemetry stream: `Roll`, `Pitch`, `Heading` ~20 times a second, the rest 1–2 times a second\n- Display only, sends nothing\n\n🔑 **Keys**\n- `Roll`, `Pitch` — numbers, °: bank (right +) and pitch (up +)\n- `Heading` — number, 0–360°: heading\n- `GSpeed`, `ASpeed` — numbers, m/s: ground speed and airspeed\n- `Alt` — number, m: altitude above home; `AltMSL` — above sea level\n- `Climb` — number, m/s: vertical speed (up +)\n- `Lat`, `Lon` — numbers, °: coordinates\n- `Vx`, `Vy` — numbers, m/s: north and east velocity — for the track\n- `Fix` — number: GPS fix type, `0`–`1` none, `2` 2D, `3` 3D, `4`–`6` DGPS/RTK; `Sat` — satellite count\n- `BV` — V; `BC` — A (`-1` not measured); `BRem` — % (`-1` unknown)\n- `Mode` — number: autopilot mode; `VType` — vehicle type (mode names depend on it); `Armed` — `1` armed\n- `HomeLat`, `HomeLon` — numbers, °: home position, `0` — unknown\n- `Press` — hPa, `Temp` — °C\n- `WPNum` — waypoint number (`0` — no mission); `WPDist`, `WPBrg` — distance, m, and bearing to it, °\n- `AltErr`, `SpdErr` — target minus current: altitude, m, and airspeed, m/s; `XTE` — cross-track error, m\n- `RSSI`, `RemRSSI` — local and remote signal, % (`-1` unknown); `Drop` — packet loss, %\n- `HDOP` — GPS precision (up to 1 excellent, over 2 poor); `EKF` — navigation filter variance (from 0.5 warning, from 0.8 alarm); `SensErr` — unhealthy sensors\n- `Msg`, `MsgSev`, `MsgSeq` — autopilot message, severity (`0`–`3` error, `4` warning, `5`–`7` info), number\n\n✨ **Features**\n- Attitude is smoothed between packets\n- Hover any instrument element or row item — its meaning and current value in the line at the bottom of the horizon; turned off with the \"?\" button in the header\n- Corners under the tapes: airspeed (relative to the air) and vertical speed (+ climb, − descent)\n- Magenta ▲ on the heading tape is the track: where the vehicle actually moves over the ground (from `Vx`, `Vy`); green ● — direction to home; a legend with degrees is below the instrument\n- No data for longer than the set time — the instrument is crossed out and marked "NO DATA"\n- In a narrow cell the speed and altitude tapes become numbers over the horizon\n- Rows are grouped: top \"System / GPS / Link / Battery\" — status, bottom \"Route / Home / Track / Position / Air\" — where we go and where we are\n- Cyan marks — targets of the current waypoint: speed and altitude on the tapes, bearing ◆ on the heading tape\n- Autopilot messages — in the same line at the bottom of the horizon for 10 s, colored by severity; hover the line for history\n- Keys `Lat`, `Lon`, `Alt`, `Heading`, `Sat` are shared with the "Map" — one group feeds both widgets\n\n⚙️ **Settings**\n- Group prefix (ID), master color\n- Limits: min/max speed and max altitude — red bands on the tapes; bank limit — red arc; low battery; time until "NO DATA"',
|
|
123
|
-
zh: "🎯 **用途**\n主飞行显示器(PFD)—— 飞行监控的主要仪表:姿态、速度、高度、航向和系统状态。\n\n📦 **数据包**\n- `MAV` 分组的字段 —— MAVLink 自驾仪遥测(可通过设置中的前缀更改),无需绑定变量\n- 遥测流:`Roll`、`Pitch`、`Heading` 约每秒 20 次,其余每秒 1–2 次\n- 仅显示,不发送任何数据\n\n🔑 **键**\n- `Roll`、`Pitch` —— 数字,°:坡度(向右 +)和俯仰(向上 +)\n- `Heading` —— 数字,0–360°:航向\n- `GSpeed`、`ASpeed` —— 数字,m/s:地速和空速\n- `Alt` —— 数字,m:相对起飞点高度;`AltMSL` —— 海拔高度\n- `Climb` —— 数字,m/s:升降速度(向上 +)\n- `Lat`、`Lon` —— 数字,°:坐标\n- `Vx`、`Vy` —— 数字,m/s:向北和向东的速度 —— 用于航迹\n- `Fix` —— 数字:GPS 定位类型,`0`–`1` 无、`2` 2D、`3` 3D、`4`–`6` DGPS/RTK;`Sat` —— 卫星数\n- `BV` —— V;`BC` —— A(`-1` 未测量);`BRem` —— %(`-1` 未知)\n- `Mode` —— 数字:自驾仪模式;`VType` —— 机型(决定模式名称);`Armed` —— `1` 已解锁\n- `HomeLat`、`HomeLon` —— 数字,°:起飞点,`0` —— 未知\n- `Press` —— hPa,`Temp` —— °C\n- `WPNum` —— 航点编号(`0` —— 无任务);`WPDist`、`WPBrg` —— 到航点的距离,m,和方位,°\n- `AltErr`、`SpdErr` —— 目标减当前:高度,m,空速,m/s;`XTE` —— 偏航距离,m\n- `RSSI`、`RemRSSI` —— 本地和远端信号,%(`-1` 未知);`Drop` —— 丢包率,%\n- `HDOP` —— GPS 精度(1 以下优秀,大于 2 不准确);`EKF` —— 导航滤波器方差(0.5 起注意,0.8 起告警);`SensErr` —— 故障传感器数\n- `Msg`、`MsgSev`、`MsgSeq` —— 自驾仪消息、严重程度(`0`–`3` 错误、`4` 警告、`5`–`7` 信息)、编号\n\n✨ **特点**\n- 数据包之间的姿态经过平滑\n- 将鼠标悬停在仪表或行中的任意元素上 —— 在地平线底部的信息行中显示含义和当前值;可通过标题栏中的「?」按钮关闭\n- 速度带和高度带下方的角落:空速(相对空气)和升降速度(+ 爬升、− 下降)\n- 航向带上的粉色 ▲ 为航迹:载具相对地面的实际运动方向(由 `Vx`、`Vy` 计算);绿色 ● —— 起飞点方向;仪表下方有带角度的图例\n- 超过设定时间无数据 —— 仪表被划叉并标记「无数据」\n- 在窄单元格中,速度带和高度带变为地平线上的数字\n- 行已分组:上方「系统 / GPS / 链路 / 电池」—— 状态,下方「航线 / 起飞点 / 航迹 / 位置 / 空气」—— 去向与位置\n- 青色标记 —— 当前航点的目标:速度带和高度带上的速度与高度,航向带上的方位 ◆\n- 自驾仪消息 —— 在地平线底部同一信息行显示 10 秒,颜色按严重程度;悬停该行可查看历史\n- 键 `Lat`、`Lon`、`Alt`、`Heading`、`Sat` 与「地图」通用 —— 一个分组同时供两个组件使用\n\n⚙️ **设置**\n- 分组前缀(ID)、主色\n- 限值:最小/最大速度和最大高度 —— 速度带和高度带上的红色区域;最大坡度 ——
|
|
121
|
+
ru: "🎯 **Назначение**\nПилотажный дисплей (PFD) — главный прибор контроля полёта: пространственное положение, скорость, высота, курс и состояние систем.\n\n📦 **Пакет данных**\n- Поля группы `MAV` — телеметрия автопилота MAVLink (меняется префиксом в настройках), привязывать переменные не нужно\n- Поток телеметрии: `Roll`, `Pitch`, `Heading`, `Load`, `Slip` — ~10–20 раз в секунду, остальное — 1–2 раза в секунду\n- Только отображение, ничего не отправляет\n\n🔑 **Ключи**\n- `Roll`, `Pitch` — числа, °: крен (вправо +) и тангаж (вверх +)\n- `Heading` — число, 0–360°: курс\n- `GSpeed`, `ASpeed` — числа, м/с: путевая и воздушная скорость\n- `Alt` — число, м: высота над точкой взлёта; `AltMSL` — над уровнем моря\n- `Climb` — число, м/с: вертикальная скорость (вверх +)\n- `Load` — число, g: нормальная перегрузка (1 — горизонтальный полёт); `Slip` — боковая перегрузка, g (вправо +); не число — плашки G и шарика нет\n- `Thr` — число, %: газ\n- `WindDir`, `WindSpd` — ветер: откуда дует, °, и скорость, м/с\n- `Lat`, `Lon` — числа, °: координаты\n- `Vx`, `Vy` — числа, м/с: скорость на север и восток — для трека\n- `Fix` — число: тип фиксации GPS, `0`–`1` нет, `2` 2D, `3` 3D, `4`–`6` DGPS/RTK; `Sat` — число спутников\n- `BV` — В; `BC` — А (`-1` не измеряется); `BRem` — % (`-1` неизвестно)\n- `Mode` — число: режим автопилота; `VType` — тип аппарата (по нему — названия режимов); `Armed` — `1` взведён\n- `HomeLat`, `HomeLon` — числа, °: точка взлёта, `0` — неизвестна\n- `Press` — гПа, `Temp` — °C\n- `WPNum` — номер точки маршрута (`0` — маршрута нет); `WPDist`, `WPBrg` — расстояние, м, и курс на неё, °\n- `AltErr`, `SpdErr` — цель минус текущее: высота, м, и воздушная скорость, м/с; `XTE` — боковое отклонение от маршрута, м\n- `RSSI`, `RemRSSI` — сигнал своей и удалённой стороны, % (`-1` неизвестно); `Drop` — потери пакетов, %\n- `HDOP` — точность GPS (до 1 отлично, больше 2 неточно); `EKF` — дисперсия навигационного фильтра (от 0.5 внимание, от 0.8 тревога); `SensErr` — неисправных датчиков\n- `Msg`, `MsgSev`, `MsgSeq` — сообщение автопилота, важность (`0`–`3` ошибка, `4` предупреждение, `5`–`7` информация), номер\n\n✨ **Особенности**\n- Ориентация сглаживается между пакетами\n- Наведите курсор на любой элемент прибора или строки — расшифровка с текущим значением в строке внизу горизонта; выключается кнопкой «?» в шапке\n- Углы под лентами: воздушная скорость (относительно воздуха) и вертикальная скорость (+ набор, − снижение)\n- Верхние углы горизонта: `G` — перегрузка (вне границ — красная, минимум и максимум — в подсказке) и `THR` — газ\n- Ветер — под `G`: стрелка — куда дует относительно носа аппарата (вниз — встречный, вверх — попутный), рядом скорость\n- В группе «Маршрут» — время до точки при текущей путевой скорости\n- Шарик скольжения под указателем крена: в центре — полёт без скольжения, в стороне — скольжение или занос (руль направления в сторону шарика)\n- Жёлтая стрелка на ленте скорости — тренд: куда придёт путевая скорость через 10 с при текущем ускорении\n- Розовый ▲ на ленте курса — трек: куда аппарат реально движется над землёй (по `Vx`, `Vy`); зелёный ● — направление на точку взлёта; легенда с градусами — под прибором\n- Нет данных дольше заданного времени — прибор перечёркнут и помечен «NO DATA»\n- В узкой ячейке ленты скорости и высоты заменяются числами поверх горизонта\n- Строки сгруппированы: сверху «Система / GPS / Связь / Батарея» — состояние, снизу «Маршрут / Дом / Трек / Позиция / Воздух» — куда движемся и где находимся\n- Бирюзовые метки — цели текущей точки маршрута: скорость и высота на лентах, курс ◆ на ленте курса\n- Сообщения автопилота — в той же строке внизу горизонта на 10 с, цвет по важности; наведение на строку — история\n- Ключи `Lat`, `Lon`, `Alt`, `Heading`, `Sat` общие с «Картой» — одна группа питает оба виджета\n\n⚙️ **Настройки**\n- Префикс группы (ID), мастер-цвет\n- Границы: мин./макс. скорость и макс. высота — красные зоны на лентах; предельный крен — красная дуга; мин./макс. перегрузка — красное значение G; низкий заряд; время до «NO DATA»",
|
|
122
|
+
en: '🎯 **Purpose**\nPrimary flight display (PFD) — the main flight instrument: attitude, speed, altitude, heading and system status.\n\n📦 **Data packet**\n- Fields of the `MAV` group — MAVLink autopilot telemetry (changed by the prefix in settings), no variable binding needed\n- Telemetry stream: `Roll`, `Pitch`, `Heading`, `Load`, `Slip` ~10–20 times a second, the rest 1–2 times a second\n- Display only, sends nothing\n\n🔑 **Keys**\n- `Roll`, `Pitch` — numbers, °: bank (right +) and pitch (up +)\n- `Heading` — number, 0–360°: heading\n- `GSpeed`, `ASpeed` — numbers, m/s: ground speed and airspeed\n- `Alt` — number, m: altitude above home; `AltMSL` — above sea level\n- `Climb` — number, m/s: vertical speed (up +)\n- `Load` — number, g: normal load factor (1 — level flight); `Slip` — lateral load, g (right +); not a number — no G box and no ball\n- `Thr` — number, %: throttle\n- `WindDir`, `WindSpd` — wind: from, °, and speed, m/s\n- `Lat`, `Lon` — numbers, °: coordinates\n- `Vx`, `Vy` — numbers, m/s: north and east velocity — for the track\n- `Fix` — number: GPS fix type, `0`–`1` none, `2` 2D, `3` 3D, `4`–`6` DGPS/RTK; `Sat` — satellite count\n- `BV` — V; `BC` — A (`-1` not measured); `BRem` — % (`-1` unknown)\n- `Mode` — number: autopilot mode; `VType` — vehicle type (mode names depend on it); `Armed` — `1` armed\n- `HomeLat`, `HomeLon` — numbers, °: home position, `0` — unknown\n- `Press` — hPa, `Temp` — °C\n- `WPNum` — waypoint number (`0` — no mission); `WPDist`, `WPBrg` — distance, m, and bearing to it, °\n- `AltErr`, `SpdErr` — target minus current: altitude, m, and airspeed, m/s; `XTE` — cross-track error, m\n- `RSSI`, `RemRSSI` — local and remote signal, % (`-1` unknown); `Drop` — packet loss, %\n- `HDOP` — GPS precision (up to 1 excellent, over 2 poor); `EKF` — navigation filter variance (from 0.5 warning, from 0.8 alarm); `SensErr` — unhealthy sensors\n- `Msg`, `MsgSev`, `MsgSeq` — autopilot message, severity (`0`–`3` error, `4` warning, `5`–`7` info), number\n\n✨ **Features**\n- Attitude is smoothed between packets\n- Hover any instrument element or row item — its meaning and current value in the line at the bottom of the horizon; turned off with the \"?\" button in the header\n- Corners under the tapes: airspeed (relative to the air) and vertical speed (+ climb, − descent)\n- Top corners of the horizon: `G` — load factor (red outside the limits, min and max in the hint) and `THR` — throttle\n- Wind — under `G`: the arrow shows where it blows relative to the nose (down — headwind, up — tailwind), speed next to it\n- The "Route" group shows the time to the waypoint at the current ground speed\n- Slip ball under the bank pointer: centred — no sideslip, off-centre — slip or skid (rudder towards the ball)\n- Yellow arrow on the speed tape — trend: where ground speed will be in 10 s at the current acceleration\n- Magenta ▲ on the heading tape is the track: where the vehicle actually moves over the ground (from `Vx`, `Vy`); green ● — direction to home; a legend with degrees is below the instrument\n- No data for longer than the set time — the instrument is crossed out and marked "NO DATA"\n- In a narrow cell the speed and altitude tapes become numbers over the horizon\n- Rows are grouped: top \"System / GPS / Link / Battery\" — status, bottom \"Route / Home / Track / Position / Air\" — where we go and where we are\n- Cyan marks — targets of the current waypoint: speed and altitude on the tapes, bearing ◆ on the heading tape\n- Autopilot messages — in the same line at the bottom of the horizon for 10 s, colored by severity; hover the line for history\n- Keys `Lat`, `Lon`, `Alt`, `Heading`, `Sat` are shared with the "Map" — one group feeds both widgets\n\n⚙️ **Settings**\n- Group prefix (ID), master color\n- Limits: min/max speed and max altitude — red bands on the tapes; bank limit — red arc; min/max load factor — red G value; low battery; time until "NO DATA"',
|
|
123
|
+
zh: "🎯 **用途**\n主飞行显示器(PFD)—— 飞行监控的主要仪表:姿态、速度、高度、航向和系统状态。\n\n📦 **数据包**\n- `MAV` 分组的字段 —— MAVLink 自驾仪遥测(可通过设置中的前缀更改),无需绑定变量\n- 遥测流:`Roll`、`Pitch`、`Heading`、`Load`、`Slip` 约每秒 10–20 次,其余每秒 1–2 次\n- 仅显示,不发送任何数据\n\n🔑 **键**\n- `Roll`、`Pitch` —— 数字,°:坡度(向右 +)和俯仰(向上 +)\n- `Heading` —— 数字,0–360°:航向\n- `GSpeed`、`ASpeed` —— 数字,m/s:地速和空速\n- `Alt` —— 数字,m:相对起飞点高度;`AltMSL` —— 海拔高度\n- `Climb` —— 数字,m/s:升降速度(向上 +)\n- `Load` —— 数字,g:法向过载(1 —— 平飞);`Slip` —— 侧向过载,g(向右 +);非数字 —— 不显示 G 和侧滑球\n- `Thr` —— 数字,%:油门\n- `WindDir`、`WindSpd` —— 风:来向,°,和风速,m/s\n- `Lat`、`Lon` —— 数字,°:坐标\n- `Vx`、`Vy` —— 数字,m/s:向北和向东的速度 —— 用于航迹\n- `Fix` —— 数字:GPS 定位类型,`0`–`1` 无、`2` 2D、`3` 3D、`4`–`6` DGPS/RTK;`Sat` —— 卫星数\n- `BV` —— V;`BC` —— A(`-1` 未测量);`BRem` —— %(`-1` 未知)\n- `Mode` —— 数字:自驾仪模式;`VType` —— 机型(决定模式名称);`Armed` —— `1` 已解锁\n- `HomeLat`、`HomeLon` —— 数字,°:起飞点,`0` —— 未知\n- `Press` —— hPa,`Temp` —— °C\n- `WPNum` —— 航点编号(`0` —— 无任务);`WPDist`、`WPBrg` —— 到航点的距离,m,和方位,°\n- `AltErr`、`SpdErr` —— 目标减当前:高度,m,空速,m/s;`XTE` —— 偏航距离,m\n- `RSSI`、`RemRSSI` —— 本地和远端信号,%(`-1` 未知);`Drop` —— 丢包率,%\n- `HDOP` —— GPS 精度(1 以下优秀,大于 2 不准确);`EKF` —— 导航滤波器方差(0.5 起注意,0.8 起告警);`SensErr` —— 故障传感器数\n- `Msg`、`MsgSev`、`MsgSeq` —— 自驾仪消息、严重程度(`0`–`3` 错误、`4` 警告、`5`–`7` 信息)、编号\n\n✨ **特点**\n- 数据包之间的姿态经过平滑\n- 将鼠标悬停在仪表或行中的任意元素上 —— 在地平线底部的信息行中显示含义和当前值;可通过标题栏中的「?」按钮关闭\n- 速度带和高度带下方的角落:空速(相对空气)和升降速度(+ 爬升、− 下降)\n- 地平线上方两角:`G` —— 过载(超出限值为红色,最小和最大值见提示)和 `THR` —— 油门\n- 风 —— 位于 `G` 下方:箭头为相对机头的风吹去方向(向下 —— 逆风,向上 —— 顺风),旁边为风速\n- 「航线」组显示按当前地速到航点的时间\n- 坡度指针下方的侧滑球:居中 —— 无侧滑,偏离 —— 侧滑或外滑(方向舵朝球的方向)\n- 速度带上的黄色箭头 —— 趋势:以当前加速度 10 秒后地速将到达的位置\n- 航向带上的粉色 ▲ 为航迹:载具相对地面的实际运动方向(由 `Vx`、`Vy` 计算);绿色 ● —— 起飞点方向;仪表下方有带角度的图例\n- 超过设定时间无数据 —— 仪表被划叉并标记「无数据」\n- 在窄单元格中,速度带和高度带变为地平线上的数字\n- 行已分组:上方「系统 / GPS / 链路 / 电池」—— 状态,下方「航线 / 起飞点 / 航迹 / 位置 / 空气」—— 去向与位置\n- 青色标记 —— 当前航点的目标:速度带和高度带上的速度与高度,航向带上的方位 ◆\n- 自驾仪消息 —— 在地平线底部同一信息行显示 10 秒,颜色按严重程度;悬停该行可查看历史\n- 键 `Lat`、`Lon`、`Alt`、`Heading`、`Sat` 与「地图」通用 —— 一个分组同时供两个组件使用\n\n⚙️ **设置**\n- 分组前缀(ID)、主色\n- 限值:最小/最大速度和最大高度 —— 速度带和高度带上的红色区域;最大坡度 —— 红色弧线;最小/最大过载 —— G 值变红;低电量;「无数据」前的时间",
|
|
124
124
|
},
|
|
125
125
|
"component.info.WidgetMap": {
|
|
126
126
|
ru: "🎯 **Назначение**\nКарта объектов в реальном времени: положение, курс и траектория каждого объекта.\n\n📦 **Пакет данных**\n- Поля групп устройства, по группе на объект: по умолчанию `MAV`, несколько — через пробел (`MAV GNSS`); привязывать переменные не нужно\n- Поток телеметрии: координаты — раз в 0.5–1 с, имя и серийный номер — реже\n- Только отображение, ничего не отправляет\n\n🔑 **Ключи**\n- `Lat` — число, °: широта, обязательный; `Lat` и `Lon` = 0 — нет фикса, объект не рисуется\n- `Lon` — число, °: долгота, обязательный\n- `Alt` — число, м: высота\n- `Heading` — число, 0–360°: курс, поворачивает маркер\n- `Sat` — число спутников\n- `DevName` — строка: имя объекта, подпись маркера; без него — имя группы\n- `DevSN` — строка: серийный номер\n\n✨ **Особенности**\n- Ключи общие с «Пилотажным дисплеем» и «Об устройстве» — один пакет питает несколько виджетов\n- Клик по объекту — карточка со всеми значениями, кнопки «Копировать» и «Скрыть»\n- Траекторию, число её точек и таймаут маркеров оператор переключает на карте; объект без новых данных краснеет, ещё через таймаут скрывается\n- «Показать все» в шапке вписывает объекты в экран; сама карта вид не меняет\n- В шапке — сколько объектов на связи из заданных\n- Только 2D: правой кнопкой мыши карта поворачивается, компас под зумом возвращает её на север\n\n⚙️ **Настройки**\n- Группы полей, мастер-цвет\n- Высота карты `S`/`M`/`L`/`XL`, иконка маркера\n- Начальные значения: траектория и число её точек",
|
|
@@ -128,9 +128,9 @@ const translations = {
|
|
|
128
128
|
zh: "🎯 **用途**\n实时对象地图:每个对象的位置、航向和轨迹。\n\n📦 **数据包**\n- 设备字段分组,每个分组一个对象:默认 `MAV`,多个用空格分隔(`MAV GNSS`);无需绑定变量\n- 遥测流:坐标每 0.5–1 秒一次,名称和序列号频率更低\n- 仅显示,不发送任何数据\n\n🔑 **键**\n- `Lat` —— 数字,°:纬度,必需;`Lat` 和 `Lon` 为 0 —— 无定位,不绘制对象\n- `Lon` —— 数字,°:经度,必需\n- `Alt` —— 数字,m:高度\n- `Heading` —— 数字,0–360°:航向,旋转标记\n- `Sat` —— 卫星数\n- `DevName` —— 字符串:对象名称,标记标签;缺省时为分组名\n- `DevSN` —— 字符串:序列号\n\n✨ **特点**\n- 键与「飞行显示器」和「关于设备」通用 —— 一个数据包可供多个组件使用\n- 点击对象 —— 显示所有值的卡片,带「复制」和「隐藏」按钮\n- 操作员在地图上切换轨迹、轨迹点数和标记超时;无新数据的对象变红,再过一个超时后隐藏\n- 标题栏中的「显示全部」将对象纳入视野;地图本身不会自动移动\n- 标题栏显示已配置对象中在线的数量\n- 仅 2D:按住鼠标右键旋转地图,缩放按钮下方的指南针将其恢复为正北\n\n⚙️ **设置**\n- 字段分组、主色\n- 地图高度 `S`/`M`/`L`/`XL`、标记图标\n- 默认值:轨迹及其点数",
|
|
129
129
|
},
|
|
130
130
|
"component.info.WidgetStackInfo": {
|
|
131
|
-
ru: '🎯 **Назначение**\nОтладка прошивки: свободная память и запас стека задач.\n\n📦 **Пакет данных**\n- Один объект `StackInfo` из ответа на `GET StackInfo`, привязывать переменные не нужно\n- Запрос — кнопкой «Обновить» или автоопросом\n\n🔑 **Ключи**\n- `Heap` — массив `[{ "Name", "Total", "Free", "MinFree" }]`, байт: кучи памяти — `Internal` встроенная и `PSRAM` внешняя ОЗУ у ESP, `Heap` у STM, в виджете понятными подписями; `MinFree` — минимум свободного с момента старта\n- `Tasks` — массив `[{ "Name", "State", "Prio", "Core", "FreeMin" }]`: задачи; `State` — `0` выполняется, `1` готова, `2` ждёт, `3` приостановлена, `4` удалена; `Core` — ядро (только ESP32, `-1` без привязки); `FreeMin` — минимальный запас стека, байт\n\n✨ **Особенности**\n- Светлая часть полосы кучи — пик занятости\n- Задачи отсортированы по запасу стека — самые рискованные сверху\n- Свёрнутый виджет не опрашивает устройство\n\n⚙️ **Настройки**\n- Порог запаса стека: меньше порога — красным, меньше удвоенного — оранжевым\n- Автоопрос: выкл / 1 / 5 / 10 с; мастер-цвет',
|
|
132
|
-
en: '🎯 **Purpose**\nFirmware debugging: free memory and task stack headroom.\n\n📦 **Data packet**\n- One `StackInfo` object from the `GET StackInfo` response, no variable binding needed\n- Requested with the "Refresh" button or by auto polling\n\n🔑 **Keys**\n- `Heap` — array `[{ "Name", "Total", "Free", "MinFree" }]`, bytes: memory heaps — `Internal` on-chip and `PSRAM` external RAM on ESP, `Heap` on STM, shown with readable names; `MinFree` — minimum free since boot\n- `Tasks` — array `[{ "Name", "State", "Prio", "Core", "FreeMin" }]`: tasks; `State` — `0` running, `1` ready, `2` blocked, `3` suspended, `4` deleted; `Core` — core (ESP32 only, `-1` unpinned); `FreeMin` — minimum stack headroom, bytes\n\n✨ **Features**\n- The light part of the heap bar is peak usage\n- Tasks are sorted by stack headroom — the riskiest on top\n- A collapsed widget does not poll the device\n\n⚙️ **Settings**\n- Stack threshold: below it — red, below double — orange\n- Auto polling: off / 1 / 5 / 10 s; master color',
|
|
133
|
-
zh: '🎯 **用途**\n固件调试:空闲内存和任务栈余量。\n\n📦 **数据包**\n- 来自 `GET StackInfo` 响应的一个 `StackInfo` 对象,无需绑定变量\n- 通过「刷新」按钮或自动轮询请求\n\n🔑 **键**\n- `Heap` —— 数组 `[{ "Name", "Total", "Free", "MinFree" }]`,字节:内存堆 —— ESP 上为 `Internal` 内部和 `PSRAM` 外部 RAM,STM 上为 `Heap`,组件中显示易读名称;`MinFree` —— 启动以来的最小空闲\n- `Tasks` —— 数组 `[{ "Name", "State", "Prio", "Core", "FreeMin" }]`:任务;`State` —— `0` 运行、`1` 就绪、`2` 阻塞、`3` 挂起、`4` 已删除;`Core` —— 内核(仅 ESP32,`-1` 未绑定);`FreeMin` —— 最小栈余量,字节\n\n✨ **特点**\n- 堆条的浅色部分为峰值占用\n- 任务按栈余量排序 —— 风险最高的在上方\n- 折叠的组件不轮询设备\n\n⚙️ **设置**\n- 栈余量阈值:低于阈值 —— 红色,低于两倍 —— 橙色\n- 自动轮询:关 / 1 / 5 / 10 秒;主色',
|
|
131
|
+
ru: '🎯 **Назначение**\nОтладка прошивки: свободная память и запас стека задач.\n\n📦 **Пакет данных**\n- Один объект `StackInfo` из ответа на `GET StackInfo`, привязывать переменные не нужно\n- Запрос — кнопкой «Обновить» или автоопросом\n\n🔑 **Ключи**\n- `Heap` — массив `[{ "Name", "Total", "Free", "MinFree" }]`, байт: кучи памяти — `Internal` встроенная и `PSRAM` внешняя ОЗУ у ESP, `Heap` у STM, в виджете понятными подписями; `MinFree` — минимум свободного с момента старта\n- `Tasks` — массив `[{ "Name", "State", "Prio", "Core", "CPU", "FreeMin" }]`: задачи; `State` — `0` выполняется, `1` готова, `2` ждёт, `3` приостановлена, `4` удалена; `Core` — ядро (только ESP32, `-1` без привязки); `CPU` — загрузка задачи, % одного ядра с прошлого ответа; `FreeMin` — минимальный запас стека, байт\n\n✨ **Особенности**\n- Светлая часть полосы кучи — пик занятости\n- Задачи отсортированы по запасу стека — самые рискованные сверху\n- Загрузка от 50 % — оранжевым, от 80 % — красным (кроме `IDLE` — это свободное время ядра); колонка есть, если прошивка её присылает\n- Свёрнутый виджет не опрашивает устройство\n\n⚙️ **Настройки**\n- Порог запаса стека: меньше порога — красным, меньше удвоенного — оранжевым\n- Автоопрос: выкл / 1 / 5 / 10 с; мастер-цвет',
|
|
132
|
+
en: '🎯 **Purpose**\nFirmware debugging: free memory and task stack headroom.\n\n📦 **Data packet**\n- One `StackInfo` object from the `GET StackInfo` response, no variable binding needed\n- Requested with the "Refresh" button or by auto polling\n\n🔑 **Keys**\n- `Heap` — array `[{ "Name", "Total", "Free", "MinFree" }]`, bytes: memory heaps — `Internal` on-chip and `PSRAM` external RAM on ESP, `Heap` on STM, shown with readable names; `MinFree` — minimum free since boot\n- `Tasks` — array `[{ "Name", "State", "Prio", "Core", "CPU", "FreeMin" }]`: tasks; `State` — `0` running, `1` ready, `2` blocked, `3` suspended, `4` deleted; `Core` — core (ESP32 only, `-1` unpinned); `CPU` — task load, % of one core since the previous response; `FreeMin` — minimum stack headroom, bytes\n\n✨ **Features**\n- The light part of the heap bar is peak usage\n- Tasks are sorted by stack headroom — the riskiest on top\n- Load from 50 % — orange, from 80 % — red (except `IDLE` — that is free core time); the column appears if the firmware sends it\n- A collapsed widget does not poll the device\n\n⚙️ **Settings**\n- Stack threshold: below it — red, below double — orange\n- Auto polling: off / 1 / 5 / 10 s; master color',
|
|
133
|
+
zh: '🎯 **用途**\n固件调试:空闲内存和任务栈余量。\n\n📦 **数据包**\n- 来自 `GET StackInfo` 响应的一个 `StackInfo` 对象,无需绑定变量\n- 通过「刷新」按钮或自动轮询请求\n\n🔑 **键**\n- `Heap` —— 数组 `[{ "Name", "Total", "Free", "MinFree" }]`,字节:内存堆 —— ESP 上为 `Internal` 内部和 `PSRAM` 外部 RAM,STM 上为 `Heap`,组件中显示易读名称;`MinFree` —— 启动以来的最小空闲\n- `Tasks` —— 数组 `[{ "Name", "State", "Prio", "Core", "CPU", "FreeMin" }]`:任务;`State` —— `0` 运行、`1` 就绪、`2` 阻塞、`3` 挂起、`4` 已删除;`Core` —— 内核(仅 ESP32,`-1` 未绑定);`CPU` —— 任务负载,自上次响应以来占单个内核的百分比;`FreeMin` —— 最小栈余量,字节\n\n✨ **特点**\n- 堆条的浅色部分为峰值占用\n- 任务按栈余量排序 —— 风险最高的在上方\n- 负载 50 % 起为橙色,80 % 起为红色(`IDLE` 除外 —— 那是内核空闲时间);固件发送该字段时才显示此列\n- 折叠的组件不轮询设备\n\n⚙️ **设置**\n- 栈余量阈值:低于阈值 —— 红色,低于两倍 —— 橙色\n- 自动轮询:关 / 1 / 5 / 10 秒;主色',
|
|
134
134
|
},
|
|
135
135
|
/* Опции редактора свойств */
|
|
136
136
|
"constructor.props.action.save": { ru: "Сохранить", en: "Save", zh: "保存" },
|
|
@@ -502,6 +502,7 @@ const translations = {
|
|
|
502
502
|
"widget.stack.state": { ru: "Состояние", en: "State", zh: "状态" },
|
|
503
503
|
"widget.stack.prio": { ru: "Приор.", en: "Prio.", zh: "优先级" },
|
|
504
504
|
"widget.stack.core": { ru: "Ядро", en: "Core", zh: "核心" },
|
|
505
|
+
"widget.stack.cpu": { ru: "CPU, %", en: "CPU, %", zh: "CPU, %" },
|
|
505
506
|
"widget.stack.free_min": { ru: "Запас, Б", en: "Headroom, B", zh: "余量, B" },
|
|
506
507
|
"widget.stack.no_data": { ru: "Нет данных — нажмите «Обновить»", en: "No data — press “Refresh”", zh: "无数据 — 请点击“刷新”" },
|
|
507
508
|
"widget.stack.heap.name.Internal": { ru: "Встроенная ОЗУ", en: "Internal RAM", zh: "内部 RAM" },
|
|
@@ -915,6 +916,52 @@ const translations = {
|
|
|
915
916
|
"pfd.tip.vs": { ru: "Вертикальная скорость, м/с: {v}", en: "Vertical speed, m/s: {v}", zh: "垂直速度, m/s:{v}" },
|
|
916
917
|
"pfd.tip.vs.up": { ru: '"+" — набор высоты', en: '"+" — climb', zh: '"+" — 爬升' },
|
|
917
918
|
"pfd.tip.vs.down": { ru: '"−" — снижение', en: '"−" — descent', zh: '"−" — 下降' },
|
|
919
|
+
"pfd.tip.wind": { ru: "Ветер: {v} м/с, дует с {dir}°", en: "Wind: {v} m/s, from {dir}°", zh: "风:{v} m/s,来自 {dir}°" },
|
|
920
|
+
"pfd.tip.wind.note": {
|
|
921
|
+
ru: "Стрелка — куда дует ветер относительно носа аппарата",
|
|
922
|
+
en: "The arrow shows where the wind blows relative to the nose",
|
|
923
|
+
zh: "箭头 —— 相对机头的风吹去方向",
|
|
924
|
+
},
|
|
925
|
+
"pfd.tip.wind.dirs": {
|
|
926
|
+
ru: "Вниз — встречный, вверх — попутный, вбок — боковой (снос)",
|
|
927
|
+
en: "Down — headwind, up — tailwind, sideways — crosswind (drift)",
|
|
928
|
+
zh: "向下 —— 逆风,向上 —— 顺风,向侧面 —— 侧风(偏流)",
|
|
929
|
+
},
|
|
930
|
+
"pfd.tip.thr": { ru: "Газ: {v} %", en: "Throttle: {v} %", zh: "油门:{v} %" },
|
|
931
|
+
"pfd.tip.thr.note": {
|
|
932
|
+
ru: "Команда автопилота на двигатель, 100 % — полный газ",
|
|
933
|
+
en: "Autopilot throttle command, 100 % — full throttle",
|
|
934
|
+
zh: "自动驾驶仪油门指令,100 % — 全油门",
|
|
935
|
+
},
|
|
936
|
+
"pfd.tip.slip": { ru: "Боковая перегрузка, g: {v}", en: "Lateral load, g: {v}", zh: "侧向过载, g:{v}" },
|
|
937
|
+
"pfd.tip.slip.note": {
|
|
938
|
+
ru: "Шарик в центре — полёт без скольжения, разворот координирован",
|
|
939
|
+
en: "Ball centred — no sideslip, coordinated turn",
|
|
940
|
+
zh: "球居中 — 无侧滑,协调转弯",
|
|
941
|
+
},
|
|
942
|
+
"pfd.tip.slip.step": {
|
|
943
|
+
ru: "Шарик в стороне — скольжение или занос: руль направления в сторону шарика; оранжевый — больше 0.1 g",
|
|
944
|
+
en: "Ball off-centre — slip or skid: rudder towards the ball; orange — over 0.1 g",
|
|
945
|
+
zh: "球偏离 — 侧滑或外滑:方向舵朝球的方向;橙色 — 超过 0.1 g",
|
|
946
|
+
},
|
|
947
|
+
"pfd.tip.trend": { ru: "Тренд скорости: через 10 с — {v} м/с", en: "Speed trend: in 10 s — {v} m/s", zh: "速度趋势:10 秒后 — {v} m/s" },
|
|
948
|
+
"pfd.tip.trend.note": {
|
|
949
|
+
ru: "Жёлтая стрелка на ленте скорости — куда придёт путевая скорость при текущем ускорении",
|
|
950
|
+
en: "Yellow arrow on the speed tape — where ground speed will be at the current acceleration",
|
|
951
|
+
zh: "速度带上的黄色箭头 — 以当前加速度地速将到达的位置",
|
|
952
|
+
},
|
|
953
|
+
"pfd.tip.load": { ru: "Перегрузка, g: {v}", en: "Load factor, g: {v}", zh: "过载, g:{v}" },
|
|
954
|
+
"pfd.tip.load.note": {
|
|
955
|
+
ru: "1 g — горизонтальный полёт; больше — разворот, выход из снижения, порыв вверх; меньше — толчок вниз",
|
|
956
|
+
en: "1 g — level flight; more — turn, pull-up, upward gust; less — push-over",
|
|
957
|
+
zh: "1 g — 平飞;更大 — 转弯、拉起、上升阵风;更小 — 推杆",
|
|
958
|
+
},
|
|
959
|
+
"pfd.tip.load.range": { ru: "За время просмотра: мин {min}, макс {max}", en: "While viewing: min {min}, max {max}", zh: "查看期间:最小 {min},最大 {max}" },
|
|
960
|
+
"pfd.tip.load.limit": {
|
|
961
|
+
ru: "Допустимо {min} … {max} g — вне диапазона значение красное",
|
|
962
|
+
en: "Allowed {min} … {max} g — outside the range the value is red",
|
|
963
|
+
zh: "允许 {min} … {max} g — 超出范围数值变红",
|
|
964
|
+
},
|
|
918
965
|
"pfd.tip.as.note": { ru: "Скорость относительно воздуха", en: "Speed relative to the air", zh: "相对空气的速度" },
|
|
919
966
|
"pfd.tip.as.wind": { ru: "Отличается от путевой на ветер:", en: "Differs from ground speed by the wind:", zh: "与地速相差风速:" },
|
|
920
967
|
"pfd.tip.as.head": { ru: "встречный — путевая меньше", en: "headwind — ground speed is lower", zh: "逆风 — 地速较低" },
|
|
@@ -941,6 +988,11 @@ const translations = {
|
|
|
941
988
|
},
|
|
942
989
|
"pfd.tip.messages": { ru: "Сообщения автопилота, последние {n}:", en: "Autopilot messages, last {n}:", zh: "自动驾驶仪消息,最近 {n} 条:" },
|
|
943
990
|
"pfd.tip.route": { ru: "Маршрут: точка {n}", en: "Route: waypoint {n}", zh: "航线:航点 {n}" },
|
|
991
|
+
"pfd.tip.route.eta": {
|
|
992
|
+
ru: "Время до точки при текущей путевой скорости: {t}",
|
|
993
|
+
en: "Time to waypoint at current ground speed: {t}",
|
|
994
|
+
zh: "按当前地速到航点的时间:{t}",
|
|
995
|
+
},
|
|
944
996
|
"pfd.tip.route.dist": { ru: "Расстояние до точки: {d}", en: "Distance to waypoint: {d}", zh: "到航点距离:{d}" },
|
|
945
997
|
"pfd.tip.route.xte": {
|
|
946
998
|
ru: "Откл. — боковое отклонение от линии маршрута: {d}",
|
|
@@ -970,6 +1022,8 @@ const translations = {
|
|
|
970
1022
|
"pfd.props.speed_max": { ru: "Скорость макс., м/с", en: "Max speed, m/s", zh: "最大速度, m/s" },
|
|
971
1023
|
"pfd.props.bank_max": { ru: "Предельный крен, °", en: "Bank limit, °", zh: "横滚极限, °" },
|
|
972
1024
|
"pfd.props.alt_max": { ru: "Высота макс., м", en: "Max altitude, m", zh: "最大高度, m" },
|
|
1025
|
+
"pfd.props.g_min": { ru: "Перегрузка мин., g", en: "Min load, g", zh: "最小过载, g" },
|
|
1026
|
+
"pfd.props.g_max": { ru: "Перегрузка макс., g", en: "Max load, g", zh: "最大过载, g" },
|
|
973
1027
|
"pfd.props.batt_low": { ru: "Низкий заряд, %", en: "Low battery, %", zh: "低电量, %" },
|
|
974
1028
|
"pfd.props.stale": { ru: "Нет данных через, мс", en: "No data after, ms", zh: "无数据超时, 毫秒" },
|
|
975
1029
|
"constructor.texts.button": { ru: "Тексты и переводы", en: "Texts and translations", zh: "文本与翻译" },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 📡 Протокол POE на базе CBOR
|
|
2
2
|
|
|
3
3
|
Один бинарный пакет `poe_cbor_pkt` на всех проводах: CAN, CAN FD, RS485, LoRa и UART-консоль (xPOE-Debug). Отдельный конверт — только у WebSocket облака.
|
|
4
|
-
Реализации: прошивки ProdFactory-ESP / ProdFactory-STM (`components/core/poe_cbor`, `api_service`) и ядро `poe-svelte-ui-lib/protocols` (DevCloud,
|
|
4
|
+
Реализации: прошивки ProdFactory-ESP / ProdFactory-STM (`components/core/poe_cbor`, `api_service`) и ядро `poe-svelte-ui-lib/protocols` (DevCloud, DevTerminal, WebApp).
|
|
5
5
|
|
|
6
6
|
## 📦 Пакет
|
|
7
7
|
|
|
@@ -50,7 +50,6 @@
|
|
|
50
50
|
- [FwUpgD, A_UPGRADE_DONE, 53] # Завершение обновления прошивки по шине
|
|
51
51
|
- [FwUpgE, A_UPGRADE_ERR, 54] # Ошибка обновления прошивки по шине
|
|
52
52
|
|
|
53
|
-
- [TestReq, TEST_REQUEST, 91] # GET: тестовый запрос
|
|
54
53
|
|
|
55
54
|
# Работа с модулями перемещения (оси подвесов, линейные перемещения, серво приводы и подобные)
|
|
56
55
|
- [MotInit, A_MOTION_INIT, 111] # SET: включение/калибровка (1 байт: команда)
|
package/dist/protocols/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* $lib/protocols/index.ts */
|
|
2
|
-
/* Общее ядро протоколов обмена с устройствами для DevCloud, WebApp и
|
|
2
|
+
/* Общее ядро протоколов обмена с устройствами для DevCloud, WebApp и DevTerminal — без Svelte, точка входа "poe-svelte-ui-lib/protocols"
|
|
3
3
|
(условие default: работает и в браузере, и под Node без Vite); импорты с расширением .js, чтобы их разрешал сам Node */
|
|
4
4
|
export * from "./cbor/types.js";
|
|
5
5
|
export { bytesToBase64, base64ToBytes } from "./cbor/base64.js";
|
package/dist/types.d.ts
CHANGED
|
@@ -592,6 +592,7 @@ export interface IWidgetStackInfoTask {
|
|
|
592
592
|
Prio: number;
|
|
593
593
|
Core?: number;
|
|
594
594
|
State: number;
|
|
595
|
+
CPU?: number;
|
|
595
596
|
}
|
|
596
597
|
export interface IWidgetStackInfo {
|
|
597
598
|
Heap: IWidgetStackInfoHeap[];
|
|
@@ -644,6 +645,11 @@ export interface IWidgetPFDData {
|
|
|
644
645
|
Heading: number;
|
|
645
646
|
GSpeed: number;
|
|
646
647
|
ASpeed: number;
|
|
648
|
+
Load: number;
|
|
649
|
+
Slip: number;
|
|
650
|
+
Thr: number;
|
|
651
|
+
WindDir: number;
|
|
652
|
+
WindSpd: number;
|
|
647
653
|
Climb: number;
|
|
648
654
|
Alt: number;
|
|
649
655
|
AltMSL: number;
|
|
@@ -684,6 +690,8 @@ export interface IWidgetPFDLimits {
|
|
|
684
690
|
speedMax?: number;
|
|
685
691
|
bankMax?: number;
|
|
686
692
|
altMax?: number;
|
|
693
|
+
gMin?: number;
|
|
694
|
+
gMax?: number;
|
|
687
695
|
battLow?: number;
|
|
688
696
|
staleMs?: number;
|
|
689
697
|
}
|