poe-svelte-ui-lib 1.68.0 → 1.70.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
@@ -59,7 +59,9 @@ npm install poe-svelte-ui-lib
59
59
 
60
60
  **Названия смарт-виджетов** — в словаре библиотеки на всех языках (WiFi, «Об устройстве», «Файловая система», «Стек и задачи», «RGB лента», «Пилотажный дисплей», «Карта»); пустой `label.name` показывает название на языке пользователя, `widgetTitle(type)` отдаёт его сразу на всех языках `{ ru, en, zh }` — так конструктор заполняет заголовок нового виджета.
61
61
 
62
- **Ядро протоколов** — `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` `{ TrgBusID : [RetBusID, HA_Code, Payload, CRC16] }` (CAN, RS485): `encodePacket`/`decodePacket` (разбор с начала буфера — `complete` с длиной, `incomplete` для потока или `garbage`; объекты с одинаковым `TrgBusID` не склеиваются, неверный CRC — флаг `crcOk`), `objectsFor` (свой адрес и broadcast `0xFFFF`), `makeHaCode`/`splitHaCode`, `packetObjectCrc`; контрольные суммы `crc16Kermit` и `crc8Rohc` — строго побайтные, как в `common.c` прошивок. Байты совпадают с `poe_cbor_pkt.c` прошивки — проверка `npm run TestProtocols` (`tests/protocols.test.mjs`, эталонные пакеты собраны прошивочным кодом). Рамка POESerial (UART-консоль `system_console`, `[SOH] HDR [US] ARG [STX] VAL [ETX] CRC8 [US] FHS [EOT]`): `encodePoeSerialFrame` — команда устройству, `createPoeSerialParser().push(байты)` — потоковый разбор кадров, строк Teleplot (`plot`) и прочего текста (`raw`), с проверкой CRC `crc8Rohc` строго побайтно, как `crc8_rohc` в `common.c` прошивок. 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 команды POESerial из ввода — `poeSerialValueFromText` (hex как есть, JSON → CBOR). Словарь протокола — `HEADER_CODES`, `DEFAULT_ARGUMENT_CODES` (генерируется из мастер-словаря `src/lib/protocols/dictionary.yaml` — `npm run Dictionary`, перед упаковкой автоматически; сам файл поставляется как `poe-svelte-ui-lib/protocols/dictionary.yaml`, `.framework/Dictionary.yaml` прошивок — его справочные копии, сборка прошивок сверяет с ними `api_ha.h`), `parseArgumentDictionary` (строки `- [Строка, Enum, Число]`), `nameByCode`. Своих копий протоколов и словаря в приложениях не держим: терминал и отладка облака читают и пишут порт сырыми байтами, разбор и сборка — здесь.
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
+
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`, `[SOH] HDR [US] ARG [STX] VAL [ETX] CRC8 [US] FHS [EOT]`): `encodeXPoeDebugFrame` — команда устройству, `createXPoeDebugParser().push(байты)` — потоковый разбор кадров, строк Teleplot (`plot`) и прочего текста (`raw`), с проверкой CRC `crc8Rohc` строго побайтно, как `crc8_rohc` в `common.c` прошивок. 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). Словарь протокола — `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`. Своих копий протоколов и словаря в приложениях не держим: терминал и отладка облака читают и пишут порт сырыми байтами, разбор и сборка — здесь.
63
65
 
64
66
  **Шапка смарт-виджета** (`WidgetHeader`): значок, заголовок и сворачивание слева, «?» в правом краю; правая часть (режимы, «Обновить» и т. п.) стоит в той же строке, а если не помещается — уходит второй строкой под заголовок и там переносится.
65
67
 
@@ -389,11 +389,12 @@
389
389
  <h5 class={`w-full px-4 text-center ${label.class}`}>{label.name}</h5>
390
390
  {/if}
391
391
 
392
- <div class="flex w-full flex-row gap-4">
393
- <!-- График -->
392
+ <div class="flex w-full min-w-0 flex-row gap-4">
393
+ <!-- График: холст абсолютно поверх контейнера — его пиксельная ширина не распирает контейнер, иначе в сетке/flex с 1fr ширина растёт по кругу (ResizeObserver) -->
394
394
  <!-- design-handoff, раздел 9: рамка --hairline-color без тени, тултип в оформлении общего Tooltip.svelte -->
395
- <div bind:this={container} class="relative h-64 grow overflow-hidden rounded-[14px] border border-(--hairline-color)">
396
- <canvas class="h-full w-full bg-(--back-color)" bind:this={canvas} onmousemove={handleMouseMove} onmouseleave={handleMouseLeave}></canvas>
395
+ <div bind:this={container} class="relative h-64 min-w-0 grow overflow-hidden rounded-[14px] border border-(--hairline-color)">
396
+ <canvas class="absolute inset-0 h-full w-full bg-(--back-color)" bind:this={canvas} onmousemove={handleMouseMove} onmouseleave={handleMouseLeave}
397
+ ></canvas>
397
398
 
398
399
  {#if tooltip.visible}
399
400
  <div
@@ -23,6 +23,8 @@
23
23
 
24
24
  ### Адаптивность
25
25
 
26
+ - Холст лежит поверх контейнера (`absolute`), контейнер может сжиматься (`min-w-0`) — в сетке или flex с колонкой `1fr` график не распирает колонку своей пиксельной шириной и не растёт по кругу при каждом пересчёте размера
27
+
26
28
  - График автоматически масштабируется под размер контейнера
27
29
 
28
30
  ### Ограничения
@@ -150,6 +150,7 @@
150
150
  - Поддерживает выбор типа поля: текст, пароль, число, многострочный текст, битовая маска
151
151
  - Включает библиотеку готовых регулярных выражений для валидации (модальное окно с выбором шаблона)
152
152
  - Для типа `bitMode` позволяет задать диапазон битов слайдером; `number.maxNum` рассчитывается автоматически
153
+ - Значение поля в `bitMode` вызывающий код берёт и записывает через `bitFieldGet`/`bitFieldSet` (`poe-svelte-ui-lib`) — корректно для любых бит 0..31, включая старший.
153
154
  - Поддерживает валидацию вводимых данных через регулярные выражения
154
155
 
155
156
  ### Что настраивается
@@ -54,7 +54,7 @@
54
54
  | `options` | `IOption<T>[]` | `[]` | Массив доступных опций |
55
55
  | `onUpdate` | `(value: IOption<T> \| IOption<T>[]) => void` | `undefined` | Обработчик изменения значения |
56
56
 
57
- > **`bitMode`/`range`**: эти поля объявлены в типе `ISelectProps` и настраиваются в конструкторской панели (`SelectProps.svelte`), но сам `Select.svelte` их не читает и не использует — они не оказывают никакого эффекта на голый `<UI.Select>`. Если нужен битовый выбор в реальном рантайме, интерпретируйте маску на стороне вызывающего кода и передавайте уже готовый `value`/`options`.
57
+ > **`bitMode`/`range`**: эти поля объявлены в типе `ISelectProps` и настраиваются в конструкторской панели (`SelectProps.svelte`), но сам `Select.svelte` их не читает и не использует — они не оказывают никакого эффекта на голый `<UI.Select>`. Если нужен битовый выбор в реальном рантайме, интерпретируйте маску на стороне вызывающего кода и передавайте уже готовый `value`/`options`. Разбор и сборка поля `range.start..range.end` 32-битного слова — `bitFieldGet(word, start, end)`/`bitFieldSet(word, start, end, value)` из `poe-svelte-ui-lib` (обычная арифметика вместо знаковых 32-битных операций JS: бит 31 и поля любой ширины без отрицательных чисел, слово на входе может быть и отрицательным — то же 32-битное представление); так делают `GUIPreview` DevCloud и WebApp.
58
58
 
59
59
  ### Вложенные типы
60
60
 
@@ -28,6 +28,8 @@
28
28
 
29
29
  - Таблица автоматически адаптируется под размеры контейнера
30
30
  - Размер текста строк задаётся CSS-переменной `--table-font-size` на любом родителе (по умолчанию `13px`), например `style="--table-font-size: 11.5px"` — так потребитель меняет масштаб данных без правки компонента
31
+ - Ширина колонки в символах моноширинного текста — `tableMonoWidth(знаков, знаковПодписи?)`: растёт вместе с `--table-font-size` и не бывает уже подписи шапки, например `width: tableMonoWidth(6, 2)` для `0x0000`
32
+ - Окно полного значения (`modal`) переносит длинные строки (hex пакета, base64) и сохраняет отступы форматирования — содержимое видно целиком, без горизонтальной прокрутки
31
33
 
32
34
  ### Ограничения
33
35
 
@@ -528,7 +528,8 @@
528
528
 
529
529
  <Modal isOpen={modalData.isOpen} title={$T("constructor.props.table.fulldata")} wrapperClass="max-h-[80%]" width="{0.8 * (container?.offsetWidth ?? 0)}px">
530
530
  {#snippet main()}
531
- <div class="text-left whitespace-pre">
531
+ <!-- Длинные строки (hex пакета, base64, длинные значения) переносятся, отступы форматирования сохраняются — содержимое видно целиком -->
532
+ <div class="text-left whitespace-pre-wrap [overflow-wrap:anywhere]">
532
533
  {@html modalData.formattedData}
533
534
  </div>
534
535
  {/snippet}
@@ -0,0 +1 @@
1
+ export declare const tableMonoWidth: (chars: number, labelChars?: number) => string;
@@ -0,0 +1,4 @@
1
+ /* $lib/Table/columnWidth.ts */
2
+ /* Ширина колонки моноширинного текста таблицы в символах: растёт вместе с --table-font-size (знак моноширинного шрифта — 0.6–0.62em, берётся 0.64 с запасом),
3
+ не уже подписи шапки (11px, заглавные, ~0.5rem на знак); 1.5rem — внутренние отступы ячейки, 1px — разделитель колонок */
4
+ export const tableMonoWidth = (chars, labelChars = 0) => `max(calc(var(--table-font-size, 13px) * ${+(chars * 0.64).toFixed(2)} + 1.5rem + 1px), calc(${labelChars * 0.5}rem + 1.5rem + 1px))`;
@@ -0,0 +1,2 @@
1
+ export declare const bitFieldGet: (word: number, start: number, end: number) => number;
2
+ export declare const bitFieldSet: (word: number, start: number, end: number, value: number) => number;
@@ -0,0 +1,11 @@
1
+ /* $lib/bitField.ts */
2
+ /* Битовое поле [start..end] 32-битного слова конфигурации (Select/Input с bitMode): обычная арифметика вместо знаковых 32-битных операций JS —
3
+ бит 31 и поля любой ширины разбираются и собираются без отрицательных значений; слово на входе может прийти и отрицательным (то же 32-битное представление) */
4
+ const span = (start, end) => 2 ** (end - start + 1);
5
+ export const bitFieldGet = (word, start, end) => Math.floor((word >>> 0) / 2 ** start) % span(start, end);
6
+ export const bitFieldSet = (word, start, end, value) => {
7
+ const w = word >>> 0;
8
+ const size = span(start, end);
9
+ const field = ((Math.trunc(value) % size) + size) % size;
10
+ return w - bitFieldGet(w, start, end) * 2 ** start + field * 2 ** start;
11
+ };
package/dist/index.d.ts CHANGED
@@ -28,6 +28,8 @@ export { default as Switch } from "./Switch/Switch.svelte";
28
28
  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
+ export { tableMonoWidth } from "./Table/columnWidth";
32
+ export { bitFieldGet, bitFieldSet } from "./bitField";
31
33
  export { default as Tabs } from "./Tabs/Tabs.svelte";
32
34
  export { default as TabsProps } from "./Tabs/TabsProps.svelte";
33
35
  export { default as TextField } from "./TextField/TextField.svelte";
package/dist/index.js CHANGED
@@ -29,6 +29,8 @@ export { default as Switch } from "./Switch/Switch.svelte";
29
29
  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
+ export { tableMonoWidth } from "./Table/columnWidth";
33
+ export { bitFieldGet, bitFieldSet } from "./bitField";
32
34
  export { default as Tabs } from "./Tabs/Tabs.svelte";
33
35
  export { default as TabsProps } from "./Tabs/TabsProps.svelte";
34
36
  export { default as TextField } from "./TextField/TextField.svelte";
@@ -9,7 +9,7 @@ export const crc16Kermit = (crc, bytes) => {
9
9
  }
10
10
  return crc & 0xffff;
11
11
  };
12
- /* CRC-8/ROHC: poly 0x07 reflected (0xE0), init 0xFF, xorout 0x00, check("123456789") = 0xD0 — рамка POESerial (system_console): HEADER, ARGUMENT, VALUE */
12
+ /* CRC-8/ROHC: poly 0x07 reflected (0xE0), init 0xFF, xorout 0x00, check("123456789") = 0xD0 — рамка xPOE-Debug (system_console): HEADER, ARGUMENT, VALUE */
13
13
  export const crc8Rohc = (crc, bytes) => {
14
14
  for (const byte of bytes) {
15
15
  crc ^= byte & 0xff;
@@ -1,5 +1,5 @@
1
1
  /* $lib/protocols/dictionary.ts */
2
- /* Словарь заголовков и аргументов протокола HA_Code — заголовок в 2 старших битах, аргумент в 14 младших (makeHaCode). Аргументы по умолчанию —
2
+ /* Словарь заголовков и аргументов протокола — заголовок и аргумент поля Cmd пакета (makeCmd/splitCmd). Аргументы по умолчанию —
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
- # Словарь аргументов протокола HA_Code — единый для всех устройств, обоих фреймворков и приложений
1
+ # Словарь аргументов протокола (поле arg в Cmd, 0..8191) — единый для всех устройств, обоих фреймворков и приложений
2
2
  # Строковое представление аргумента | Название в перечислении | Числовой код
3
3
  # Источник — poe-svelte-ui-lib (src/lib/protocols/dictionary.yaml); .framework/Dictionary.yaml фреймворков — копии, не править
4
4
 
@@ -5,7 +5,7 @@ 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
7
  export { crc16Kermit, crc8Rohc } from "./crc.js";
8
- export { BROADCAST_BUS_ID, makeHaCode, splitHaCode, packetObjectCrc, encodePacket, decodePacket, objectsFor, createPacketStreamParser, type PacketObject, type DecodedPacketObject, type PacketParse, } from "./packet.js";
9
- export { encodePoeSerialFrame, createPoeSerialParser, poeSerialValueFromText, type PoeSerialItem } from "./poeSerial.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 { encodeXPoeDebugFrame, createXPoeDebugParser, xPoeDebugValueFromText, type XPoeDebugItem } from "./xPoeDebug.js";
10
10
  export { CAN_FINAL_BIT, CAN_PACKET_ID_MASK, canFragments, encodeSlcanFrame, encodeCanPacket, createCanParser, slcanOpenCommands, type CanFrame, type CanMessage, } from "./can.js";
11
11
  export { HEADER_CODES, DEFAULT_ARGUMENT_CODES, parseArgumentDictionary, nameByCode } from "./dictionary.js";
@@ -9,12 +9,12 @@ 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 : [RetBusID, HA_Code, Payload, CRC16] } (CAN, RS485) */
12
+ /* Контрольные суммы и пакет poe_cbor_pkt [[TrgBusID, SrcBusID, Cmd, Payload, CRC16], …] (CAN, RS485) */
13
13
  export { crc16Kermit, crc8Rohc } from "./crc.js";
14
- export { BROADCAST_BUS_ID, makeHaCode, splitHaCode, packetObjectCrc, encodePacket, decodePacket, objectsFor, createPacketStreamParser, } from "./packet.js";
15
- /* Рамка POESerial (UART-консоль system_console): сборка команды и потоковый разбор кадров, строк Teleplot и текста */
16
- export { encodePoeSerialFrame, createPoeSerialParser, poeSerialValueFromText } from "./poeSerial.js";
14
+ export { BROADCAST_BUS_ID, makeCmd, splitCmd, EXTERNAL_BUS_ID, packetObjectCrc, encodePacket, decodePacket, objectsFor, createPacketStreamParser, } from "./packet.js";
15
+ /* Рамка xPOE-Debug (UART-консоль system_console): сборка команды и потоковый разбор кадров, строк Teleplot и текста */
16
+ export { encodeXPoeDebugFrame, createXPoeDebugParser, xPoeDebugValueFromText } from "./xPoeDebug.js";
17
17
  /* CAN/CAN FD через slcan-адаптер: нарезка пакета на кадры как в can_service прошивки, кадры slcan, потоковый разбор со сборкой фрагментов */
18
18
  export { CAN_FINAL_BIT, CAN_PACKET_ID_MASK, canFragments, encodeSlcanFrame, encodeCanPacket, createCanParser, slcanOpenCommands, } from "./can.js";
19
- /* Словарь заголовков и аргументов HA_Code — по умолчанию из мастер-словаря dictionary.yaml библиотеки, разбор файла словаря */
19
+ /* Словарь заголовков и аргументов Cmd — по умолчанию из мастер-словаря dictionary.yaml библиотеки, разбор файла словаря */
20
20
  export { HEADER_CODES, DEFAULT_ARGUMENT_CODES, parseArgumentDictionary, nameByCode } from "./dictionary.js";
@@ -1,22 +1,25 @@
1
1
  export declare const BROADCAST_BUS_ID = 65535;
2
+ export declare const EXTERNAL_BUS_ID = 0;
2
3
  export interface PacketObject {
3
4
  trgBusID: number;
4
- retBusID: number;
5
- haCode: number;
5
+ srcBusID: number;
6
+ cmd: number;
6
7
  payload: Uint8Array;
7
8
  }
8
9
  export interface DecodedPacketObject extends PacketObject {
9
10
  header: number;
10
11
  argument: number;
12
+ reply: boolean;
11
13
  crc: number;
12
14
  crcOk: boolean;
13
15
  }
14
- export declare const makeHaCode: (header: number, argument: number) => number;
15
- export declare const splitHaCode: (haCode: number) => {
16
+ export declare const makeCmd: (header: number, argument: number, reply?: boolean) => number;
17
+ export declare const splitCmd: (cmd: number) => {
16
18
  header: number;
17
19
  argument: number;
20
+ reply: boolean;
18
21
  };
19
- export declare const packetObjectCrc: (trgBusID: number, retBusID: number, haCode: number, payload: Uint8Array) => number;
22
+ export declare const packetObjectCrc: (trgBusID: number, srcBusID: number, cmd: number, payload: Uint8Array) => number;
20
23
  export declare const encodePacket: (objects: PacketObject[]) => Uint8Array;
21
24
  export type PacketParse = {
22
25
  status: "complete";
@@ -1,32 +1,38 @@
1
1
  /* $lib/protocols/packet.ts */
2
- /* Пакет poe_cbor_pkt (README poe_cbor прошивки, ProdFactory-ESP/STM): indefinite MAP { TrgBusID : [RetBusID, HA_Code, Payload, CRC16] },
3
- объектов в пакете сколько угодно (в том числе с одинаковым TrgBusID), у каждого свой CRC-16/KERMIT по TrgBusID|RetBusID|HA_Code (по 2 байта
4
- big-endian) и Payload — не зависит от того, как числа записаны в CBOR. Общая точка сборки и разбора для терминала (CAN, RS485) и облака */
2
+ /* Пакет poe_cbor_pkt (README poe_cbor прошивки, ProdFactory-ESP/STM): indefinite ARRAY объектов [[TrgBusID, SrcBusID, Cmd, Payload, CRC16], …],
3
+ объектов в пакете сколько угодно (в том числе одному адресату), порядок сохраняется; у каждого свой CRC-16/KERMIT по TrgBusID|SrcBusID|Cmd
4
+ (по 2 байта big-endian) и Payload — не зависит от того, как числа записаны в CBOR. Общая точка сборки и разбора для терминала (CAN, RS485) и облака */
5
5
  import { CborDecoder } from "./cbor/decoder.js";
6
6
  import { CborEncoder } from "./cbor/encoder.js";
7
7
  import { CborEndOfDataError } from "./cbor/types.js";
8
8
  import { crc16Kermit } from "./crc.js";
9
- /* Адрес «всем устройствам шины» */
9
+ /* Адрес «всем устройствам шины» — только адресат */
10
10
  export const BROADCAST_BUS_ID = 0xffff;
11
- /* HA_Code = заголовок (0..3) в старших 2 битах и аргумент (0..0x3FFF) */
12
- export const makeHaCode = (header, argument) => ((header & 0x03) << 14) | (argument & 0x3fff);
13
- export const splitHaCode = (haCode) => ({ header: (haCode >> 14) & 0x03, argument: haCode & 0x3fff });
14
- /* CRC объекта: TrgBusID | RetBusID | HA_Code по 2 байта big-endian, затем Payload */
15
- export const packetObjectCrc = (trgBusID, retBusID, haCode, payload) => {
16
- const head = [trgBusID >> 8, trgBusID, retBusID >> 8, retBusID, haCode >> 8, haCode].map((byte) => byte & 0xff);
11
+ /* Адрес внешнего клиента (терминал, отладка облака) — у модулей такого адреса не бывает */
12
+ export const EXTERNAL_BUS_ID = 0x0000;
13
+ /* Cmd = заголовок (0..3) в битах 14-15, аргумент (0..8191) в битах 1-13, флаг ответа в бите 0 — как CMD/CMD_NR в api_cmd.h прошивки */
14
+ export const makeCmd = (header, argument, reply = true) => ((header & 0x03) << 14) | ((argument & 0x1fff) << 1) | (reply ? 1 : 0);
15
+ export const splitCmd = (cmd) => ({
16
+ header: (cmd >> 14) & 0x03,
17
+ argument: (cmd >> 1) & 0x1fff,
18
+ reply: (cmd & 1) === 1,
19
+ });
20
+ /* CRC объекта: TrgBusID | SrcBusID | Cmd по 2 байта big-endian, затем Payload */
21
+ export const packetObjectCrc = (trgBusID, srcBusID, cmd, payload) => {
22
+ const head = [trgBusID >> 8, trgBusID, srcBusID >> 8, srcBusID, cmd >> 8, cmd].map((byte) => byte & 0xff);
17
23
  return crc16Kermit(crc16Kermit(0, head), payload);
18
24
  };
19
25
  /* Пакет из одного или нескольких объектов — те же байты, что CBOR_PACK/CBOR_PktEncFinish прошивки */
20
26
  export const encodePacket = (objects) => {
21
27
  const encoder = new CborEncoder();
22
- encoder.encodeMapStart(null);
23
- for (const { trgBusID, retBusID, haCode, payload } of objects) {
28
+ encoder.encodeArrayStart(null);
29
+ for (const { trgBusID, srcBusID, cmd, payload } of objects) {
30
+ encoder.encodeArrayStart(5);
24
31
  encoder.encodeUnsigned(trgBusID);
25
- encoder.encodeArrayStart(4);
26
- encoder.encodeUnsigned(retBusID);
27
- encoder.encodeUnsigned(haCode);
32
+ encoder.encodeUnsigned(srcBusID);
33
+ encoder.encodeUnsigned(cmd);
28
34
  encoder.encodeBytes(payload);
29
- encoder.encodeUnsigned(packetObjectCrc(trgBusID, retBusID, haCode, payload));
35
+ encoder.encodeUnsigned(packetObjectCrc(trgBusID, srcBusID, cmd, payload));
30
36
  }
31
37
  encoder.encodeBreak();
32
38
  return encoder.getResult();
@@ -45,24 +51,23 @@ const readValue = (bytes, offset) => {
45
51
  }
46
52
  };
47
53
  const toNumber = (value) => (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 0xffff ? value : null);
48
- /* Объект пакета из пары ключ/значение; не по формату (не 4 элемента, не те типы, старый формат без CRC) — null, как CBOR_PktDecGet его пропускает */
49
- const toObject = (key, value) => {
50
- const trgBusID = toNumber(key);
51
- if (trgBusID === null || !Array.isArray(value) || value.length !== 4)
54
+ /* Объект пакета; не по формату (не 5 элементов, не те типы) — null, как CBOR_PktDecNext прошивки его пропускает */
55
+ const toObject = (value) => {
56
+ if (!Array.isArray(value) || value.length !== 5)
52
57
  return null;
53
- const [retBusID, haCode, payload, crc] = [toNumber(value[0]), toNumber(value[1]), value[2], toNumber(value[3])];
54
- if (retBusID === null || haCode === null || !(payload instanceof Uint8Array) || crc === null)
58
+ const [trgBusID, srcBusID, cmd, payload, crc] = [toNumber(value[0]), toNumber(value[1]), toNumber(value[2]), value[3], toNumber(value[4])];
59
+ if (trgBusID === null || srcBusID === null || cmd === null || !(payload instanceof Uint8Array) || crc === null)
55
60
  return null;
56
- return { trgBusID, retBusID, haCode, payload, crc, crcOk: packetObjectCrc(trgBusID, retBusID, haCode, payload) === crc, ...splitHaCode(haCode) };
61
+ return { trgBusID, srcBusID, cmd, payload, crc, crcOk: packetObjectCrc(trgBusID, srcBusID, cmd, payload) === crc, ...splitCmd(cmd) };
57
62
  };
58
63
  /* Разбор пакета с начала буфера — и для целого кадра (CAN), и для потока (RS485: «incomplete» — ждать ещё байт, «garbage» — сдвинуться на байт) */
59
64
  export const decodePacket = (bytes) => {
60
65
  if (bytes.length === 0)
61
66
  return { status: "incomplete" };
62
67
  const first = bytes[0];
63
- if (first >> 5 !== 5)
68
+ if (first >> 5 !== 4)
64
69
  return { status: "garbage" };
65
- /* Число записей: indefinite (0xBF, до BREAK) или definite — прямо в байте либо в следующих 1/2 байтах */
70
+ /* Число объектов: indefinite (0x9F, до BREAK) или definite — прямо в байте либо в следующих 1/2 байтах */
66
71
  let offset = 1;
67
72
  let count = null;
68
73
  const info = first & 0x1f;
@@ -84,14 +89,11 @@ export const decodePacket = (bytes) => {
84
89
  return { status: "incomplete" };
85
90
  if (count === null && bytes[offset] === 0xff)
86
91
  return { status: "complete", length: offset + 1, objects };
87
- const key = readValue(bytes, offset);
88
- if (!key)
89
- return { status: "incomplete" };
90
- const value = readValue(bytes, offset + key.length);
92
+ const value = readValue(bytes, offset);
91
93
  if (!value)
92
94
  return { status: "incomplete" };
93
- offset += key.length + value.length;
94
- const object = toObject(key.value, value.value);
95
+ offset += value.length;
96
+ const object = toObject(value.value);
95
97
  if (object)
96
98
  objects.push(object);
97
99
  }
@@ -101,7 +103,7 @@ export const decodePacket = (bytes) => {
101
103
  }
102
104
  return { status: "complete", length: offset, objects };
103
105
  };
104
- /* Объекты пакета для своего адреса (и broadcast) с верным CRC — как CBOR_PktDecGet прошивки, только все, а не первый */
106
+ /* Объекты пакета для своего адреса (и broadcast) с верным CRC, по порядку — как CBOR_PktDecNext прошивки */
105
107
  export const objectsFor = (objects, busID) => objects.filter((object) => object.crcOk && (object.trgBusID === busID || object.trgBusID === BROADCAST_BUS_ID));
106
108
  /* Потоковый разбор пакетов из сырого потока (RS485): пауза дольше gapMs обрывает недособранный пакет, мусор сдвигается по байту,
107
109
  без единого целого пакета буфер не растёт дальше maxBytes */
@@ -3,7 +3,7 @@ export declare const STX = 2;
3
3
  export declare const ETX = 3;
4
4
  export declare const EOT = 4;
5
5
  export declare const US = 31;
6
- export type PoeSerialItem = {
6
+ export type XPoeDebugItem = {
7
7
  type: "frame";
8
8
  header: string;
9
9
  argument: string;
@@ -20,9 +20,9 @@ export type PoeSerialItem = {
20
20
  type: "raw";
21
21
  text: string;
22
22
  };
23
- export declare const encodePoeSerialFrame: (header: string, argument: string, value: Uint8Array) => Uint8Array;
24
- export declare const createPoeSerialParser: () => {
25
- push(chunk: Uint8Array): PoeSerialItem[];
23
+ export declare const encodeXPoeDebugFrame: (header: string, argument: string, value: Uint8Array) => Uint8Array;
24
+ export declare const createXPoeDebugParser: () => {
25
+ push(chunk: Uint8Array): XPoeDebugItem[];
26
26
  reset(): void;
27
27
  };
28
- export declare const poeSerialValueFromText: (text: string | undefined) => Uint8Array;
28
+ export declare const xPoeDebugValueFromText: (text: string | undefined) => Uint8Array;
@@ -1,5 +1,5 @@
1
- /* $lib/protocols/poeSerial.ts */
2
- /* Рамка POESerial (system_console прошивки): [SOH] HEADER [US] ARGUMENT [STX] VALUE [ETX] CRC8 [US] FHS [EOT] \n — CRC8 двумя hex-символами
1
+ /* $lib/protocols/xPoeDebug.ts */
2
+ /* Рамка xPOE-Debug (system_console прошивки): [SOH] HEADER [US] ARGUMENT [STX] VALUE [ETX] CRC8 [US] FHS [EOT] \n — CRC8 двумя hex-символами
3
3
  по HEADER+ARGUMENT+VALUE (crc8Rohc), FHS (свободная куча) только у кадров устройства. Устройство шлёт VALUE текстом (лог или hex-дамп CBOR),
4
4
  устройству VALUE уходит сырыми байтами CBOR. Между кадрами — текст: строки Teleplot «>Имя:Значение» (PLOT) и прочий вывод (RAW) */
5
5
  import { crc8Rohc } from "./crc.js";
@@ -17,7 +17,7 @@ const encoder = new TextEncoder();
17
17
  const decoder = new TextDecoder();
18
18
  const hex2 = (value) => value.toString(16).toUpperCase().padStart(2, "0");
19
19
  /* Кадр команды устройству — те же байты, что разбирает system_console (VALUE — сырой CBOR, FHS не передаётся) */
20
- export const encodePoeSerialFrame = (header, argument, value) => {
20
+ export const encodeXPoeDebugFrame = (header, argument, value) => {
21
21
  const headerBytes = encoder.encode(header);
22
22
  const argumentBytes = encoder.encode(argument);
23
23
  const crc = crc8Rohc(crc8Rohc(crc8Rohc(0xff, headerBytes), argumentBytes), value);
@@ -65,7 +65,7 @@ const concat = (a, b) => {
65
65
  return out;
66
66
  };
67
67
  /* Потоковый разбор: push(байты из порта) → готовые кадры и строки; незавершённое ждёт следующего чанка */
68
- export const createPoeSerialParser = () => {
68
+ export const createXPoeDebugParser = () => {
69
69
  let buffer = new Uint8Array();
70
70
  return {
71
71
  push(chunk) {
@@ -113,7 +113,7 @@ export const createPoeSerialParser = () => {
113
113
  };
114
114
  /* Поле Value команды из ввода пользователя: пусто — без тела, hex-строка (с пробелами или без) — готовые байты как есть, иначе JSON → CBOR
115
115
  (двоичные данные — base64, ключи с BIN — байты, см. jsonToCbor); не hex и не JSON — ошибка */
116
- export const poeSerialValueFromText = (text) => {
116
+ export const xPoeDebugValueFromText = (text) => {
117
117
  const trimmed = (text ?? "").trim();
118
118
  if (!trimmed)
119
119
  return new Uint8Array();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-svelte-ui-lib",
3
- "version": "1.68.0",
3
+ "version": "1.70.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Oleg-Perevyshin/ElementsUI.git"