poe-svelte-ui-lib 1.74.2 → 1.75.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 +1 -1
- package/dist/Table/xPoeDebugColumns.d.ts +3 -0
- package/dist/Table/xPoeDebugColumns.js +17 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/protocols/crc.d.ts +0 -1
- package/dist/protocols/crc.js +1 -10
- package/dist/protocols/dictionary.js +1 -1
- package/dist/protocols/dictionary.yaml +3 -3
- package/dist/protocols/dictionaryCodes.js +1 -1
- package/dist/protocols/index.d.ts +4 -4
- package/dist/protocols/index.js +8 -8
- package/dist/protocols/packet.d.ts +6 -3
- package/dist/protocols/packet.js +31 -24
- package/dist/protocols/plotGroups.d.ts +2 -2
- package/dist/protocols/plotGroups.js +5 -5
- package/dist/protocols/xPoeDebug.d.ts +16 -3
- package/dist/protocols/xPoeDebug.js +72 -94
- package/dist/protocols/xPoeDebugView.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -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` (без Svelte, работает и под Node без Vite): общий для DevCloud, WebApp и SYP-DevTerminal CBOR-кодек (RFC 8949) — `encodeCbor`/`decodeCbor`, `CborEncoder`/`CborDecoder`; `valueToCbor`/`cborToValue` — обычные значения, двоичные данные как `Uint8Array` (WebSocket, хранилище, телеметрия); `jsonToCbor`/`cborToJson` — JSON-совместимые значения, двоичные данные в base64, строки под ключами с `BIN` — байты (поля ввода терминала и отладки). Пакет `poe_cbor_pkt` — indefinite-массив объектов `[[TrgBusID, SrcBusID,
|
|
64
|
+
**Ядро протоколов** — `poe-svelte-ui-lib/protocols` (без Svelte, работает и под Node без Vite): общий для DevCloud, WebApp и SYP-DevTerminal CBOR-кодек (RFC 8949) — `encodeCbor`/`decodeCbor`, `CborEncoder`/`CborDecoder`; `valueToCbor`/`cborToValue` — обычные значения, двоичные данные как `Uint8Array` (WebSocket, хранилище, телеметрия); `jsonToCbor`/`cborToJson` — JSON-совместимые значения, двоичные данные в 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`), `objectsFor` (свой адрес и broadcast `BROADCAST_BUS_ID = 0xFFFF`), `EXTERNAL_BUS_ID = 0x0000` — `SrcBusID` терминала и отладки, `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` (основной вход библиотеки): общие для отладки облака и терминала. 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` (`SC_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
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { formatXPoeDebugModal } from "../protocols/index.js";
|
|
2
|
+
import { tableMonoWidth } from "./columnWidth";
|
|
3
|
+
export const xPoeDebugTableColumns = [
|
|
4
|
+
{ label: { name: "TRG" }, content: [{ type: "text", data: { key: "trgId" } }], width: tableMonoWidth(6, 3), align: "center" },
|
|
5
|
+
{ label: { name: "SRC" }, content: [{ type: "text", data: { key: "srcId" } }], width: tableMonoWidth(6, 3), align: "center" },
|
|
6
|
+
{ label: { name: "HAR" }, content: [{ type: "text", data: { key: "har" } }], width: tableMonoWidth(4, 3), align: "center" },
|
|
7
|
+
{ label: { name: "HDR" }, content: [{ type: "text", data: { key: "header" } }], width: tableMonoWidth(4, 3), align: "center" },
|
|
8
|
+
{ label: { name: "ARG" }, content: [{ type: "text", data: { key: "argument" } }], width: tableMonoWidth(16, 3), align: "left" },
|
|
9
|
+
{
|
|
10
|
+
label: { name: "VALUE" },
|
|
11
|
+
content: [{ type: "text", data: { key: "value", truncated: true, formatting: formatXPoeDebugModal, copy: true, modal: true } }],
|
|
12
|
+
width: "1fr",
|
|
13
|
+
align: "left",
|
|
14
|
+
},
|
|
15
|
+
/* Свободная куча устройства из DebugLog */
|
|
16
|
+
{ label: { name: "FHS" }, content: [{ type: "text", data: { key: "freeHeap" } }], width: tableMonoWidth(7, 3), align: "center" },
|
|
17
|
+
];
|
package/dist/index.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export { default as SwitchProps } from "./Switch/SwitchProps.svelte";
|
|
|
29
29
|
export { default as Table } from "./Table/Table.svelte";
|
|
30
30
|
export { default as TableProps } from "./Table/TableProps.svelte";
|
|
31
31
|
export { tableMonoWidth } from "./Table/columnWidth";
|
|
32
|
+
export { xPoeDebugTableColumns } from "./Table/xPoeDebugColumns";
|
|
32
33
|
export { bitFieldGet, bitFieldSet } from "./bitField";
|
|
33
34
|
export { default as Tabs } from "./Tabs/Tabs.svelte";
|
|
34
35
|
export { default as TabsProps } from "./Tabs/TabsProps.svelte";
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ export { default as SwitchProps } from "./Switch/SwitchProps.svelte";
|
|
|
30
30
|
export { default as Table } from "./Table/Table.svelte";
|
|
31
31
|
export { default as TableProps } from "./Table/TableProps.svelte";
|
|
32
32
|
export { tableMonoWidth } from "./Table/columnWidth";
|
|
33
|
+
export { xPoeDebugTableColumns } from "./Table/xPoeDebugColumns";
|
|
33
34
|
export { bitFieldGet, bitFieldSet } from "./bitField";
|
|
34
35
|
export { default as Tabs } from "./Tabs/Tabs.svelte";
|
|
35
36
|
export { default as TabsProps } from "./Tabs/TabsProps.svelte";
|
package/dist/protocols/crc.d.ts
CHANGED
package/dist/protocols/crc.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* $lib/protocols/crc.ts */
|
|
2
|
-
/* Контрольные суммы протоколов устройств — строго побайтные, как
|
|
2
|
+
/* Контрольные суммы протоколов устройств — строго побайтные, как crc16_kermit в common.c прошивок (ProdFactory-ESP/STM) */
|
|
3
3
|
/* CRC-16/KERMIT: poly 0x1021 reflected (0x8408), init 0x0000, xorout 0x0000, check("123456789") = 0x2189 — объекты пакета poe_cbor_pkt */
|
|
4
4
|
export const crc16Kermit = (crc, bytes) => {
|
|
5
5
|
for (const byte of bytes) {
|
|
@@ -9,12 +9,3 @@ export const crc16Kermit = (crc, bytes) => {
|
|
|
9
9
|
}
|
|
10
10
|
return crc & 0xffff;
|
|
11
11
|
};
|
|
12
|
-
/* CRC-8/ROHC remains available for legacy consumers. System Console frames use CRC-16/KERMIT over CBOR bytes. */
|
|
13
|
-
export const crc8Rohc = (crc, bytes) => {
|
|
14
|
-
for (const byte of bytes) {
|
|
15
|
-
crc ^= byte & 0xff;
|
|
16
|
-
for (let bit = 0; bit < 8; bit++)
|
|
17
|
-
crc = crc & 1 ? (crc >>> 1) ^ 0xe0 : crc >>> 1;
|
|
18
|
-
}
|
|
19
|
-
return crc & 0xff;
|
|
20
|
-
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* $lib/protocols/dictionary.ts */
|
|
2
|
-
/* Словарь заголовков и аргументов протокола — заголовок и аргумент поля
|
|
2
|
+
/* Словарь заголовков и аргументов протокола — заголовок и аргумент поля HAR пакета (makeHar/splitHar). Аргументы по умолчанию —
|
|
3
3
|
из мастер-словаря dictionary.yaml рядом (единственный источник; .framework/Dictionary.yaml прошивок — его справочные копии), DEFAULT_ARGUMENT_CODES
|
|
4
4
|
генерируется из него (dictionaryCodes.ts, npm run Dictionary); у устройства словарь может быть свой — его загружают из файла
|
|
5
5
|
(parseArgumentDictionary), своих копий словаря в приложениях не держим */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Словарь аргументов протокола (поле arg в
|
|
1
|
+
# Словарь аргументов протокола (поле arg в HAR, 0..8191) — единый для всех устройств, обоих фреймворков и приложений
|
|
2
2
|
# Строковое представление аргумента | Название в перечислении | Числовой код
|
|
3
3
|
# Источник — poe-svelte-ui-lib (src/lib/protocols/dictionary.yaml); .framework/Dictionary.yaml фреймворков — копии, не править
|
|
4
4
|
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
- [StackInfo, A_STACK_INFO, 8] # GET: снимок куч и стеков задач
|
|
16
16
|
- [BackUp, A_BACKUP, 9] # Резервная копия конфигурации
|
|
17
17
|
- [BindMKey, A_BIND_MOD_KEY, 10] # SET: привязка ключа модуля (ModBindKey)
|
|
18
|
-
- [DebugLog, A_DEBUG_LOG, 11] # OK!:
|
|
19
|
-
- [
|
|
18
|
+
- [DebugLog, A_DEBUG_LOG, 11] # OK!: сообщение отладки — CBOR-массив [уровень, тег, текст|байты, FHS]
|
|
19
|
+
- [DebugPlot, A_DEBUG_PLOT, 12] # OK!: значения графиков отладки — CBOR-карта {"Имя": float32} (SC_PLOT)
|
|
20
20
|
- [DataStream, A_DATA_STREAM, 15] # OK!: поток данных групп конфигурации (CS_PushGroupStream)
|
|
21
21
|
|
|
22
22
|
# Локальная сеть
|
|
@@ -4,10 +4,10 @@ export { CborEncoder, encodeCbor } from "./cbor/encoder.js";
|
|
|
4
4
|
export { bytesToBase64, base64ToBytes } from "./cbor/base64.js";
|
|
5
5
|
export { CborToValue, ValueToCbor, cborToValue, valueToCbor } from "./cbor/value.js";
|
|
6
6
|
export { CborToJson, JsonToCbor, cborToJson, jsonToCbor } from "./cbor/json.js";
|
|
7
|
-
export { crc16Kermit
|
|
8
|
-
export { BROADCAST_BUS_ID,
|
|
9
|
-
export { createPlotGroups,
|
|
10
|
-
export { encodeXPoeDebugFrame, createXPoeDebugParser,
|
|
7
|
+
export { crc16Kermit } from "./crc.js";
|
|
8
|
+
export { BROADCAST_BUS_ID, BUS_ID_REGEXP, parseBusId, formatBusId, makeHar, splitHar, EXTERNAL_BUS_ID, packetObjectCrc, encodePacketBytesPayload, encodePacket, decodePacket, objectsFor, createPacketStreamParser, type PacketObject, type DecodedPacketObject, type PacketParse, } from "./packet.js";
|
|
9
|
+
export { createPlotGroups, debugPlotValues, DEBUG_PLOT_ARGUMENT, type PlotPoint } from "./plotGroups.js";
|
|
10
|
+
export { encodeXPoeDebugFrame, createXPoeDebugParser, xPoeDebugHar, xPoeDebugValueFromText, xPoeDebugRow, DEBUG_LOG_ARGUMENT, type XPoeDebugItem, type XPoeDebugRow, } from "./xPoeDebug.js";
|
|
11
11
|
export { formatXPoeDebugValue, formatXPoeDebugValueForTable, formatXPoeDebugModal, bindXPoeDebugCopyButtons } from "./xPoeDebugView.js";
|
|
12
12
|
export { CAN_FINAL_BIT, CAN_PACKET_ID_MASK, canFragments, encodeSlcanFrame, encodeCanPacket, createCanParser, slcanOpenCommands, type CanFrame, type CanMessage, } from "./can.js";
|
|
13
13
|
export { HEADER_CODES, DEFAULT_ARGUMENT_CODES, parseArgumentDictionary, nameByCode } from "./dictionary.js";
|
package/dist/protocols/index.js
CHANGED
|
@@ -9,16 +9,16 @@ export { bytesToBase64, base64ToBytes } from "./cbor/base64.js";
|
|
|
9
9
|
export { CborToValue, ValueToCbor, cborToValue, valueToCbor } from "./cbor/value.js";
|
|
10
10
|
/* JSON-текст ↔ CBOR: двоичные данные — base64, ключи с "BIN" кодируются байтами (поля ввода терминала и отладки) */
|
|
11
11
|
export { CborToJson, JsonToCbor, cborToJson, jsonToCbor } from "./cbor/json.js";
|
|
12
|
-
/*
|
|
13
|
-
export { crc16Kermit
|
|
14
|
-
export { BROADCAST_BUS_ID,
|
|
15
|
-
/* Графики отладки:
|
|
16
|
-
export { createPlotGroups,
|
|
17
|
-
/*
|
|
18
|
-
export { encodeXPoeDebugFrame, createXPoeDebugParser,
|
|
12
|
+
/* Контрольная сумма и пакет poe_cbor_pkt [[TrgBusID, SrcBusID, HAR, Payload, CRC16], …] — все шины и консоль */
|
|
13
|
+
export { crc16Kermit } from "./crc.js";
|
|
14
|
+
export { BROADCAST_BUS_ID, BUS_ID_REGEXP, parseBusId, formatBusId, makeHar, splitHar, EXTERNAL_BUS_ID, packetObjectCrc, encodePacketBytesPayload, encodePacket, decodePacket, objectsFor, createPacketStreamParser, } from "./packet.js";
|
|
15
|
+
/* Графики отладки: пакет OK! DebugPlot (SC_PLOT прошивки) — значения из CBOR-карты, график на группу, серии в порядке устройства */
|
|
16
|
+
export { createPlotGroups, debugPlotValues, DEBUG_PLOT_ARGUMENT } from "./plotGroups.js";
|
|
17
|
+
/* xPOE-Debug — UART-консоль как шина: пакеты вперемешку с текстом загрузчика, логи OK! DebugLog */
|
|
18
|
+
export { encodeXPoeDebugFrame, createXPoeDebugParser, xPoeDebugHar, xPoeDebugValueFromText, xPoeDebugRow, DEBUG_LOG_ARGUMENT, } from "./xPoeDebug.js";
|
|
19
19
|
/* Отображение Value xPOE-Debug в таблице/модалке и копирование Value/HEX */
|
|
20
20
|
export { formatXPoeDebugValue, formatXPoeDebugValueForTable, formatXPoeDebugModal, bindXPoeDebugCopyButtons } from "./xPoeDebugView.js";
|
|
21
21
|
/* CAN/CAN FD через slcan-адаптер: нарезка пакета на кадры как в can_service прошивки, кадры slcan, потоковый разбор со сборкой фрагментов */
|
|
22
22
|
export { CAN_FINAL_BIT, CAN_PACKET_ID_MASK, canFragments, encodeSlcanFrame, encodeCanPacket, createCanParser, slcanOpenCommands, } from "./can.js";
|
|
23
|
-
/* Словарь заголовков и аргументов
|
|
23
|
+
/* Словарь заголовков и аргументов HAR — по умолчанию из мастер-словаря dictionary.yaml библиотеки, разбор файла словаря */
|
|
24
24
|
export { HEADER_CODES, DEFAULT_ARGUMENT_CODES, parseArgumentDictionary, nameByCode } from "./dictionary.js";
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
export declare const BROADCAST_BUS_ID = 65535;
|
|
2
2
|
export declare const EXTERNAL_BUS_ID = 0;
|
|
3
|
+
export declare const BUS_ID_REGEXP: RegExp;
|
|
4
|
+
export declare const parseBusId: (text: string | undefined, fallback?: number) => number;
|
|
5
|
+
export declare const formatBusId: (id: number) => string;
|
|
3
6
|
export interface PacketObject {
|
|
4
7
|
trgBusID: number;
|
|
5
8
|
srcBusID: number;
|
|
6
|
-
|
|
9
|
+
har: number;
|
|
7
10
|
payload: Uint8Array;
|
|
8
11
|
}
|
|
9
12
|
export interface DecodedPacketObject extends PacketObject {
|
|
@@ -13,8 +16,8 @@ export interface DecodedPacketObject extends PacketObject {
|
|
|
13
16
|
crc: number;
|
|
14
17
|
crcOk: boolean;
|
|
15
18
|
}
|
|
16
|
-
export declare const
|
|
17
|
-
export declare const
|
|
19
|
+
export declare const makeHar: (header: number, argument: number, reply?: boolean) => number;
|
|
20
|
+
export declare const splitHar: (har: number) => {
|
|
18
21
|
header: number;
|
|
19
22
|
argument: number;
|
|
20
23
|
reply: boolean;
|
package/dist/protocols/packet.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* $lib/protocols/packet.ts */
|
|
2
|
-
/* Общий пакет poe_cbor_pkt
|
|
3
|
-
Payload — ровно
|
|
2
|
+
/* Общий пакет poe_cbor_pkt всех шин и консоли: indefinite ARRAY записей [TrgBusID, SrcBusID, HAR, Payload, CRC16].
|
|
3
|
+
Payload — ровно одно CBOR item (сырые данные — байтовая строка), пустой — null. CRC-16/KERMIT — по CBOR-байтам записи от её заголовка до конца Payload */
|
|
4
4
|
import { CborDecoder } from "./cbor/decoder.js";
|
|
5
5
|
import { CborEncoder } from "./cbor/encoder.js";
|
|
6
6
|
import { CborEndOfDataError } from "./cbor/types.js";
|
|
@@ -9,37 +9,43 @@ import { crc16Kermit } from "./crc.js";
|
|
|
9
9
|
export const BROADCAST_BUS_ID = 0xffff;
|
|
10
10
|
/* Адрес внешнего клиента (терминал, отладка облака) — у модулей такого адреса не бывает */
|
|
11
11
|
export const EXTERNAL_BUS_ID = 0x0000;
|
|
12
|
-
/*
|
|
13
|
-
export const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
/* BusID в полях ввода — HEX без "0x", 1-4 знака (uint16) */
|
|
13
|
+
export const BUS_ID_REGEXP = /^[0-9A-Fa-f]{1,4}$/;
|
|
14
|
+
/* HEX-поле BusID → число; пусто или не HEX — fallback */
|
|
15
|
+
export const parseBusId = (text, fallback = 0) => (text && BUS_ID_REGEXP.test(text) ? parseInt(text, 16) : fallback);
|
|
16
|
+
/* BusID для таблиц — 0x0000 */
|
|
17
|
+
export const formatBusId = (id) => `0x${id.toString(16).toUpperCase().padStart(4, "0")}`;
|
|
18
|
+
/* HAR = заголовок (0..3) в битах 14-15, аргумент (0..8191) в битах 1-13, флаг ответа в бите 0 — как HAR/HAR_NR в api_har.h прошивки */
|
|
19
|
+
export const makeHar = (header, argument, reply = true) => ((header & 0x03) << 14) | ((argument & 0x1fff) << 1) | (reply ? 1 : 0);
|
|
20
|
+
export const splitHar = (har) => ({
|
|
21
|
+
header: (har >> 14) & 0x03,
|
|
22
|
+
argument: (har >> 1) & 0x1fff,
|
|
23
|
+
reply: (har & 1) === 1,
|
|
18
24
|
});
|
|
25
|
+
const NULL_PAYLOAD = Uint8Array.of(0xf6);
|
|
19
26
|
export const packetObjectCrc = (encodedRecordPrefix) => crc16Kermit(0, encodedRecordPrefix);
|
|
20
|
-
/*
|
|
27
|
+
/* Сырые байты одним CBOR item Payload — байтовой строкой, как CBOR_PACK прошивки */
|
|
21
28
|
export const encodePacketBytesPayload = (bytes) => {
|
|
22
29
|
const encoder = new CborEncoder();
|
|
23
30
|
encoder.encodeBytes(bytes);
|
|
24
31
|
return encoder.getResult();
|
|
25
32
|
};
|
|
26
|
-
/* Пакет из одного или нескольких объектов — те же байты, что
|
|
33
|
+
/* Пакет из одного или нескольких объектов — те же байты, что CBOR_PktEncAddRaw/CBOR_PktEncFinish прошивки */
|
|
27
34
|
export const encodePacket = (objects) => {
|
|
28
35
|
const encoder = new CborEncoder();
|
|
29
36
|
encoder.encodeArrayStart(null);
|
|
30
|
-
for (const { trgBusID, srcBusID,
|
|
37
|
+
for (const { trgBusID, srcBusID, har, payload } of objects) {
|
|
38
|
+
const item = payload.length ? payload : NULL_PAYLOAD;
|
|
39
|
+
const payloadDecoder = new CborDecoder(item);
|
|
40
|
+
if (!payloadDecoder.skipValue() || payloadDecoder.offset !== item.length)
|
|
41
|
+
throw new Error("Payload должен содержать ровно одно CBOR item");
|
|
31
42
|
const recordStart = encoder.getResult().length;
|
|
32
43
|
encoder.encodeArrayStart(5);
|
|
33
44
|
encoder.encodeUnsigned(trgBusID);
|
|
34
45
|
encoder.encodeUnsigned(srcBusID);
|
|
35
|
-
encoder.encodeUnsigned(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const payloadDecoder = new CborDecoder(payload);
|
|
39
|
-
if (!payloadDecoder.skipValue() || payloadDecoder.offset !== payload.length)
|
|
40
|
-
throw new Error("Payload должен содержать ровно один CBOR item");
|
|
41
|
-
encoder.encodeRaw(payload);
|
|
42
|
-
const prefix = encoder.getResult().slice(recordStart, prefixStart + payload.length);
|
|
46
|
+
encoder.encodeUnsigned(har);
|
|
47
|
+
encoder.encodeRaw(item);
|
|
48
|
+
const prefix = encoder.getResult().slice(recordStart);
|
|
43
49
|
encoder.encodeUnsigned(packetObjectCrc(prefix));
|
|
44
50
|
}
|
|
45
51
|
encoder.encodeBreak();
|
|
@@ -63,12 +69,12 @@ const toNumber = (value) => (typeof value === "number" && Number.isInteger(value
|
|
|
63
69
|
const toObject = (value, rawRecord) => {
|
|
64
70
|
if (!Array.isArray(value) || value.length !== 5)
|
|
65
71
|
return null;
|
|
66
|
-
const [trgBusID, srcBusID,
|
|
67
|
-
if (trgBusID === null || srcBusID === null ||
|
|
72
|
+
const [trgBusID, srcBusID, har, crc] = [toNumber(value[0]), toNumber(value[1]), toNumber(value[2]), toNumber(value[4])];
|
|
73
|
+
if (trgBusID === null || srcBusID === null || har === null || crc === null)
|
|
68
74
|
return null;
|
|
69
75
|
const info = rawRecord[0] & 0x1f;
|
|
70
76
|
const headerLength = info < 24 ? 1 : info === 24 ? 2 : info === 25 ? 3 : info === 26 ? 5 : info === 27 ? 9 : 0;
|
|
71
|
-
if (
|
|
77
|
+
if (rawRecord[0] >> 5 !== 4 || headerLength === 0)
|
|
72
78
|
return null;
|
|
73
79
|
const fields = new CborDecoder(rawRecord.subarray(headerLength));
|
|
74
80
|
for (let i = 0; i < 3; i++)
|
|
@@ -79,8 +85,9 @@ const toObject = (value, rawRecord) => {
|
|
|
79
85
|
return null;
|
|
80
86
|
const payloadEnd = headerLength + fields.offset;
|
|
81
87
|
const prefix = rawRecord.subarray(0, payloadEnd);
|
|
82
|
-
const
|
|
83
|
-
|
|
88
|
+
const item = rawRecord.subarray(payloadStart, payloadEnd);
|
|
89
|
+
const payload = item.length === 1 && item[0] === 0xf6 ? new Uint8Array() : item;
|
|
90
|
+
return { trgBusID, srcBusID, har, payload, crc, crcOk: packetObjectCrc(prefix) === crc, ...splitHar(har) };
|
|
84
91
|
};
|
|
85
92
|
/* Разбор пакета с начала буфера — и для целого кадра (CAN), и для потока (RS485: «incomplete» — ждать ещё байт, «garbage» — сдвинуться на байт) */
|
|
86
93
|
export const decodePacket = (bytes) => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const DEBUG_PLOT_ARGUMENT = "DebugPlot";
|
|
2
2
|
export interface PlotPoint {
|
|
3
3
|
name: string;
|
|
4
4
|
value: number;
|
|
5
5
|
}
|
|
6
|
-
export declare const
|
|
6
|
+
export declare const debugPlotValues: (value: string) => [string, number][];
|
|
7
7
|
export declare const createPlotGroups: () => {
|
|
8
8
|
update(values: [string, number][]): string[];
|
|
9
9
|
data(group: string): PlotPoint[];
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/* $lib/protocols/plotGroups.ts */
|
|
2
|
-
/* Графики отладки:
|
|
2
|
+
/* Графики отладки: пакет xPOE-Debug OK! DebugPlot (SC_PLOT прошивки), Payload — CBOR-карта {"Группа.Имя": float32, …} в порядке вызова.
|
|
3
3
|
График на группу — префикс имени до первой точки, без префикса — общая группа ""; серии в порядке карты, у серий, которых в кадре нет, — после них */
|
|
4
4
|
import { cborToValue } from "./cbor/value.js";
|
|
5
5
|
import { xPoeDebugValueFromText } from "./xPoeDebug.js";
|
|
6
|
-
export const
|
|
7
|
-
/* Значения графиков из
|
|
8
|
-
export const
|
|
6
|
+
export const DEBUG_PLOT_ARGUMENT = "DebugPlot";
|
|
7
|
+
/* Значения графиков из Payload пакета DebugPlot (HEX CBOR-карты); не карта — пустой список */
|
|
8
|
+
export const debugPlotValues = (value) => {
|
|
9
9
|
try {
|
|
10
10
|
const decoded = cborToValue(xPoeDebugValueFromText(value));
|
|
11
11
|
const entries = decoded instanceof Map ? [...decoded] : decoded && typeof decoded === "object" ? Object.entries(decoded) : [];
|
|
@@ -18,7 +18,7 @@ export const debPlotValues = (value) => {
|
|
|
18
18
|
export const createPlotGroups = () => {
|
|
19
19
|
const groups = new Map();
|
|
20
20
|
return {
|
|
21
|
-
/* Значения одного
|
|
21
|
+
/* Значения одного пакета; возвращает группы, которые обновились */
|
|
22
22
|
update(values) {
|
|
23
23
|
const fresh = new Map();
|
|
24
24
|
for (const [series, value] of values) {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
export declare const DEBUG_LOG_ARGUMENT = "DebugLog";
|
|
1
2
|
export type XPoeDebugItem = {
|
|
2
3
|
type: "frame";
|
|
3
4
|
trgId: number;
|
|
4
5
|
srcId: number;
|
|
5
|
-
|
|
6
|
+
har: number;
|
|
6
7
|
header: string;
|
|
7
8
|
argument: string;
|
|
9
|
+
reply: boolean;
|
|
8
10
|
value: string;
|
|
9
11
|
valueBytes: Uint8Array;
|
|
10
12
|
crc: string;
|
|
@@ -14,8 +16,19 @@ export type XPoeDebugItem = {
|
|
|
14
16
|
type: "raw";
|
|
15
17
|
text: string;
|
|
16
18
|
};
|
|
17
|
-
export declare const
|
|
18
|
-
export declare const encodeXPoeDebugFrame: (header: string, argument: string, value: Uint8Array, srcId?: number,
|
|
19
|
+
export declare const xPoeDebugHar: (header: string, argument: string, reply?: boolean) => number;
|
|
20
|
+
export declare const encodeXPoeDebugFrame: (header: string, argument: string, value: Uint8Array, srcId?: number, trgId?: number, reply?: boolean) => Uint8Array;
|
|
21
|
+
export interface XPoeDebugRow {
|
|
22
|
+
trgId: string;
|
|
23
|
+
srcId: string;
|
|
24
|
+
har: string;
|
|
25
|
+
header: string;
|
|
26
|
+
argument: string;
|
|
27
|
+
value: string;
|
|
28
|
+
crc: string;
|
|
29
|
+
freeHeap: string;
|
|
30
|
+
}
|
|
31
|
+
export declare const xPoeDebugRow: (item: XPoeDebugItem) => XPoeDebugRow;
|
|
19
32
|
export declare const createXPoeDebugParser: () => {
|
|
20
33
|
push(chunk: Uint8Array): XPoeDebugItem[];
|
|
21
34
|
reset(): void;
|
|
@@ -1,45 +1,30 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
1
|
+
/* $lib/protocols/xPoeDebug.ts */
|
|
2
|
+
/* xPOE-Debug — UART-консоль как ещё одна шина: пакеты poe_cbor_pkt [[TrgBusID, SrcBusID, HAR, Payload, CRC16], …] вперемешку с текстом загрузчика.
|
|
3
|
+
Логи — OK! DebugLog, Payload [уровень, тег, текст|байты, FHS]; графики — OK! DebugPlot, Payload {"Группа.Имя": float32} */
|
|
4
|
+
import { cborToValue } from "./cbor/value.js";
|
|
5
5
|
import { jsonToCbor } from "./cbor/json.js";
|
|
6
6
|
import { DEFAULT_ARGUMENT_CODES, HEADER_CODES, nameByCode } from "./dictionary.js";
|
|
7
|
-
import {
|
|
7
|
+
import { decodePacket, encodePacket, formatBusId, makeHar } from "./packet.js";
|
|
8
|
+
export const DEBUG_LOG_ARGUMENT = "DebugLog";
|
|
8
9
|
const MAX_TEXT_TAIL = 4096;
|
|
9
|
-
const
|
|
10
|
+
const MAX_PACKET = 8192;
|
|
10
11
|
const PACKET_START = 0x9f;
|
|
11
|
-
const
|
|
12
|
+
const RECORD = 0x85;
|
|
12
13
|
const decoder = new TextDecoder();
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return [Number(value)];
|
|
17
|
-
if (value <= 0xffn)
|
|
18
|
-
return [0x18, Number(value)];
|
|
19
|
-
if (value <= 0xffffn)
|
|
20
|
-
return [0x19, Number(value >> 8n), Number(value & 0xffn)];
|
|
21
|
-
if (value <= 0xffffffffn)
|
|
22
|
-
return [0x1a, Number(value >> 24n), Number((value >> 16n) & 0xffn), Number((value >> 8n) & 0xffn), Number(value & 0xffn)];
|
|
23
|
-
throw new Error("xPOE-Debug integer is out of range");
|
|
24
|
-
};
|
|
25
|
-
export const xPoeDebugCmd = (header, argument) => {
|
|
14
|
+
const toHex = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0").toUpperCase()).join("");
|
|
15
|
+
/* HAR по строкам словаря; неизвестные заголовок или аргумент — ошибка */
|
|
16
|
+
export const xPoeDebugHar = (header, argument, reply = true) => {
|
|
26
17
|
const hdr = HEADER_CODES[header];
|
|
27
18
|
const arg = DEFAULT_ARGUMENT_CODES[argument];
|
|
28
19
|
if (hdr === undefined || arg === undefined)
|
|
29
20
|
throw new Error(`Hdr/Arg отсутствует в общем словаре: ${header} ${argument}`);
|
|
30
|
-
return
|
|
21
|
+
return makeHar(hdr, arg, reply);
|
|
31
22
|
};
|
|
32
|
-
/* Команда
|
|
33
|
-
export const encodeXPoeDebugFrame = (header, argument, value, srcId = 0,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const valueParser = new CborDecoder(value);
|
|
38
|
-
if (!valueParser.skipValue() || valueParser.offset !== value.length)
|
|
39
|
-
throw new Error("Value должен содержать ровно один CBOR item");
|
|
40
|
-
const recordPrefix = new Uint8Array([RECORD6, ...encodeUInt(trgId), ...encodeUInt(srcId), ...encodeUInt(cmd), ...value, ...(freeHeap === null ? [0xf6] : encodeUInt(freeHeap))]);
|
|
41
|
-
const crc = crc16Kermit(0, recordPrefix);
|
|
42
|
-
return new Uint8Array([PACKET_START, ...recordPrefix, ...encodeUInt(crc), 0xff]);
|
|
23
|
+
/* Команда терминала — пакет из одной записи; value — CBOR item Payload, пустой — null */
|
|
24
|
+
export const encodeXPoeDebugFrame = (header, argument, value, srcId = 0, trgId = 0, reply = true) => {
|
|
25
|
+
if (![srcId, trgId].every((n) => Number.isInteger(n) && n >= 0 && n <= 0xffff))
|
|
26
|
+
throw new Error("TrgBusID и SrcBusID должны быть uint16");
|
|
27
|
+
return encodePacket([{ trgBusID: trgId, srcBusID: srcId, har: xPoeDebugHar(header, argument, reply), payload: value }]);
|
|
43
28
|
};
|
|
44
29
|
const textItems = (bytes) => decoder
|
|
45
30
|
.decode(bytes)
|
|
@@ -47,64 +32,61 @@ const textItems = (bytes) => decoder
|
|
|
47
32
|
.map((line) => line.trim())
|
|
48
33
|
.filter((line) => line && ![...line].some((char) => char.charCodeAt(0) < 0x20 && char !== "\t"))
|
|
49
34
|
.map((text) => ({ type: "raw", text }));
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return { status: "garbage" };
|
|
35
|
+
/* FHS — четвёртый элемент Payload DebugLog */
|
|
36
|
+
const freeHeapOf = (argument, payload) => {
|
|
37
|
+
if (argument !== DEBUG_LOG_ARGUMENT || !payload.length)
|
|
38
|
+
return "";
|
|
55
39
|
try {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
if (!Array.isArray(packet) || packet.length !== 1 || !Array.isArray(packet[0]) || packet[0].length !== 6)
|
|
59
|
-
return { status: "garbage" };
|
|
60
|
-
const length = cbor.offset;
|
|
61
|
-
const fields = new CborDecoder(buffer.subarray(2, length));
|
|
62
|
-
const trgId = readUInt(fields.decode());
|
|
63
|
-
const srcId = readUInt(fields.decode());
|
|
64
|
-
const cmd = readUInt(fields.decode());
|
|
65
|
-
if (trgId === null || srcId === null || cmd === null)
|
|
66
|
-
return { status: "garbage" };
|
|
67
|
-
const valueStart = 2 + fields.offset;
|
|
68
|
-
if (!fields.skipValue())
|
|
69
|
-
return { status: "garbage" };
|
|
70
|
-
const valueEnd = 2 + fields.offset;
|
|
71
|
-
const freeHeapValue = fields.decode();
|
|
72
|
-
const crcStart = 2 + fields.offset;
|
|
73
|
-
const crc = readUInt(fields.decode());
|
|
74
|
-
if (fields.offset !== length - 3 || crc === null || (freeHeapValue !== null && (typeof freeHeapValue !== "number" || !Number.isInteger(freeHeapValue) || freeHeapValue < 0)))
|
|
75
|
-
return { status: "garbage" };
|
|
76
|
-
const valueBytes = buffer.slice(valueStart, valueEnd);
|
|
77
|
-
const { header, argument } = splitCmd(cmd);
|
|
78
|
-
return {
|
|
79
|
-
status: "complete",
|
|
80
|
-
length,
|
|
81
|
-
item: {
|
|
82
|
-
type: "frame",
|
|
83
|
-
trgId,
|
|
84
|
-
srcId,
|
|
85
|
-
cmd,
|
|
86
|
-
header: Object.entries(HEADER_CODES).find(([, code]) => code === header)?.[0] ?? String(header),
|
|
87
|
-
argument: nameByCode(DEFAULT_ARGUMENT_CODES, argument),
|
|
88
|
-
value: Array.from(valueBytes, (byte) => byte.toString(16).padStart(2, "0").toUpperCase()).join(""),
|
|
89
|
-
valueBytes,
|
|
90
|
-
crc: crc.toString(16).toUpperCase().padStart(4, "0"),
|
|
91
|
-
crcOk: crc16Kermit(0, buffer.subarray(1, crcStart)) === crc,
|
|
92
|
-
freeHeap: typeof freeHeapValue === "number" ? String(freeHeapValue) : "",
|
|
93
|
-
},
|
|
94
|
-
};
|
|
40
|
+
const value = cborToValue(payload);
|
|
41
|
+
return Array.isArray(value) && typeof value[3] === "number" ? String(value[3]) : "";
|
|
95
42
|
}
|
|
96
|
-
catch
|
|
97
|
-
|
|
98
|
-
return { status: "incomplete" };
|
|
99
|
-
return { status: "garbage" };
|
|
43
|
+
catch {
|
|
44
|
+
return "";
|
|
100
45
|
}
|
|
101
46
|
};
|
|
47
|
+
const frameItem = (object) => {
|
|
48
|
+
const argument = nameByCode(DEFAULT_ARGUMENT_CODES, object.argument);
|
|
49
|
+
return {
|
|
50
|
+
type: "frame",
|
|
51
|
+
trgId: object.trgBusID,
|
|
52
|
+
srcId: object.srcBusID,
|
|
53
|
+
har: object.har,
|
|
54
|
+
header: nameByCode(HEADER_CODES, object.header),
|
|
55
|
+
argument,
|
|
56
|
+
reply: object.reply,
|
|
57
|
+
value: toHex(object.payload),
|
|
58
|
+
valueBytes: object.payload.slice(),
|
|
59
|
+
crc: object.crc.toString(16).toUpperCase().padStart(4, "0"),
|
|
60
|
+
crcOk: object.crcOk,
|
|
61
|
+
freeHeap: freeHeapOf(argument, object.payload),
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
export const xPoeDebugRow = (item) => item.type === "raw"
|
|
65
|
+
? { trgId: "", srcId: "", har: "", header: "RAW", argument: "", value: item.text, crc: "", freeHeap: "" }
|
|
66
|
+
: {
|
|
67
|
+
trgId: formatBusId(item.trgId),
|
|
68
|
+
srcId: formatBusId(item.srcId),
|
|
69
|
+
har: item.har.toString(16).toUpperCase().padStart(4, "0"),
|
|
70
|
+
header: item.crcOk ? item.header : "⚠️",
|
|
71
|
+
argument: item.argument,
|
|
72
|
+
value: item.value,
|
|
73
|
+
crc: item.crc,
|
|
74
|
+
freeHeap: item.freeHeap,
|
|
75
|
+
};
|
|
76
|
+
/* Начало пакета — 0x9F 0x85 (0x9F последним байтом буфера — возможное начало) */
|
|
77
|
+
const packetStart = (buffer) => {
|
|
78
|
+
for (let i = buffer.indexOf(PACKET_START); i >= 0; i = buffer.indexOf(PACKET_START, i + 1))
|
|
79
|
+
if (i + 1 >= buffer.length || buffer[i + 1] === RECORD)
|
|
80
|
+
return i;
|
|
81
|
+
return -1;
|
|
82
|
+
};
|
|
102
83
|
const concat = (a, b) => {
|
|
103
84
|
const out = new Uint8Array(a.length + b.length);
|
|
104
85
|
out.set(a);
|
|
105
86
|
out.set(b, a.length);
|
|
106
87
|
return out;
|
|
107
88
|
};
|
|
89
|
+
/* Потоковый разбор: пакеты в любом месте разреза, текст — построчно, мусор сдвигается по байту */
|
|
108
90
|
export const createXPoeDebugParser = () => {
|
|
109
91
|
let buffer = new Uint8Array();
|
|
110
92
|
return {
|
|
@@ -112,34 +94,30 @@ export const createXPoeDebugParser = () => {
|
|
|
112
94
|
buffer = concat(buffer, chunk);
|
|
113
95
|
const items = [];
|
|
114
96
|
while (buffer.length) {
|
|
115
|
-
const start = buffer
|
|
97
|
+
const start = packetStart(buffer);
|
|
116
98
|
if (start < 0) {
|
|
117
99
|
const cut = buffer.length > MAX_TEXT_TAIL ? buffer.length : buffer.lastIndexOf(0x0a);
|
|
118
100
|
if (cut >= 0) {
|
|
119
101
|
items.push(...textItems(buffer.subarray(0, cut)));
|
|
120
102
|
buffer = buffer.slice(Math.min(cut + 1, buffer.length));
|
|
121
103
|
}
|
|
122
|
-
else if (buffer.length > MAX_TEXT_TAIL)
|
|
123
|
-
buffer = new Uint8Array();
|
|
124
104
|
break;
|
|
125
105
|
}
|
|
106
|
+
if (start + 1 >= buffer.length)
|
|
107
|
+
break; /* 0x9F последним байтом — ждём следующий */
|
|
126
108
|
if (start > 0) {
|
|
127
109
|
items.push(...textItems(buffer.subarray(0, start)));
|
|
128
110
|
buffer = buffer.slice(start);
|
|
129
111
|
continue;
|
|
130
112
|
}
|
|
131
|
-
const parsed =
|
|
132
|
-
if (parsed.status === "incomplete")
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
buffer = buffer.slice(1);
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
if (parsed.status === "garbage") {
|
|
113
|
+
const parsed = decodePacket(buffer);
|
|
114
|
+
if (parsed.status === "incomplete" && buffer.length <= MAX_PACKET)
|
|
115
|
+
break;
|
|
116
|
+
if (parsed.status !== "complete" || !parsed.objects.length) {
|
|
139
117
|
buffer = buffer.slice(1);
|
|
140
118
|
continue;
|
|
141
119
|
}
|
|
142
|
-
items.push(parsed.
|
|
120
|
+
items.push(...parsed.objects.map(frameItem));
|
|
143
121
|
buffer = buffer.slice(parsed.length);
|
|
144
122
|
}
|
|
145
123
|
return items;
|
|
@@ -149,11 +127,11 @@ export const createXPoeDebugParser = () => {
|
|
|
149
127
|
},
|
|
150
128
|
};
|
|
151
129
|
};
|
|
152
|
-
/* Value
|
|
130
|
+
/* Поле Value: готовый HEX CBOR проходит как есть, JSON кодируется в CBOR, пусто — пустой Payload */
|
|
153
131
|
export const xPoeDebugValueFromText = (text) => {
|
|
154
132
|
const trimmed = (text ?? "").trim();
|
|
155
133
|
if (!trimmed)
|
|
156
|
-
return new Uint8Array(
|
|
134
|
+
return new Uint8Array();
|
|
157
135
|
if (/^([0-9A-Fa-f]{2}\s?)+$/.test(trimmed))
|
|
158
136
|
return Uint8Array.from(trimmed.replace(/\s/g, "").match(/../g) ?? [], (pair) => parseInt(pair, 16));
|
|
159
137
|
let parsed;
|
|
@@ -19,7 +19,7 @@ const compactCborValue = (value) => {
|
|
|
19
19
|
return `bytes(${value.length}): ${escapeHtml(bytesToHex(value))}`;
|
|
20
20
|
return escapeHtml(JSON.stringify(normalizeCborValue(value)) ?? String(value));
|
|
21
21
|
};
|
|
22
|
-
/*
|
|
22
|
+
/* Payload в таблицу: CBOR как JSON, лог DebugLog [уровень, тег, текст|байты, FHS] — уровень · тег · текст */
|
|
23
23
|
export function formatXPoeDebugValue(rawValue) {
|
|
24
24
|
const hex = rawValue.trim().replace(/\s/g, "");
|
|
25
25
|
if (!hex || hex.length % 2 !== 0 || !/^[0-9A-Fa-f]+$/.test(hex))
|
|
@@ -27,7 +27,7 @@ export function formatXPoeDebugValue(rawValue) {
|
|
|
27
27
|
try {
|
|
28
28
|
const value = cborToValue(Uint8Array.from(hex.match(/.{2}/g), (byte) => parseInt(byte, 16)));
|
|
29
29
|
if (Array.isArray(value) &&
|
|
30
|
-
value.length ===
|
|
30
|
+
value.length === 4 &&
|
|
31
31
|
typeof value[0] === "number" &&
|
|
32
32
|
LOG_LEVELS[value[0]] &&
|
|
33
33
|
typeof value[1] === "string" &&
|
|
@@ -42,7 +42,7 @@ export function formatXPoeDebugValue(rawValue) {
|
|
|
42
42
|
return escapeHtml(rawValue);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
/*
|
|
45
|
+
/* Исходный HEX скрыт до раскрытия строки в общей таблице */
|
|
46
46
|
export function formatXPoeDebugValueForTable(rawValue, isCbor = true) {
|
|
47
47
|
if (!isCbor)
|
|
48
48
|
return escapeHtml(rawValue);
|