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 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, Cmd, Payload, CRC16], …]` (CAN, RS485): `encodePacket`/`decodePacket` (разбор с начала буфера — `complete` с длиной, `incomplete` для потока или `garbage`; объекты одному адресату не склеиваются, порядок сохраняется, неверный CRC — флаг `crcOk`), `objectsFor` (свой адрес и broadcast `BROADCAST_BUS_ID = 0xFFFF`), `EXTERNAL_BUS_ID = 0x0000` — `SrcBusID` терминала и отладки, `makeCmd(header, argument, reply = true)`/`splitCmd` — `Cmd`: заголовок в битах 14-15, аргумент в битах 1-13 (0..8191), флаг ответа в бите 0 (как `CMD`/`CMD_NR` прошивки), `packetObjectCrc`; контрольные суммы `crc16Kermit` и `crc8Rohc` — строго побайтные, как в `common.c` прошивок. Байты совпадают с `poe_cbor_pkt.c` прошивки — проверка `npm run TestProtocols` (`tests/protocols.test.mjs`, эталонные пакеты собраны прошивочным кодом). Протокол xPOE-Debug (UART-консоль `system_console`): definite CBOR-массив `[SrcID, HdrArg, Value, FHS, CRC16]`; `HdrArg` — общее 16-битное `cmd_t.word`, `Value` — один CBOR item, FHS — свободная куча (или `null` в запросе), CRC-16/KERMIT покрывает сырые CBOR-байты первых четырёх элементов. `encodeXPoeDebugFrame` кодирует запрос, `createXPoeDebugParser().push(байты)` разбирает поток кадров и внешний текст (`raw`). 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). Графики отладки — кадр `OK! DebPlot` (`SC_PLOT` прошивки, `DEB_PLOT_ARGUMENT`): `debPlotValues(value)` — пары `[имя, число]` из hex CBOR-карты `VALUE`, `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_cmd.h`), `parseArgumentDictionary` (строки `- [Строка, Enum, Число]`), `nameByCode`. Своих копий протоколов и словаря в приложениях не держим: терминал и отладка облака читают и пишут порт сырыми байтами, разбор и сборка — здесь.
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,3 @@
1
+ import type { ITableHeader } from "../types";
2
+ import { type XPoeDebugRow } from "../protocols/index.js";
3
+ export declare const xPoeDebugTableColumns: ITableHeader<XPoeDebugRow>[];
@@ -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";
@@ -1,2 +1 @@
1
1
  export declare const crc16Kermit: (crc: number, bytes: Uint8Array | number[]) => number;
2
- export declare const crc8Rohc: (crc: number, bytes: Uint8Array | number[]) => number;
@@ -1,5 +1,5 @@
1
1
  /* $lib/protocols/crc.ts */
2
- /* Контрольные суммы протоколов устройств — строго побайтные, как crc8_rohc/crc16_kermit в common.c прошивок (ProdFactory-ESP/STM) */
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
- /* Словарь заголовков и аргументов протокола — заголовок и аргумент поля Cmd пакета (makeCmd/splitCmd). Аргументы по умолчанию —
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 в Cmd, 0..8191) — единый для всех устройств, обоих фреймворков и приложений
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!: системное сообщение отладки; VALUE — CBOR-массив [уровень, тег, текст]
19
- - [DebPlot, A_DEBUG_PLOT, 12] # OK!: значения графиков отладки — CBOR-карта {"Имя": float32} (SC_PLOT)
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
  # Локальная сеть
@@ -11,7 +11,7 @@ export const DEFAULT_ARGUMENT_CODES = {
11
11
  BackUp: 9,
12
12
  BindMKey: 10,
13
13
  DebugLog: 11,
14
- DebPlot: 12,
14
+ DebugPlot: 12,
15
15
  DataStream: 15,
16
16
  APsList: 21,
17
17
  FS: 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, crc8Rohc } from "./crc.js";
8
- export { BROADCAST_BUS_ID, makeCmd, splitCmd, EXTERNAL_BUS_ID, packetObjectCrc, encodePacket, decodePacket, objectsFor, createPacketStreamParser, type PacketObject, type DecodedPacketObject, type PacketParse, } from "./packet.js";
9
- export { createPlotGroups, debPlotValues, DEB_PLOT_ARGUMENT, type PlotPoint } from "./plotGroups.js";
10
- export { encodeXPoeDebugFrame, createXPoeDebugParser, xPoeDebugCmd, xPoeDebugValueFromText, type XPoeDebugItem } from "./xPoeDebug.js";
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";
@@ -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
- /* Контрольные суммы и пакет poe_cbor_pkt [[TrgBusID, SrcBusID, Cmd, Payload, CRC16], …] (CAN, RS485) */
13
- export { crc16Kermit, crc8Rohc } from "./crc.js";
14
- export { BROADCAST_BUS_ID, makeCmd, splitCmd, EXTERNAL_BUS_ID, packetObjectCrc, encodePacket, decodePacket, objectsFor, createPacketStreamParser, } from "./packet.js";
15
- /* Графики отладки: кадр OK! DebPlot (SC_PLOT прошивки) — значения из CBOR-карты, график на группу, серии в порядке устройства */
16
- export { createPlotGroups, debPlotValues, DEB_PLOT_ARGUMENT } from "./plotGroups.js";
17
- /* System Console over UART: single-record poe_cbor_pkt with debug-only FHS */
18
- export { encodeXPoeDebugFrame, createXPoeDebugParser, xPoeDebugCmd, xPoeDebugValueFromText } from "./xPoeDebug.js";
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
- /* Словарь заголовков и аргументов Cmd — по умолчанию из мастер-словаря dictionary.yaml библиотеки, разбор файла словаря */
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
- cmd: number;
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 makeCmd: (header: number, argument: number, reply?: boolean) => number;
17
- export declare const splitCmd: (cmd: number) => {
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;
@@ -1,6 +1,6 @@
1
1
  /* $lib/protocols/packet.ts */
2
- /* Общий пакет poe_cbor_pkt: indefinite ARRAY записей [TrgBusID, SrcBusID, Cmd, Payload, CRC16].
3
- Payload — ровно один CBOR item. CRC-16/KERMIT считается по сырым CBOR-байтам записи от её заголовка до конца 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
- /* Cmd = заголовок (0..3) в битах 14-15, аргумент (0..8191) в битах 1-13, флаг ответа в бите 0 — как CMD/CMD_NR в api_cmd.h прошивки */
13
- export const makeCmd = (header, argument, reply = true) => ((header & 0x03) << 14) | ((argument & 0x1fff) << 1) | (reply ? 1 : 0);
14
- export const splitCmd = (cmd) => ({
15
- header: (cmd >> 14) & 0x03,
16
- argument: (cmd >> 1) & 0x1fff,
17
- reply: (cmd & 1) === 1,
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
- /* Кодирует сырые байты как один CBOR byte string для Payload. */
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
- /* Пакет из одного или нескольких объектов — те же байты, что CBOR_PACK/CBOR_PktEncFinish прошивки */
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, cmd, payload } of objects) {
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(cmd);
36
- const prefixStart = encoder.getResult().length;
37
- /* Payload is already CBOR-encoded; reject malformed or concatenated values. */
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, cmd, crc] = [toNumber(value[0]), toNumber(value[1]), toNumber(value[2]), toNumber(value[4])];
67
- if (trgBusID === null || srcBusID === null || cmd === null || crc === 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 ((rawRecord[0] >> 5) !== 4 || headerLength === 0)
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 payloadBytes = rawRecord.subarray(payloadStart, payloadEnd);
83
- return { trgBusID, srcBusID, cmd, payload: payloadBytes, crc, crcOk: packetObjectCrc(prefix) === crc, ...splitCmd(cmd) };
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 DEB_PLOT_ARGUMENT = "DebPlot";
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 debPlotValues: (value: string) => [string, number][];
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
- /* Графики отладки: кадр xPOE-Debug OK! DebPlot (SC_PLOT прошивки), VALUE — hex CBOR-карты {"Группа.Имя": float32, …} в порядке вызова.
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 DEB_PLOT_ARGUMENT = "DebPlot";
7
- /* Значения графиков из VALUE кадра DebPlot (hex CBOR-карты); не карта — пустой список */
8
- export const debPlotValues = (value) => {
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
- cmd: number;
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 xPoeDebugCmd: (header: string, argument: string) => number;
18
- export declare const encodeXPoeDebugFrame: (header: string, argument: string, value: Uint8Array, srcId?: number, freeHeap?: number | null, trgId?: number) => Uint8Array;
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
- /* Debug is a one-record poe_cbor_pkt. Its record adds FHS to the common bus fields. */
2
- import { CborDecoder } from "./cbor/decoder.js";
3
- import { CborEndOfDataError } from "./cbor/types.js";
4
- import { crc16Kermit } from "./crc.js";
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 { makeCmd, splitCmd } from "./packet.js";
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 MAX_FRAME = 8192;
10
+ const MAX_PACKET = 8192;
10
11
  const PACKET_START = 0x9f;
11
- const RECORD6 = 0x86;
12
+ const RECORD = 0x85;
12
13
  const decoder = new TextDecoder();
13
- const encodeUInt = (input) => {
14
- const value = BigInt(input);
15
- if (value < 24n)
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 makeCmd(hdr, arg);
21
+ return makeHar(hdr, arg, reply);
31
22
  };
32
- /* Команда терминала: CRC считается по тем CBOR-байтам, которые реально отправляются. */
33
- export const encodeXPoeDebugFrame = (header, argument, value, srcId = 0, freeHeap = null, trgId = 0) => {
34
- const cmd = xPoeDebugCmd(header, argument);
35
- if (![srcId, trgId, cmd].every((n) => Number.isInteger(n) && n >= 0 && n <= 0xffff))
36
- throw new Error("TrgBusID, SrcBusID и Cmd должны быть uint16");
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
- const readUInt = (value) => (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 0xffff ? value : null);
51
- /* Декодирует пакет из одной записи с FHS; null означает неполный или невалидный кадр. */
52
- const frameItem = (buffer) => {
53
- if (buffer[0] !== PACKET_START || buffer[1] !== RECORD6)
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 cbor = new CborDecoder(buffer);
57
- const packet = cbor.decode(); /* Проверка структуры и границы всего пакета */
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 (error) {
97
- if (error instanceof CborEndOfDataError)
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.indexOf(PACKET_START);
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 = frameItem(buffer);
132
- if (parsed.status === "incomplete") {
133
- if (buffer.length <= MAX_FRAME)
134
- break;
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.item);
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 form input: ready CBOR hex passes through; JSON is converted to CBOR. */
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([0xf6]);
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
- /* Декодирует CBOR Value для таблицы; системный лог имеет вид [уровень, тег, текст]. */
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 === 3 &&
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
- /* Сохраняет исходный HEX скрытым до раскрытия строки в общей таблице. */
45
+ /* Исходный HEX скрыт до раскрытия строки в общей таблице */
46
46
  export function formatXPoeDebugValueForTable(rawValue, isCbor = true) {
47
47
  if (!isCbor)
48
48
  return escapeHtml(rawValue);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-svelte-ui-lib",
3
- "version": "1.74.2",
3
+ "version": "1.75.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Oleg-Perevyshin/ElementsUI.git"