poe-svelte-ui-lib 1.66.0 → 1.68.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/README.md +8 -7
- package/dist/Table/Table.svelte +2 -0
- package/dist/WidgetFileSystem/README.md +20 -18
- package/dist/WidgetFileSystem/WidgetFileSystem.svelte +50 -16
- package/dist/libIcons/ButtonDownload.svelte +12 -0
- package/dist/libIcons/ButtonDownload.svelte.d.ts +18 -0
- package/dist/locales/translations.js +6 -3
- package/dist/protocols/crc.d.ts +0 -1
- package/dist/protocols/crc.js +2 -26
- package/dist/protocols/dictionary.d.ts +1 -1
- package/dist/protocols/dictionary.js +4 -56
- package/dist/protocols/dictionary.yaml +66 -0
- package/dist/protocols/dictionaryCodes.d.ts +1 -0
- package/dist/protocols/dictionaryCodes.js +51 -0
- package/dist/protocols/index.d.ts +1 -1
- package/dist/protocols/index.js +2 -2
- package/dist/protocols/poeSerial.js +4 -4
- package/dist/types.d.ts +2 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ 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
|
|
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`. Своих копий протоколов и словаря в приложениях не держим: терминал и отладка облака читают и пишут порт сырыми байтами, разбор и сборка — здесь.
|
|
63
63
|
|
|
64
64
|
**Шапка смарт-виджета** (`WidgetHeader`): значок, заголовок и сворачивание слева, «?» в правом краю; правая часть (режимы, «Обновить» и т. п.) стоит в той же строке, а если не помещается — уходит второй строкой под заголовок и там переносится.
|
|
65
65
|
|
package/dist/Table/README.md
CHANGED
|
@@ -110,13 +110,14 @@
|
|
|
110
110
|
|
|
111
111
|
#### ITableButton\<T\>
|
|
112
112
|
|
|
113
|
-
| Название | Тип | По умолчанию | Описание
|
|
114
|
-
| -------------- | ---------------------------------------------- | ------------ |
|
|
115
|
-
| `name` | `string \| ((row: T) => string)` | `undefined` | Текст кнопки (статичный или вычисляемый по строке)
|
|
116
|
-
| `icon` | `ConstructorOfATypedSvelteComponent \| string` | `undefined` | Иконка кнопки (Svelte-компонент или строка SVG)
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
113
|
+
| Название | Тип | По умолчанию | Описание |
|
|
114
|
+
| -------------- | ---------------------------------------------- | ------------ | --------------------------------------------------------------------------------- |
|
|
115
|
+
| `name` | `string \| ((row: T) => string)` | `undefined` | Текст кнопки (статичный или вычисляемый по строке) |
|
|
116
|
+
| `icon` | `ConstructorOfATypedSvelteComponent \| string` | `undefined` | Иконка кнопки (Svelte-компонент или строка SVG) |
|
|
117
|
+
| `title` | `string \| ((row: T) => string)` | `undefined` | Подсказка при наведении и `aria-label` — обязательна для кнопки-значка без `name` |
|
|
118
|
+
| `class` | `string \| ((row: T) => string)` | `undefined` | CSS-классы кнопки (статичные или вычисляемые по строке) |
|
|
119
|
+
| `eventHandler` | `IUIComponentHandler` | `undefined` | Обработчик события конструктора |
|
|
120
|
+
| `onClick` | `(row: T) => void` | `undefined` | Обработчик клика по кнопке |
|
|
120
121
|
|
|
121
122
|
#### ITableProgressBar\<T\>
|
|
122
123
|
|
package/dist/Table/Table.svelte
CHANGED
|
@@ -340,6 +340,8 @@
|
|
|
340
340
|
}
|
|
341
341
|
${typeof button.class === 'function' ? button.class(row) : button.class}`)} bg-(--bg-color)"
|
|
342
342
|
onclick={() => buttonClick(row, button)}
|
|
343
|
+
title={typeof button.title === "function" ? button.title(row) : button.title}
|
|
344
|
+
aria-label={typeof button.title === "function" ? button.title(row) : button.title}
|
|
343
345
|
>
|
|
344
346
|
{#if button?.icon}
|
|
345
347
|
<span class={`flex size-4 shrink-0 items-center justify-center overflow-visible [&_svg]:h-full [&_svg]:w-full`}>
|
|
@@ -49,30 +49,31 @@
|
|
|
49
49
|
|
|
50
50
|
## Особенности
|
|
51
51
|
|
|
52
|
-
Смарт-виджет файловой системы устройства: занятость памяти, список файлов
|
|
52
|
+
Смарт-виджет файловой системы устройства: занятость памяти, список файлов со скачиванием и удалением и закачка нового файла с прогрессом. Работает с одним объектом `CFG.FS` ровно в том виде, в каком его отдаёт прошивка (`FS_WriteState`, ProdFactory-ESP), и с одной командой `FS`. Компонент ничего не знает про DeviceStore/WebSocket — только `value` и колбэки `onRefresh`/`onDelete`/`onUpload`/`onDownload`, вся отправка (в том числе протоколы закачки и скачивания) на стороне вызывающего кода.
|
|
53
53
|
|
|
54
54
|
- **Занятость** — «занято X из Y», процент и полоса мастер-цвета; процент оранжевый от 75%, красный от 90%. Размеры в Б/КБ/МБ.
|
|
55
|
-
- **Файлы** — таблица «Путь / Размер /
|
|
55
|
+
- **Файлы** — таблица «Путь / Размер / действия»; длинный путь обрезается «…». Действия — значки без текста с подсказкой при наведении: «Скачать» (есть, только если передан `onDownload`) и «Удалить» (только с `onDelete`, подтверждается внутри карточки). Одновременно идёт одно скачивание — его имя и ошибка из `onDownload` выводятся под таблицей. Пустая ФС — «Файлов нет».
|
|
56
56
|
- **Закачка** — блок есть, только если передан `onUpload`. Имя файла проверяется до отправки (`maxNameLength`, по умолчанию 31 — ограничение прошивки). Прогресс берётся из `value.Upload` и виден, пока закачку ведёт этот клиент или пока устройство сообщает `Upload` (закачка с другого клиента). Ошибка из `onUpload` выводится под полем.
|
|
57
57
|
|
|
58
58
|
## Пропсы
|
|
59
59
|
|
|
60
|
-
| Название | Тип
|
|
61
|
-
| ---------------- |
|
|
62
|
-
| `id` | `string`
|
|
63
|
-
| `wrapperClass` | `string`
|
|
64
|
-
| `componentClass` | `string`
|
|
65
|
-
| `label` | `{ name?: string }`
|
|
66
|
-
| `value` | `IWidgetFileSystemInfo`
|
|
67
|
-
| `keys` | `{ FS?: string }`
|
|
68
|
-
| `argument` | `string`
|
|
69
|
-
| `accept` | `string`
|
|
70
|
-
| `maxNameLength` | `number`
|
|
71
|
-
| `collapsed` | `boolean`
|
|
72
|
-
| `persistKey` | `string`
|
|
73
|
-
| `onRefresh` | `() => void`
|
|
74
|
-
| `onDelete` | `(name: string) => void`
|
|
75
|
-
| `onUpload` | `(file: File) => void \| Promise<void>`
|
|
60
|
+
| Название | Тип | По умолчанию | Описание |
|
|
61
|
+
| ---------------- | ----------------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| `id` | `string` | `undefined` | Префикс прошивочной группы (обычно `"CFG"`), используется вызывающим кодом для чтения `FS` |
|
|
63
|
+
| `wrapperClass` | `string` | `""` | CSS-классы обёртки |
|
|
64
|
+
| `componentClass` | `string` | `""` | «Мастер-цвет» виджета — роль вида `"bg-red"`, перекрашивает весь виджет |
|
|
65
|
+
| `label` | `{ name?: string }` | `{}` | Заголовок карточки; пустой — «Файловая система» на текущем языке |
|
|
66
|
+
| `value` | `IWidgetFileSystemInfo` | `undefined` | Состояние файловой системы (`CFG.FS`) |
|
|
67
|
+
| `keys` | `{ FS?: string }` | `undefined` | Переопределение имени поля устройства, по умолчанию `FS` |
|
|
68
|
+
| `argument` | `string` | `undefined` | Аргумент команд, по умолчанию `"FS"` (используется вызывающим кодом) |
|
|
69
|
+
| `accept` | `string` | `"*/*"` | Какие файлы можно выбрать (атрибут `accept`), например `".bin, .txt, .pem"` |
|
|
70
|
+
| `maxNameLength` | `number` | `31` | Максимальная длина имени файла |
|
|
71
|
+
| `collapsed` | `boolean` | `false` | Свёрнуто ли тело виджета (`$bindable`) |
|
|
72
|
+
| `persistKey` | `string` | `undefined` | Ключ для сохранения `collapsed` в `localStorage` |
|
|
73
|
+
| `onRefresh` | `() => void` | `undefined` | Кнопка «Обновить» в шапке (`GET FS`); без обработчика кнопка неактивна |
|
|
74
|
+
| `onDelete` | `(name: string) => void` | `undefined` | Удаление файла после подтверждения; `name` — полный путь из `List` |
|
|
75
|
+
| `onUpload` | `(file: File) => void \| Promise<void>` | `undefined` | Закачка выбранного файла; исключение показывается как ошибка под полем |
|
|
76
|
+
| `onDownload` | `(name: string) => void \| Promise<void>` | `undefined` | Скачивание файла (`SET FS` с `Op` `DownloadStart`/`DownloadChunk`/`DownloadDone`) и сохранение; `name` — полный путь из `List`, исключение показывается под таблицей |
|
|
76
77
|
|
|
77
78
|
### IWidgetFileSystemInfo
|
|
78
79
|
|
|
@@ -99,6 +100,7 @@
|
|
|
99
100
|
onRefresh={() => console.log("GET FS")}
|
|
100
101
|
onDelete={(name) => console.log("SET FS", { Op: "Delete", Name: name })}
|
|
101
102
|
onUpload={async (file) => console.log("SET FS UploadStart/UploadChunk/UploadDone", file.name)}
|
|
103
|
+
onDownload={async (name) => console.log("SET FS DownloadStart/DownloadChunk/DownloadDone", name)}
|
|
102
104
|
/>
|
|
103
105
|
```
|
|
104
106
|
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
<!-- $lib/WidgetFileSystem/WidgetFileSystem.svelte — смарт-виджет файловой системы устройства.
|
|
2
2
|
Данные — один объект CFG.FS в том виде, в каком его отдаёт прошивка (FS_WriteState, ProdFactory-ESP):
|
|
3
3
|
Total/Used/Free/List[{Name, Size}] и Upload{Name, Size, Progress} — только пока идёт закачка.
|
|
4
|
-
Ничего не знает про DeviceStore/WebSocket — value/onRefresh/onDelete/onUpload,
|
|
5
|
-
закачки (SET FS с Op UploadStart/UploadChunk/UploadDone
|
|
4
|
+
Ничего не знает про DeviceStore/WebSocket — value/onRefresh/onDelete/onUpload/onDownload, протоколы
|
|
5
|
+
закачки и скачивания (SET FS с Op UploadStart/UploadChunk/UploadDone и DownloadStart/DownloadChunk/DownloadDone)
|
|
6
|
+
и отправка команд на стороне вызывающего. -->
|
|
6
7
|
<script lang="ts">
|
|
7
8
|
import { T } from "../locales/i18n"
|
|
8
9
|
import { slide, fade, scale } from "svelte/transition"
|
|
9
10
|
import { twMerge } from "tailwind-merge"
|
|
10
11
|
import * as UI from ".."
|
|
11
|
-
import type { IWidgetFileSystemFile, IWidgetFileSystemProps, ITableHeader } from "../types"
|
|
12
|
+
import type { IWidgetFileSystemFile, IWidgetFileSystemProps, ITableHeader, ITableContent } from "../types"
|
|
12
13
|
import StorageIcon from "./StorageIcon.svelte"
|
|
13
14
|
import RefreshIcon from "./RefreshIcon.svelte"
|
|
15
|
+
import ButtonDownload from "../libIcons/ButtonDownload.svelte"
|
|
16
|
+
import ButtonDelete from "../libIcons/ButtonDelete.svelte"
|
|
14
17
|
import WidgetHeader from "../WidgetHeader.svelte"
|
|
15
18
|
import { hint, hintsSetting } from "../hints"
|
|
16
19
|
import { widgetAccentStyle } from "../widgetAccent"
|
|
@@ -28,6 +31,7 @@
|
|
|
28
31
|
onRefresh,
|
|
29
32
|
onDelete,
|
|
30
33
|
onUpload,
|
|
34
|
+
onDownload,
|
|
31
35
|
}: IWidgetFileSystemProps = $props()
|
|
32
36
|
|
|
33
37
|
/* Подсказки — кнопка «?» в шапке, выбор общий для всех WidgetFileSystem */
|
|
@@ -61,24 +65,30 @@
|
|
|
61
65
|
/* Полоса — мастер-цвет виджета; почти заполненную ФС (закачка скоро начнёт падать) выдаёт только процент */
|
|
62
66
|
let percentColor = $derived(usedPercent >= 90 ? "var(--red-color)" : usedPercent >= 75 ? "var(--orange-color)" : "var(--muted-color)")
|
|
63
67
|
|
|
64
|
-
/* Таблица файлов: путь обрезается «…», размер — уже
|
|
68
|
+
/* Таблица файлов: путь обрезается «…», размер — уже отформатирован; действия — значки без текста: скачать и удалить (с подтверждением) */
|
|
65
69
|
type FileRow = IWidgetFileSystemFile & { SizeText: string }
|
|
66
70
|
let rows: FileRow[] = $derived(files.map((f) => ({ ...f, SizeText: formatBytes(f.Size) })))
|
|
71
|
+
let actions = $derived(
|
|
72
|
+
[
|
|
73
|
+
onDownload && {
|
|
74
|
+
type: "button",
|
|
75
|
+
data: { icon: ButtonDownload, title: $T("widget.fs.download"), class: "bg-transparent px-0", onClick: (row: FileRow) => handleDownload(row.Name) },
|
|
76
|
+
},
|
|
77
|
+
onDelete && {
|
|
78
|
+
type: "button",
|
|
79
|
+
data: {
|
|
80
|
+
icon: ButtonDelete,
|
|
81
|
+
title: $T("widget.common.delete"),
|
|
82
|
+
class: "bg-transparent px-0 text-(--red-color) hover:bg-(--red-color)/10",
|
|
83
|
+
onClick: (row: FileRow) => (pendingDelete = row.Name),
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
].filter(Boolean) as ITableContent<FileRow>[],
|
|
87
|
+
)
|
|
67
88
|
let header: ITableHeader<FileRow>[] = $derived([
|
|
68
89
|
{ label: { name: $T("widget.fs.path") }, content: [{ type: "text", data: { key: "Name", truncated: true, sortable: true } }], width: "1fr", align: "left" },
|
|
69
90
|
{ label: { name: $T("widget.fs.size") }, content: [{ type: "text", data: { key: "SizeText" } }], width: "6rem", align: "right" },
|
|
70
|
-
...(
|
|
71
|
-
? [
|
|
72
|
-
{
|
|
73
|
-
label: { name: "" },
|
|
74
|
-
content: [
|
|
75
|
-
{ type: "button", data: { name: $T("widget.common.delete"), class: "bg-accent", onClick: (row: FileRow) => (pendingDelete = row.Name) } },
|
|
76
|
-
],
|
|
77
|
-
width: "6.5rem",
|
|
78
|
-
align: "center",
|
|
79
|
-
} as ITableHeader<FileRow>,
|
|
80
|
-
]
|
|
81
|
-
: []),
|
|
91
|
+
...(actions.length ? [{ label: { name: "" }, content: actions, width: `${actions.length * 2.5 + 1}rem`, align: "center" } as ITableHeader<FileRow>] : []),
|
|
82
92
|
])
|
|
83
93
|
|
|
84
94
|
/* Закачка видна, пока её ведёт этот клиент или пока устройство сообщает Upload (закачка с другого клиента);
|
|
@@ -92,6 +102,22 @@
|
|
|
92
102
|
pendingDelete = null
|
|
93
103
|
}
|
|
94
104
|
|
|
105
|
+
/* Скачивание: одно за раз, ошибка вызывающего показывается под таблицей */
|
|
106
|
+
let downloading: string | null = $state(null)
|
|
107
|
+
let downloadError = $state("")
|
|
108
|
+
const handleDownload = async (name: string) => {
|
|
109
|
+
if (!onDownload || downloading) return
|
|
110
|
+
downloadError = ""
|
|
111
|
+
downloading = name
|
|
112
|
+
try {
|
|
113
|
+
await onDownload(name)
|
|
114
|
+
} catch (error) {
|
|
115
|
+
downloadError = $T("widget.fs.download_failed", { error: error instanceof Error ? error.message : String(error) })
|
|
116
|
+
} finally {
|
|
117
|
+
downloading = null
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
95
121
|
/* Закачка: имя проверяется до отправки (прошивка отвергнет длинное имя уже после UploadStart) */
|
|
96
122
|
let uploading = $state(false)
|
|
97
123
|
let uploadError = $state("")
|
|
@@ -159,6 +185,14 @@
|
|
|
159
185
|
{$T("widget.fs.empty")}
|
|
160
186
|
</div>
|
|
161
187
|
{/if}
|
|
188
|
+
{#if downloading}
|
|
189
|
+
<span class="mt-2 truncate text-[12px] text-(--muted-color)" transition:slide={{ duration: 100 }}
|
|
190
|
+
>{$T("widget.fs.downloading", { name: downloading })}</span
|
|
191
|
+
>
|
|
192
|
+
{/if}
|
|
193
|
+
{#if downloadError}
|
|
194
|
+
<span class="mt-2 text-[12px] text-(--red-color)" transition:slide={{ duration: 100 }}>{downloadError}</span>
|
|
195
|
+
{/if}
|
|
162
196
|
</div>
|
|
163
197
|
|
|
164
198
|
<!-- Закачка -->
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script lang="ts"></script>
|
|
2
|
+
|
|
3
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1.5em" height="1.5em" viewBox="0 0 24 24"
|
|
4
|
+
><path
|
|
5
|
+
fill="none"
|
|
6
|
+
stroke="currentColor"
|
|
7
|
+
stroke-linecap="round"
|
|
8
|
+
stroke-linejoin="round"
|
|
9
|
+
stroke-width="1.5"
|
|
10
|
+
d="M12 3.5v12m0 0l-4.5-4.5m4.5 4.5l4.5-4.5M4.5 15.5v1.25c0 1.875 0 2.813.477 3.47a2.5 2.5 0 0 0 .553.553c.657.477 1.595.477 3.47.477h6c1.875 0 2.813 0 3.47-.477a2.5 2.5 0 0 0 .553-.553c.477-.657.477-1.595.477-3.47V15.5"
|
|
11
|
+
/></svg
|
|
12
|
+
>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
declare const ButtonDownload: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type ButtonDownload = InstanceType<typeof ButtonDownload>;
|
|
18
|
+
export default ButtonDownload;
|
|
@@ -644,9 +644,9 @@ const translations = {
|
|
|
644
644
|
zh: "设备文件系统占用;存满后无法写入新文件、日志和更新",
|
|
645
645
|
},
|
|
646
646
|
"widget.fs.hint.files": {
|
|
647
|
-
ru: "Файлы на устройстве: сортировка — по заголовку колонки, удаление —
|
|
648
|
-
en: "Files on the device: sort by the column header, delete with the
|
|
649
|
-
zh: "
|
|
647
|
+
ru: "Файлы на устройстве: сортировка — по заголовку колонки, скачивание и удаление — значками в строке",
|
|
648
|
+
en: "Files on the device: sort by the column header, download and delete with the icons in the row",
|
|
649
|
+
zh: "设备上的文件:点击列标题排序,点击行内图标下载或删除",
|
|
650
650
|
},
|
|
651
651
|
"widget.fs.hint.upload": {
|
|
652
652
|
ru: "Загрузить файл на устройство; файл с тем же именем будет заменён",
|
|
@@ -712,6 +712,9 @@ const translations = {
|
|
|
712
712
|
"widget.fs.files": { ru: "Файлов: {n}", en: "Files: {n}", zh: "文件:{n}" },
|
|
713
713
|
"widget.fs.empty": { ru: "Файлов нет", en: "No files", zh: "没有文件" },
|
|
714
714
|
"widget.fs.upload": { ru: "Закачать файл", en: "Upload file", zh: "上传文件" },
|
|
715
|
+
"widget.fs.download": { ru: "Скачать", en: "Download", zh: "下载" },
|
|
716
|
+
"widget.fs.downloading": { ru: "Скачивание: {name}", en: "Downloading: {name}", zh: "正在下载:{name}" },
|
|
717
|
+
"widget.fs.download_failed": { ru: "Скачивание прервано: {error}", en: "Download aborted: {error}", zh: "下载中断:{error}" },
|
|
715
718
|
"widget.fs.delete_confirm": { ru: "Удалить файл?", en: "Delete file?", zh: "删除文件?" },
|
|
716
719
|
"widget.fs.upload_group": { ru: "Закачка файлов", en: "File upload", zh: "文件上传" },
|
|
717
720
|
"widget.fs.accept": { ru: "Допустимые файлы (accept)", en: "Allowed files (accept)", zh: "允许的文件 (accept)" },
|
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
|
+
/* Контрольные суммы протоколов устройств — строго побайтные, как crc8_rohc/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,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) */
|
|
12
|
+
/* CRC-8/ROHC: poly 0x07 reflected (0xE0), init 0xFF, xorout 0x00, check("123456789") = 0xD0 — рамка POESerial (system_console): HEADER, ARGUMENT, VALUE */
|
|
13
13
|
export const crc8Rohc = (crc, bytes) => {
|
|
14
14
|
for (const byte of bytes) {
|
|
15
15
|
crc ^= byte & 0xff;
|
|
@@ -18,27 +18,3 @@ export const crc8Rohc = (crc, bytes) => {
|
|
|
18
18
|
}
|
|
19
19
|
return crc & 0xff;
|
|
20
20
|
};
|
|
21
|
-
/* Байт для CRC от позиции pos по правилу прошивки (common.c, utf8_seq_byte): целая UTF-8-последовательность даёт младший байт
|
|
22
|
-
своего code point, иначе байт как есть — так прошивка считает CRC и по тексту с эмодзи, и по сырому CBOR */
|
|
23
|
-
const utf8SeqByte = (bytes, pos) => {
|
|
24
|
-
const first = bytes[pos];
|
|
25
|
-
const cont = (offset) => (bytes[pos + offset] & 0xc0) === 0x80;
|
|
26
|
-
if ((first & 0x80) === 0)
|
|
27
|
-
return { data: first, length: 1 };
|
|
28
|
-
if ((first & 0xe0) === 0xc0 && pos + 1 < bytes.length && cont(1))
|
|
29
|
-
return { data: (((first & 0x1f) << 6) | (bytes[pos + 1] & 0x3f)) & 0xff, length: 2 };
|
|
30
|
-
if ((first & 0xf0) === 0xe0 && pos + 2 < bytes.length && cont(1) && cont(2))
|
|
31
|
-
return { data: (((first & 0x0f) << 12) | ((bytes[pos + 1] & 0x3f) << 6) | (bytes[pos + 2] & 0x3f)) & 0xff, length: 3 };
|
|
32
|
-
if ((first & 0xf8) === 0xf0 && pos + 3 < bytes.length && cont(1) && cont(2) && cont(3))
|
|
33
|
-
return { data: (((bytes[pos + 1] & 0x3f) << 12) | ((bytes[pos + 2] & 0x3f) << 6) | (bytes[pos + 3] & 0x3f)) & 0xff, length: 4 };
|
|
34
|
-
return { data: first, length: 1 };
|
|
35
|
-
};
|
|
36
|
-
/* CRC-8/ROHC по правилу прошивки для UTF-8 (crc8_rohc в common.c) — рамка POESerial: HEADER, ARGUMENT, VALUE */
|
|
37
|
-
export const crc8RohcUtf8 = (crc, bytes) => {
|
|
38
|
-
for (let pos = 0; pos < bytes.length;) {
|
|
39
|
-
const { data, length } = utf8SeqByte(bytes, pos);
|
|
40
|
-
crc = crc8Rohc(crc, [data]);
|
|
41
|
-
pos += length;
|
|
42
|
-
}
|
|
43
|
-
return crc;
|
|
44
|
-
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const HEADER_CODES: Record<string, number>;
|
|
2
|
-
export
|
|
2
|
+
export { DEFAULT_ARGUMENT_CODES } from "./dictionaryCodes.js";
|
|
3
3
|
export declare const parseArgumentDictionary: (text: string) => Record<string, number>;
|
|
4
4
|
export declare const nameByCode: (codes: Record<string, number>, code: number) => string;
|
|
@@ -1,67 +1,15 @@
|
|
|
1
1
|
/* $lib/protocols/dictionary.ts */
|
|
2
2
|
/* Словарь заголовков и аргументов протокола HA_Code — заголовок в 2 старших битах, аргумент в 14 младших (makeHaCode). Аргументы по умолчанию —
|
|
3
|
-
из .framework/Dictionary.yaml прошивок
|
|
4
|
-
из
|
|
3
|
+
из мастер-словаря dictionary.yaml рядом (единственный источник; .framework/Dictionary.yaml прошивок — его справочные копии), DEFAULT_ARGUMENT_CODES
|
|
4
|
+
генерируется из него (dictionaryCodes.ts, npm run Dictionary); у устройства словарь может быть свой — его загружают из файла
|
|
5
|
+
(parseArgumentDictionary), своих копий словаря в приложениях не держим */
|
|
5
6
|
export const HEADER_CODES = {
|
|
6
7
|
GET: 0,
|
|
7
8
|
SET: 1,
|
|
8
9
|
"OK!": 2,
|
|
9
10
|
"ER!": 3,
|
|
10
11
|
};
|
|
11
|
-
export
|
|
12
|
-
Unknown: 0,
|
|
13
|
-
Restart: 1,
|
|
14
|
-
DefCfg: 2,
|
|
15
|
-
DlFW: 3,
|
|
16
|
-
RstNVS: 5,
|
|
17
|
-
LocTime: 6,
|
|
18
|
-
Update: 7,
|
|
19
|
-
StackInfo: 8,
|
|
20
|
-
BackUp: 9,
|
|
21
|
-
BindMKey: 10,
|
|
22
|
-
DataStream: 15,
|
|
23
|
-
APsList: 21,
|
|
24
|
-
FS: 22,
|
|
25
|
-
JoinDev: 23,
|
|
26
|
-
JoinUser: 24,
|
|
27
|
-
LeaveUser: 25,
|
|
28
|
-
ModFind: 31,
|
|
29
|
-
ModList: 32,
|
|
30
|
-
ModCap: 33,
|
|
31
|
-
ModInfo: 34,
|
|
32
|
-
ModMode: 35,
|
|
33
|
-
ModCfg: 36,
|
|
34
|
-
ModT1: 37,
|
|
35
|
-
ModT2: 38,
|
|
36
|
-
ModT3: 39,
|
|
37
|
-
ModT4: 40,
|
|
38
|
-
ModT5: 41,
|
|
39
|
-
ModS1: 42,
|
|
40
|
-
ModS2: 43,
|
|
41
|
-
ModS3: 44,
|
|
42
|
-
ModS4: 45,
|
|
43
|
-
ModS5: 46,
|
|
44
|
-
FwUpgS: 51,
|
|
45
|
-
FwUpgP: 52,
|
|
46
|
-
FwUpgD: 53,
|
|
47
|
-
FwUpgE: 54,
|
|
48
|
-
FDwS: 65,
|
|
49
|
-
FDwP: 66,
|
|
50
|
-
FDwD: 67,
|
|
51
|
-
FDwE: 68,
|
|
52
|
-
TestReq: 91,
|
|
53
|
-
MotInit: 111,
|
|
54
|
-
MotMode: 112,
|
|
55
|
-
MotPos: 113,
|
|
56
|
-
MotPID: 114,
|
|
57
|
-
MotInfo: 115,
|
|
58
|
-
MotCfg: 116,
|
|
59
|
-
MotCal: 117,
|
|
60
|
-
OrData: 141,
|
|
61
|
-
OrOfs: 142,
|
|
62
|
-
OrAccCal: 143,
|
|
63
|
-
OrMagCal: 144,
|
|
64
|
-
};
|
|
12
|
+
export { DEFAULT_ARGUMENT_CODES } from "./dictionaryCodes.js";
|
|
65
13
|
/* Аргументы из текста Dictionary.yaml (или его фрагмента): строки «- [Строка, Enum, Число]», комментарии и прочее пропускаются */
|
|
66
14
|
export const parseArgumentDictionary = (text) => Object.fromEntries([...text.matchAll(/^\s*-\s*\[\s*([^,\]]+?)\s*,\s*[^,\]]+?\s*,\s*(\d+)\s*\]/gm)].map(([, name, code]) => [name, Number(code)]));
|
|
67
15
|
/* Имя по коду (обратный поиск) — для показа принятых пакетов; неизвестный код — числом */
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Словарь аргументов протокола HA_Code — единый для всех устройств, обоих фреймворков и приложений
|
|
2
|
+
# Строковое представление аргумента | Название в перечислении | Числовой код
|
|
3
|
+
# Источник — poe-svelte-ui-lib (src/lib/protocols/dictionary.yaml); .framework/Dictionary.yaml фреймворков — копии, не править
|
|
4
|
+
|
|
5
|
+
# 0 — отсутствие аргумента
|
|
6
|
+
- [Unknown, A_UNKNOWN, 0] # Аргумент отсутствует
|
|
7
|
+
|
|
8
|
+
# Служебные команды
|
|
9
|
+
- [Restart, A_RESTART, 1] # SET: перезагрузка устройства
|
|
10
|
+
- [DefCfg, A_DEF_CONFIG, 2] # SET: сброс конфигурации к заводской
|
|
11
|
+
- [DlFW, A_DOWNLOAD_FIRMWARE, 3] # SET: скачивание прошивки с сервера (http_client)
|
|
12
|
+
- [RstNVS, A_RESET_NVS, 5] # SET: очистка NVS и перезагрузка
|
|
13
|
+
- [LocTime, A_LOCAL_TIME, 6] # GET: локальное время устройства
|
|
14
|
+
- [Update, A_UPDATE, 7] # OK!/ER!: обновление данных в интерфейсе (DevCloud ретранслирует DataStream)
|
|
15
|
+
- [StackInfo, A_STACK_INFO, 8] # GET: снимок куч и стеков задач
|
|
16
|
+
- [BackUp, A_BACKUP, 9] # Резервная копия конфигурации
|
|
17
|
+
- [BindMKey, A_BIND_MOD_KEY, 10] # SET: привязка ключа модуля (ModBindKey)
|
|
18
|
+
- [DataStream, A_DATA_STREAM, 15] # OK!: поток данных групп конфигурации (CS_PushGroupStream)
|
|
19
|
+
|
|
20
|
+
# Локальная сеть
|
|
21
|
+
- [APsList, A_APS_LIST, 21] # GET: список точек доступа Wi-Fi
|
|
22
|
+
- [FS, A_FS, 22] # Файловая система: GET FS — снимок, SET FS {Op: Delete|UploadStart|UploadChunk|UploadDone|DownloadStart|DownloadChunk|DownloadDone}
|
|
23
|
+
- [JoinDev, A_JOIN_DEV, 23] # SET: подтверждение подключения устройства к облаку
|
|
24
|
+
- [JoinUser, A_JOIN_USER, 24] # SET: пользователь подключился к устройству
|
|
25
|
+
- [LeaveUser, A_LEAVE_USER, 25] # SET: пользователь отключился от устройства
|
|
26
|
+
|
|
27
|
+
# Модули в рамках изделия
|
|
28
|
+
- [ModFind, A_MODULE_FIND, 31] # GET/OK!: поиск модулей на шинах изделия
|
|
29
|
+
- [ModList, A_MODULE_LIST, 32] # Список модулей изделия (CFG.ModList)
|
|
30
|
+
- [ModCap, A_MODULE_CAPTURE, 33] # GET/OK!: захват модуля для привязки
|
|
31
|
+
- [ModInfo, A_MODULE_INFO, 34] # GET/OK!: информация о модуле
|
|
32
|
+
- [ModMode, A_MODULE_MODE, 35] # SET: режим работы модуля
|
|
33
|
+
- [ModCfg, A_MODULE_CFG, 36] # GET/SET: конфигурация модуля (группы config_service)
|
|
34
|
+
- [ModT1, A_MODULE_TELEMETRY_1, 37] # Телеметрия модуля, канал 1
|
|
35
|
+
- [ModT2, A_MODULE_TELEMETRY_2, 38] # Телеметрия модуля, канал 2
|
|
36
|
+
- [ModT3, A_MODULE_TELEMETRY_3, 39] # Телеметрия модуля, канал 3
|
|
37
|
+
- [ModT4, A_MODULE_TELEMETRY_4, 40] # Телеметрия модуля, канал 4
|
|
38
|
+
- [ModT5, A_MODULE_TELEMETRY_5, 41] # Телеметрия модуля, канал 5
|
|
39
|
+
- [ModS1, A_MODULE_STREAM_1, 42] # Поток данных модуля, канал 1
|
|
40
|
+
- [ModS2, A_MODULE_STREAM_2, 43] # Поток данных модуля, канал 2
|
|
41
|
+
- [ModS3, A_MODULE_STREAM_3, 44] # Поток данных модуля, канал 3
|
|
42
|
+
- [ModS4, A_MODULE_STREAM_4, 45] # Поток данных модуля, канал 4
|
|
43
|
+
- [ModS5, A_MODULE_STREAM_5, 46] # Поток данных модуля, канал 5
|
|
44
|
+
|
|
45
|
+
# Обновление прошивки
|
|
46
|
+
- [FwUpgS, A_UPGRADE_START, 51] # Старт обновления прошивки модуля по шине
|
|
47
|
+
- [FwUpgP, A_UPGRADE_PROC, 52] # Блок прошивки при обновлении по шине
|
|
48
|
+
- [FwUpgD, A_UPGRADE_DONE, 53] # Завершение обновления прошивки по шине
|
|
49
|
+
- [FwUpgE, A_UPGRADE_ERR, 54] # Ошибка обновления прошивки по шине
|
|
50
|
+
|
|
51
|
+
- [TestReq, TEST_REQUEST, 91] # GET: тестовый запрос
|
|
52
|
+
|
|
53
|
+
# Работа с модулями перемещения (оси подвесов, линейные перемещения, серво приводы и подобные)
|
|
54
|
+
- [MotInit, A_MOTION_INIT, 111] # SET: включение/калибровка (1 байт: команда)
|
|
55
|
+
- [MotMode, A_MOTION_MODE, 112] # SET: режим управления (2 байта: ctrl_mode + torque_mode)
|
|
56
|
+
- [MotPos, A_MOTION_POS, 113] # GET/SET: целевое значение (4 байта: float)
|
|
57
|
+
- [MotPID, A_MOTION_PID, 114] # SET: коэффициенты PID (13 байт: selector + Kp + Ki + Kd)
|
|
58
|
+
- [MotInfo, A_MOTION_INFO, 115] # GET: телеметрия (32 байта: angle+vel+Iq+Id+target+flags)
|
|
59
|
+
- [MotCfg, A_MOTION_CFG, 116] # GET/SET: конфигурация мотора (28 байт: лимиты+параметры)
|
|
60
|
+
- [MotCal, A_MOTION_CAL, 117] # SET: запуск калибровки нулевого угла (0 байт)
|
|
61
|
+
|
|
62
|
+
# Работа с модулями ориентации (IMU, магнетометры, GNSS и подобные)
|
|
63
|
+
- [OrData, A_ORIENTATION_DATA, 141] # SET: углы ориентации от модуля
|
|
64
|
+
- [OrOfs, A_ORIENTATION_OFFSET, 142] # Смещения ориентации
|
|
65
|
+
- [OrAccCal, A_ORIENTATION_ACC_CALIB, 143] # Калибровка акселерометра
|
|
66
|
+
- [OrMagCal, A_ORIENTATION_MAG_CALIB, 144] # Калибровка магнетометра
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const DEFAULT_ARGUMENT_CODES: Record<string, number>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/* $lib/protocols/dictionaryCodes.ts — СГЕНЕРИРОВАНО из dictionary.yaml (npm run Dictionary), руками не править */
|
|
2
|
+
export const DEFAULT_ARGUMENT_CODES = {
|
|
3
|
+
Unknown: 0,
|
|
4
|
+
Restart: 1,
|
|
5
|
+
DefCfg: 2,
|
|
6
|
+
DlFW: 3,
|
|
7
|
+
RstNVS: 5,
|
|
8
|
+
LocTime: 6,
|
|
9
|
+
Update: 7,
|
|
10
|
+
StackInfo: 8,
|
|
11
|
+
BackUp: 9,
|
|
12
|
+
BindMKey: 10,
|
|
13
|
+
DataStream: 15,
|
|
14
|
+
APsList: 21,
|
|
15
|
+
FS: 22,
|
|
16
|
+
JoinDev: 23,
|
|
17
|
+
JoinUser: 24,
|
|
18
|
+
LeaveUser: 25,
|
|
19
|
+
ModFind: 31,
|
|
20
|
+
ModList: 32,
|
|
21
|
+
ModCap: 33,
|
|
22
|
+
ModInfo: 34,
|
|
23
|
+
ModMode: 35,
|
|
24
|
+
ModCfg: 36,
|
|
25
|
+
ModT1: 37,
|
|
26
|
+
ModT2: 38,
|
|
27
|
+
ModT3: 39,
|
|
28
|
+
ModT4: 40,
|
|
29
|
+
ModT5: 41,
|
|
30
|
+
ModS1: 42,
|
|
31
|
+
ModS2: 43,
|
|
32
|
+
ModS3: 44,
|
|
33
|
+
ModS4: 45,
|
|
34
|
+
ModS5: 46,
|
|
35
|
+
FwUpgS: 51,
|
|
36
|
+
FwUpgP: 52,
|
|
37
|
+
FwUpgD: 53,
|
|
38
|
+
FwUpgE: 54,
|
|
39
|
+
TestReq: 91,
|
|
40
|
+
MotInit: 111,
|
|
41
|
+
MotMode: 112,
|
|
42
|
+
MotPos: 113,
|
|
43
|
+
MotPID: 114,
|
|
44
|
+
MotInfo: 115,
|
|
45
|
+
MotCfg: 116,
|
|
46
|
+
MotCal: 117,
|
|
47
|
+
OrData: 141,
|
|
48
|
+
OrOfs: 142,
|
|
49
|
+
OrAccCal: 143,
|
|
50
|
+
OrMagCal: 144,
|
|
51
|
+
};
|
|
@@ -4,7 +4,7 @@ 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
|
|
7
|
+
export { crc16Kermit, crc8Rohc } from "./crc.js";
|
|
8
8
|
export { BROADCAST_BUS_ID, makeHaCode, splitHaCode, packetObjectCrc, encodePacket, decodePacket, objectsFor, createPacketStreamParser, type PacketObject, type DecodedPacketObject, type PacketParse, } from "./packet.js";
|
|
9
9
|
export { encodePoeSerialFrame, createPoeSerialParser, poeSerialValueFromText, type PoeSerialItem } from "./poeSerial.js";
|
|
10
10
|
export { CAN_FINAL_BIT, CAN_PACKET_ID_MASK, canFragments, encodeSlcanFrame, encodeCanPacket, createCanParser, slcanOpenCommands, type CanFrame, type CanMessage, } from "./can.js";
|
package/dist/protocols/index.js
CHANGED
|
@@ -10,11 +10,11 @@ export { CborToValue, ValueToCbor, cborToValue, valueToCbor } from "./cbor/value
|
|
|
10
10
|
/* JSON-текст ↔ CBOR: двоичные данные — base64, ключи с "BIN" кодируются байтами (поля ввода терминала и отладки) */
|
|
11
11
|
export { CborToJson, JsonToCbor, cborToJson, jsonToCbor } from "./cbor/json.js";
|
|
12
12
|
/* Контрольные суммы и пакет poe_cbor_pkt { TrgBusID : [RetBusID, HA_Code, Payload, CRC16] } (CAN, RS485) */
|
|
13
|
-
export { crc16Kermit, crc8Rohc
|
|
13
|
+
export { crc16Kermit, crc8Rohc } from "./crc.js";
|
|
14
14
|
export { BROADCAST_BUS_ID, makeHaCode, splitHaCode, packetObjectCrc, encodePacket, decodePacket, objectsFor, createPacketStreamParser, } from "./packet.js";
|
|
15
15
|
/* Рамка POESerial (UART-консоль system_console): сборка команды и потоковый разбор кадров, строк Teleplot и текста */
|
|
16
16
|
export { encodePoeSerialFrame, createPoeSerialParser, poeSerialValueFromText } from "./poeSerial.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 — по умолчанию из
|
|
19
|
+
/* Словарь заголовков и аргументов HA_Code — по умолчанию из мастер-словаря dictionary.yaml библиотеки, разбор файла словаря */
|
|
20
20
|
export { HEADER_CODES, DEFAULT_ARGUMENT_CODES, parseArgumentDictionary, nameByCode } from "./dictionary.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* $lib/protocols/poeSerial.ts */
|
|
2
2
|
/* Рамка POESerial (system_console прошивки): [SOH] HEADER [US] ARGUMENT [STX] VALUE [ETX] CRC8 [US] FHS [EOT] \n — CRC8 двумя hex-символами
|
|
3
|
-
по HEADER+ARGUMENT+VALUE (
|
|
3
|
+
по HEADER+ARGUMENT+VALUE (crc8Rohc), FHS (свободная куча) только у кадров устройства. Устройство шлёт VALUE текстом (лог или hex-дамп CBOR),
|
|
4
4
|
устройству VALUE уходит сырыми байтами CBOR. Между кадрами — текст: строки Teleplot «>Имя:Значение» (PLOT) и прочий вывод (RAW) */
|
|
5
|
-
import {
|
|
5
|
+
import { crc8Rohc } from "./crc.js";
|
|
6
6
|
import { jsonToCbor } from "./cbor/json.js";
|
|
7
7
|
export const SOH = 0x01;
|
|
8
8
|
export const STX = 0x02;
|
|
@@ -20,7 +20,7 @@ const hex2 = (value) => value.toString(16).toUpperCase().padStart(2, "0");
|
|
|
20
20
|
export const encodePoeSerialFrame = (header, argument, value) => {
|
|
21
21
|
const headerBytes = encoder.encode(header);
|
|
22
22
|
const argumentBytes = encoder.encode(argument);
|
|
23
|
-
const crc =
|
|
23
|
+
const crc = crc8Rohc(crc8Rohc(crc8Rohc(0xff, headerBytes), argumentBytes), value);
|
|
24
24
|
return new Uint8Array([SOH, ...headerBytes, US, ...argumentBytes, STX, ...value, ETX, ...encoder.encode(hex2(crc)), EOT]);
|
|
25
25
|
};
|
|
26
26
|
/* Строки текста вне кадров: Teleplot «>Имя:Значение» → plot, остальное → raw; пустые и с управляющими символами пропускаются */
|
|
@@ -46,7 +46,7 @@ const frameItem = (frame) => {
|
|
|
46
46
|
const tailUs = frame.indexOf(US, etx + 1);
|
|
47
47
|
const crc = decoder.decode(frame.subarray(etx + 1, tailUs < 0 ? frame.length - 1 : tailUs)).trim();
|
|
48
48
|
const freeHeap = tailUs < 0 ? "" : decoder.decode(frame.subarray(tailUs + 1, frame.length - 1)).trim();
|
|
49
|
-
const crcCalc =
|
|
49
|
+
const crcCalc = crc8Rohc(crc8Rohc(crc8Rohc(0xff, header), argument), valueBytes);
|
|
50
50
|
return {
|
|
51
51
|
type: "frame",
|
|
52
52
|
header: decoder.decode(header),
|
package/dist/types.d.ts
CHANGED
|
@@ -319,6 +319,7 @@ export interface ITableSelect<T extends object> {
|
|
|
319
319
|
export interface ITableButton<T extends object> {
|
|
320
320
|
name?: string | ((row: T) => string);
|
|
321
321
|
icon?: ConstructorOfATypedSvelteComponent | string;
|
|
322
|
+
title?: string | ((row: T) => string);
|
|
322
323
|
class?: string | ((row: T) => string);
|
|
323
324
|
eventHandler?: IUIComponentHandler;
|
|
324
325
|
onClick?: (row: T) => void;
|
|
@@ -571,6 +572,7 @@ export interface IWidgetFileSystemProps {
|
|
|
571
572
|
persistKey?: string;
|
|
572
573
|
onRefresh?: () => void;
|
|
573
574
|
onDelete?: (name: string) => void;
|
|
575
|
+
onDownload?: (name: string) => void | Promise<void>;
|
|
574
576
|
onUpload?: (file: File) => void | Promise<void>;
|
|
575
577
|
}
|
|
576
578
|
export interface IWidgetStackInfoHeap {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poe-svelte-ui-lib",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.68.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/Oleg-Perevyshin/ElementsUI.git"
|
|
@@ -11,12 +11,13 @@
|
|
|
11
11
|
"dev": "vite dev",
|
|
12
12
|
"build": "vite build",
|
|
13
13
|
"preview": "vite preview",
|
|
14
|
-
"prepack": "svelte-kit sync && svelte-package && publint",
|
|
14
|
+
"prepack": "node scripts/GenerateDictionary.mjs && svelte-kit sync && svelte-package && publint",
|
|
15
15
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
16
16
|
"CheckUpdate": "npx npm-check-updates -u && npm install",
|
|
17
17
|
"UpdateIconsLib": "tsx src/lib/IconsCatalog/iconsLib.ts",
|
|
18
18
|
"Formatting": "prettier --write .",
|
|
19
|
-
"
|
|
19
|
+
"Dictionary": "node scripts/GenerateDictionary.mjs",
|
|
20
|
+
"TestProtocols": "node scripts/GenerateDictionary.mjs && svelte-package && node --test tests/*.test.mjs"
|
|
20
21
|
},
|
|
21
22
|
"svelte": "./dist/index.js",
|
|
22
23
|
"types": "./dist/index.d.ts",
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
"types": "./dist/protocols/index.d.ts",
|
|
30
31
|
"default": "./dist/protocols/index.js"
|
|
31
32
|
},
|
|
33
|
+
"./protocols/dictionary.yaml": "./dist/protocols/dictionary.yaml",
|
|
32
34
|
"./locales": {
|
|
33
35
|
"types": "./dist/locales/index.d.ts",
|
|
34
36
|
"default": "./dist/locales/index.js"
|