poe-svelte-ui-lib 1.80.0 → 1.82.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/WidgetDeviceInfo/WidgetDeviceInfo.svelte +7 -1
- package/dist/WidgetRGBStrip/README.md +1 -1
- package/dist/WidgetRGBStrip/WidgetRGBStrip.svelte +21 -2
- package/dist/WidgetStackInfo/README.md +5 -5
- package/dist/WidgetStackInfo/WidgetStackInfo.svelte +17 -2
- package/dist/WidgetWiFi/WidgetWiFi.svelte +4 -2
- package/dist/locales/translations.js +4 -3
- package/dist/protocols/PROTOCOL.md +7 -7
- 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 +2 -1
- 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
|
|
|
@@ -153,7 +153,13 @@
|
|
|
153
153
|
<UI.Input label={{ name: $T("widget.info.web_user") }} bind:value={info.WebUser} maxlength={16} />
|
|
154
154
|
</div>
|
|
155
155
|
<div use:hint={{ text: $T("widget.info.hint.web_psw"), enabled: $hints }}>
|
|
156
|
-
<UI.Input
|
|
156
|
+
<UI.Input
|
|
157
|
+
type="password"
|
|
158
|
+
label={{ name: $T("widget.info.web_psw") }}
|
|
159
|
+
bind:value={info.WebPsw}
|
|
160
|
+
maxlength={16}
|
|
161
|
+
help={{ autocomplete: "one-time-code" }}
|
|
162
|
+
/>
|
|
157
163
|
</div>
|
|
158
164
|
</div>
|
|
159
165
|
</div>
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
|
|
45
45
|
- **Свечение** — палитра цвета, белый канал (только для модели SK6812), эффект и скорость. Каждое изменение сразу уходит в `onChange`.
|
|
46
46
|
- **Оборудование** — количество светодиодов (1…`MaxCount`), модель чипа и порядок цветов. Редактируются локально и отправляются одним пакетом по «Сохранить»: прошивка пересоздаёт драйвер ленты. Пока правок нет, поля показывают свежие значения с устройства.
|
|
47
|
-
- В шапке — образец
|
|
47
|
+
- В шапке — образец цвета первого светодиода (`Color`): оттенок без учёта яркости, как превью палитры; выбранный цвет показывается сразу, до ответа устройства. Эффекты Rainbow/Palette/Fire цвет пикселей не сообщают — образец остаётся базовым `Color`.
|
|
48
48
|
|
|
49
49
|
## Пропсы
|
|
50
50
|
|
|
@@ -80,7 +80,26 @@
|
|
|
80
80
|
onSave?.($state.snapshot(hardware))
|
|
81
81
|
savedSnapshot = JSON.stringify(hardware)
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
|
|
84
|
+
/* Образец в шапке — цвет первого светодиода: отправленный сразу, до ответа устройства; новое значение с устройства его заменяет */
|
|
85
|
+
let sentColor: number[] | null = $state(null)
|
|
86
|
+
let colorKey = $derived(color.join(","))
|
|
87
|
+
$effect(() => {
|
|
88
|
+
void colorKey
|
|
89
|
+
sentColor = null
|
|
90
|
+
})
|
|
91
|
+
/* Как превью палитры: оттенок без учёта яркости (тусклый светодиод светится тем же цветом), белый — своим уровнем */
|
|
92
|
+
let headerColor = $derived.by(() => {
|
|
93
|
+
const [r, g, b] = sentColor ?? color
|
|
94
|
+
const max = Math.max(r, g, b)
|
|
95
|
+
if (max === 0 || (r === g && g === b)) return [r, g, b]
|
|
96
|
+
return [r, g, b].map((c) => Math.round((c / max) * 255))
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const sendColor = (rgbw: number[]) => {
|
|
100
|
+
sentColor = rgbw
|
|
101
|
+
onChange?.("Color", rgbw)
|
|
102
|
+
}
|
|
84
103
|
</script>
|
|
85
104
|
|
|
86
105
|
<div
|
|
@@ -92,7 +111,7 @@
|
|
|
92
111
|
{#snippet right()}
|
|
93
112
|
<span
|
|
94
113
|
class="size-6 rounded-md border border-(--hairline-color)"
|
|
95
|
-
style="background: rgb({
|
|
114
|
+
style="background: rgb({headerColor[0]}, {headerColor[1]}, {headerColor[2]})"
|
|
96
115
|
title={$T("widget.rgb.current_color")}
|
|
97
116
|
></span>
|
|
98
117
|
{/snippet}
|
|
@@ -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>
|
|
@@ -187,12 +187,14 @@
|
|
|
187
187
|
/>
|
|
188
188
|
</div>
|
|
189
189
|
|
|
190
|
+
<!-- one-time-code: менеджер паролей браузера не считает поле паролем входа и не накрывает список сетей у SSID
|
|
191
|
+
своими сохранёнными логинами (autocomplete="off" для паролей Chrome игнорирует) -->
|
|
190
192
|
<UI.Input
|
|
191
193
|
type="password"
|
|
192
194
|
label={{ name: $T("widget.common.password") }}
|
|
193
195
|
bind:value={cfg.StaPSK}
|
|
194
196
|
maxlength={64}
|
|
195
|
-
help={{ autocomplete: "
|
|
197
|
+
help={{ autocomplete: "one-time-code", regExp: PSK_REGEXP }}
|
|
196
198
|
/>
|
|
197
199
|
|
|
198
200
|
<div class="w-48" use:hint={{ text: $T("widget.wifi.hint.ip_mode"), enabled: $hints }}>
|
|
@@ -254,7 +256,7 @@
|
|
|
254
256
|
label={{ name: $T("widget.common.password") }}
|
|
255
257
|
bind:value={cfg.ApPSK}
|
|
256
258
|
maxlength={64}
|
|
257
|
-
help={{ autocomplete: "
|
|
259
|
+
help={{ autocomplete: "one-time-code", regExp: PSK_REGEXP }}
|
|
258
260
|
/>
|
|
259
261
|
</div>
|
|
260
262
|
|
|
@@ -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" },
|
|
@@ -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
|
|
|
@@ -59,12 +59,12 @@
|
|
|
59
59
|
|
|
60
60
|
## 🚌 Транспорты
|
|
61
61
|
|
|
62
|
-
| Канал | Передача
|
|
63
|
-
| ------------ |
|
|
64
|
-
| CAN / CAN FD | Кадры по 8 / 64 байта; 11-битный ID: бит 10 — последний фрагмент, биты 9-0 — номер пакета; паддинг CAN FD отрезается по длине пакета
|
|
65
|
-
| RS485 | Сырой поток: начало `0x9F 0x85`, пауза приёма (20
|
|
66
|
-
| LoRa | Пакет целиком в радиокадре
|
|
67
|
-
| UART-консоль | Сырой поток вперемешку с текстом загрузчика: начало `0x9F 0x85`, остальное — текст (RAW), хвост сбрасывается после 100 мс тишины
|
|
62
|
+
| Канал | Передача |
|
|
63
|
+
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
64
|
+
| CAN / CAN FD | Кадры по 8 / 64 байта; 11-битный ID: бит 10 — последний фрагмент, биты 9-0 — номер пакета; паддинг CAN FD отрезается по длине пакета |
|
|
65
|
+
| RS485 | Сырой поток: начало `0x9F 0x85`, пауза приёма сбрасывает недособранный хвост (прошивка — 20 мс, `createPacketStreamParser` на ПК — 100 мс: USB-адаптер отдаёт байты пачками), мусор сдвигается по байту |
|
|
66
|
+
| LoRa | Пакет целиком в радиокадре |
|
|
67
|
+
| UART-консоль | Сырой поток вперемешку с текстом загрузчика: начало `0x9F 0x85`, остальное — текст (RAW), хвост сбрасывается после 100 мс тишины |
|
|
68
68
|
|
|
69
69
|
## 🐞 Консоль: отладочные пакеты
|
|
70
70
|
|
|
@@ -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
|
@@ -205,7 +205,7 @@ export interface IInputProps {
|
|
|
205
205
|
info?: string;
|
|
206
206
|
copyButton?: boolean;
|
|
207
207
|
regExp?: string | RegExp;
|
|
208
|
-
autocomplete?: "on" | "off" | "given-name" | "family-name" | "nickname" | "username" | "new-password" | "current-password" | "organization-title" | "country-name" | "address-level1" | "address-level2" | "street-address" | "postal-code" | "email" | "tel" | null;
|
|
208
|
+
autocomplete?: "on" | "off" | "given-name" | "family-name" | "nickname" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "country-name" | "address-level1" | "address-level2" | "street-address" | "postal-code" | "email" | "tel" | null;
|
|
209
209
|
};
|
|
210
210
|
onUpdate?: (value: string | number) => void;
|
|
211
211
|
}
|
|
@@ -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[];
|