poe-svelte-ui-lib 1.74.1 → 1.74.2
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/dist/protocols/cbor/encoder.d.ts +1 -0
- package/dist/protocols/cbor/encoder.js +5 -0
- package/dist/protocols/index.js +1 -1
- package/dist/protocols/packet.d.ts +2 -1
- package/dist/protocols/packet.js +36 -14
- package/dist/protocols/xPoeDebug.d.ts +2 -1
- package/dist/protocols/xPoeDebug.js +24 -22
- package/package.json +1 -1
|
@@ -16,6 +16,7 @@ export declare class CborEncoder {
|
|
|
16
16
|
encodeFloat16(value: number): this;
|
|
17
17
|
encodeString(value: string): this;
|
|
18
18
|
encodeBytes(value: Uint8Array | number[]): this;
|
|
19
|
+
encodeRaw(value: Uint8Array): this;
|
|
19
20
|
encodeArrayStart(count: number | null): this;
|
|
20
21
|
encodeMapStart(count: number | null): this;
|
|
21
22
|
encodeBreak(): this;
|
|
@@ -211,6 +211,11 @@ export class CborEncoder {
|
|
|
211
211
|
this.writeBytes(bytes);
|
|
212
212
|
return this;
|
|
213
213
|
}
|
|
214
|
+
/* Добавить заранее проверенный CBOR item без повторного кодирования. */
|
|
215
|
+
encodeRaw(value) {
|
|
216
|
+
this.writeBytes(Array.from(value));
|
|
217
|
+
return this;
|
|
218
|
+
}
|
|
214
219
|
/* Начать кодирование массива - count элементов или null для неопределённой длины */
|
|
215
220
|
encodeArrayStart(count) {
|
|
216
221
|
if (count === null)
|
package/dist/protocols/index.js
CHANGED
|
@@ -14,7 +14,7 @@ export { crc16Kermit, crc8Rohc } from "./crc.js";
|
|
|
14
14
|
export { BROADCAST_BUS_ID, makeCmd, splitCmd, EXTERNAL_BUS_ID, packetObjectCrc, encodePacket, decodePacket, objectsFor, createPacketStreamParser, } from "./packet.js";
|
|
15
15
|
/* Графики отладки: кадр OK! DebPlot (SC_PLOT прошивки) — значения из CBOR-карты, график на группу, серии в порядке устройства */
|
|
16
16
|
export { createPlotGroups, debPlotValues, DEB_PLOT_ARGUMENT } from "./plotGroups.js";
|
|
17
|
-
/* System Console over UART:
|
|
17
|
+
/* System Console over UART: single-record poe_cbor_pkt with debug-only FHS */
|
|
18
18
|
export { encodeXPoeDebugFrame, createXPoeDebugParser, xPoeDebugCmd, xPoeDebugValueFromText } from "./xPoeDebug.js";
|
|
19
19
|
/* Отображение Value xPOE-Debug в таблице/модалке и копирование Value/HEX */
|
|
20
20
|
export { formatXPoeDebugValue, formatXPoeDebugValueForTable, formatXPoeDebugModal, bindXPoeDebugCopyButtons } from "./xPoeDebugView.js";
|
|
@@ -19,7 +19,8 @@ export declare const splitCmd: (cmd: number) => {
|
|
|
19
19
|
argument: number;
|
|
20
20
|
reply: boolean;
|
|
21
21
|
};
|
|
22
|
-
export declare const packetObjectCrc: (
|
|
22
|
+
export declare const packetObjectCrc: (encodedRecordPrefix: Uint8Array) => number;
|
|
23
|
+
export declare const encodePacketBytesPayload: (bytes: Uint8Array) => Uint8Array;
|
|
23
24
|
export declare const encodePacket: (objects: PacketObject[]) => Uint8Array;
|
|
24
25
|
export type PacketParse = {
|
|
25
26
|
status: "complete";
|
package/dist/protocols/packet.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/* $lib/protocols/packet.ts */
|
|
2
|
-
/*
|
|
3
|
-
|
|
4
|
-
(по 2 байта big-endian) и Payload — не зависит от того, как числа записаны в CBOR. Общая точка сборки и разбора для терминала (CAN, RS485) и облака */
|
|
2
|
+
/* Общий пакет poe_cbor_pkt: indefinite ARRAY записей [TrgBusID, SrcBusID, Cmd, Payload, CRC16].
|
|
3
|
+
Payload — ровно один CBOR item. CRC-16/KERMIT считается по сырым CBOR-байтам записи от её заголовка до конца Payload. */
|
|
5
4
|
import { CborDecoder } from "./cbor/decoder.js";
|
|
6
5
|
import { CborEncoder } from "./cbor/encoder.js";
|
|
7
6
|
import { CborEndOfDataError } from "./cbor/types.js";
|
|
@@ -17,22 +16,31 @@ export const splitCmd = (cmd) => ({
|
|
|
17
16
|
argument: (cmd >> 1) & 0x1fff,
|
|
18
17
|
reply: (cmd & 1) === 1,
|
|
19
18
|
});
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
export const packetObjectCrc = (encodedRecordPrefix) => crc16Kermit(0, encodedRecordPrefix);
|
|
20
|
+
/* Кодирует сырые байты как один CBOR byte string для Payload. */
|
|
21
|
+
export const encodePacketBytesPayload = (bytes) => {
|
|
22
|
+
const encoder = new CborEncoder();
|
|
23
|
+
encoder.encodeBytes(bytes);
|
|
24
|
+
return encoder.getResult();
|
|
24
25
|
};
|
|
25
26
|
/* Пакет из одного или нескольких объектов — те же байты, что CBOR_PACK/CBOR_PktEncFinish прошивки */
|
|
26
27
|
export const encodePacket = (objects) => {
|
|
27
28
|
const encoder = new CborEncoder();
|
|
28
29
|
encoder.encodeArrayStart(null);
|
|
29
30
|
for (const { trgBusID, srcBusID, cmd, payload } of objects) {
|
|
31
|
+
const recordStart = encoder.getResult().length;
|
|
30
32
|
encoder.encodeArrayStart(5);
|
|
31
33
|
encoder.encodeUnsigned(trgBusID);
|
|
32
34
|
encoder.encodeUnsigned(srcBusID);
|
|
33
35
|
encoder.encodeUnsigned(cmd);
|
|
34
|
-
encoder.
|
|
35
|
-
|
|
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);
|
|
43
|
+
encoder.encodeUnsigned(packetObjectCrc(prefix));
|
|
36
44
|
}
|
|
37
45
|
encoder.encodeBreak();
|
|
38
46
|
return encoder.getResult();
|
|
@@ -52,13 +60,27 @@ const readValue = (bytes, offset) => {
|
|
|
52
60
|
};
|
|
53
61
|
const toNumber = (value) => (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 0xffff ? value : null);
|
|
54
62
|
/* Объект пакета; не по формату (не 5 элементов, не те типы) — null, как CBOR_PktDecNext прошивки его пропускает */
|
|
55
|
-
const toObject = (value) => {
|
|
63
|
+
const toObject = (value, rawRecord) => {
|
|
56
64
|
if (!Array.isArray(value) || value.length !== 5)
|
|
57
65
|
return null;
|
|
58
|
-
const [trgBusID, srcBusID, cmd,
|
|
59
|
-
if (trgBusID === null || srcBusID === null || cmd === 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)
|
|
68
|
+
return null;
|
|
69
|
+
const info = rawRecord[0] & 0x1f;
|
|
70
|
+
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)
|
|
72
|
+
return null;
|
|
73
|
+
const fields = new CborDecoder(rawRecord.subarray(headerLength));
|
|
74
|
+
for (let i = 0; i < 3; i++)
|
|
75
|
+
if (!fields.skipValue())
|
|
76
|
+
return null;
|
|
77
|
+
const payloadStart = headerLength + fields.offset;
|
|
78
|
+
if (!fields.skipValue())
|
|
60
79
|
return null;
|
|
61
|
-
|
|
80
|
+
const payloadEnd = headerLength + fields.offset;
|
|
81
|
+
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) };
|
|
62
84
|
};
|
|
63
85
|
/* Разбор пакета с начала буфера — и для целого кадра (CAN), и для потока (RS485: «incomplete» — ждать ещё байт, «garbage» — сдвинуться на байт) */
|
|
64
86
|
export const decodePacket = (bytes) => {
|
|
@@ -93,7 +115,7 @@ export const decodePacket = (bytes) => {
|
|
|
93
115
|
if (!value)
|
|
94
116
|
return { status: "incomplete" };
|
|
95
117
|
offset += value.length;
|
|
96
|
-
const object = toObject(value.value);
|
|
118
|
+
const object = toObject(value.value, bytes.subarray(offset - value.length, offset));
|
|
97
119
|
if (object)
|
|
98
120
|
objects.push(object);
|
|
99
121
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type XPoeDebugItem = {
|
|
2
2
|
type: "frame";
|
|
3
|
+
trgId: number;
|
|
3
4
|
srcId: number;
|
|
4
5
|
cmd: number;
|
|
5
6
|
header: string;
|
|
@@ -14,7 +15,7 @@ export type XPoeDebugItem = {
|
|
|
14
15
|
text: string;
|
|
15
16
|
};
|
|
16
17
|
export declare const xPoeDebugCmd: (header: string, argument: string) => number;
|
|
17
|
-
export declare const encodeXPoeDebugFrame: (header: string, argument: string, value: Uint8Array, srcId?: number, freeHeap?: number | null) => Uint8Array;
|
|
18
|
+
export declare const encodeXPoeDebugFrame: (header: string, argument: string, value: Uint8Array, srcId?: number, freeHeap?: number | null, trgId?: number) => Uint8Array;
|
|
18
19
|
export declare const createXPoeDebugParser: () => {
|
|
19
20
|
push(chunk: Uint8Array): XPoeDebugItem[];
|
|
20
21
|
reset(): void;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
/*
|
|
2
|
-
first four array items, including the array header. UART is a stream; the definite array is self-delimiting. */
|
|
1
|
+
/* Debug is a one-record poe_cbor_pkt. Its record adds FHS to the common bus fields. */
|
|
3
2
|
import { CborDecoder } from "./cbor/decoder.js";
|
|
4
3
|
import { CborEndOfDataError } from "./cbor/types.js";
|
|
5
4
|
import { crc16Kermit } from "./crc.js";
|
|
@@ -8,7 +7,8 @@ import { DEFAULT_ARGUMENT_CODES, HEADER_CODES, nameByCode } from "./dictionary.j
|
|
|
8
7
|
import { makeCmd, splitCmd } from "./packet.js";
|
|
9
8
|
const MAX_TEXT_TAIL = 4096;
|
|
10
9
|
const MAX_FRAME = 8192;
|
|
11
|
-
const
|
|
10
|
+
const PACKET_START = 0x9f;
|
|
11
|
+
const RECORD6 = 0x86;
|
|
12
12
|
const decoder = new TextDecoder();
|
|
13
13
|
const encodeUInt = (input) => {
|
|
14
14
|
const value = BigInt(input);
|
|
@@ -30,16 +30,16 @@ export const xPoeDebugCmd = (header, argument) => {
|
|
|
30
30
|
return makeCmd(hdr, arg);
|
|
31
31
|
};
|
|
32
32
|
/* Команда терминала: CRC считается по тем CBOR-байтам, которые реально отправляются. */
|
|
33
|
-
export const encodeXPoeDebugFrame = (header, argument, value, srcId = 0, freeHeap = null) => {
|
|
33
|
+
export const encodeXPoeDebugFrame = (header, argument, value, srcId = 0, freeHeap = null, trgId = 0) => {
|
|
34
34
|
const cmd = xPoeDebugCmd(header, argument);
|
|
35
|
-
if (!
|
|
36
|
-
throw new Error("
|
|
35
|
+
if (![srcId, trgId, cmd].every((n) => Number.isInteger(n) && n >= 0 && n <= 0xffff))
|
|
36
|
+
throw new Error("TrgBusID, SrcBusID и Cmd должны быть uint16");
|
|
37
37
|
const valueParser = new CborDecoder(value);
|
|
38
38
|
if (!valueParser.skipValue() || valueParser.offset !== value.length)
|
|
39
39
|
throw new Error("Value должен содержать ровно один CBOR item");
|
|
40
|
-
const
|
|
41
|
-
const crc = crc16Kermit(0,
|
|
42
|
-
return new Uint8Array([...
|
|
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]);
|
|
43
43
|
};
|
|
44
44
|
const textItems = (bytes) => decoder
|
|
45
45
|
.decode(bytes)
|
|
@@ -48,29 +48,30 @@ const textItems = (bytes) => decoder
|
|
|
48
48
|
.filter((line) => line && ![...line].some((char) => char.charCodeAt(0) < 0x20 && char !== "\t"))
|
|
49
49
|
.map((text) => ({ type: "raw", text }));
|
|
50
50
|
const readUInt = (value) => (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 0xffff ? value : null);
|
|
51
|
-
/* Декодирует
|
|
51
|
+
/* Декодирует пакет из одной записи с FHS; null означает неполный или невалидный кадр. */
|
|
52
52
|
const frameItem = (buffer) => {
|
|
53
|
-
if (buffer[0] !==
|
|
53
|
+
if (buffer[0] !== PACKET_START || buffer[1] !== RECORD6)
|
|
54
54
|
return { status: "garbage" };
|
|
55
55
|
try {
|
|
56
56
|
const cbor = new CborDecoder(buffer);
|
|
57
|
-
cbor.decode(); /* Проверка структуры и границы всего
|
|
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" };
|
|
58
60
|
const length = cbor.offset;
|
|
59
|
-
const fields = new CborDecoder(buffer.subarray(
|
|
61
|
+
const fields = new CborDecoder(buffer.subarray(2, length));
|
|
62
|
+
const trgId = readUInt(fields.decode());
|
|
60
63
|
const srcId = readUInt(fields.decode());
|
|
61
64
|
const cmd = readUInt(fields.decode());
|
|
62
|
-
if (srcId === null || cmd === null)
|
|
65
|
+
if (trgId === null || srcId === null || cmd === null)
|
|
63
66
|
return { status: "garbage" };
|
|
64
|
-
const valueStart =
|
|
67
|
+
const valueStart = 2 + fields.offset;
|
|
65
68
|
if (!fields.skipValue())
|
|
66
69
|
return { status: "garbage" };
|
|
67
|
-
const valueEnd =
|
|
70
|
+
const valueEnd = 2 + fields.offset;
|
|
68
71
|
const freeHeapValue = fields.decode();
|
|
69
|
-
const crcStart =
|
|
72
|
+
const crcStart = 2 + fields.offset;
|
|
70
73
|
const crc = readUInt(fields.decode());
|
|
71
|
-
if (fields.offset !== length -
|
|
72
|
-
crc === null ||
|
|
73
|
-
(freeHeapValue !== null && (typeof freeHeapValue !== "number" || !Number.isInteger(freeHeapValue) || freeHeapValue < 0)))
|
|
74
|
+
if (fields.offset !== length - 3 || crc === null || (freeHeapValue !== null && (typeof freeHeapValue !== "number" || !Number.isInteger(freeHeapValue) || freeHeapValue < 0)))
|
|
74
75
|
return { status: "garbage" };
|
|
75
76
|
const valueBytes = buffer.slice(valueStart, valueEnd);
|
|
76
77
|
const { header, argument } = splitCmd(cmd);
|
|
@@ -79,6 +80,7 @@ const frameItem = (buffer) => {
|
|
|
79
80
|
length,
|
|
80
81
|
item: {
|
|
81
82
|
type: "frame",
|
|
83
|
+
trgId,
|
|
82
84
|
srcId,
|
|
83
85
|
cmd,
|
|
84
86
|
header: Object.entries(HEADER_CODES).find(([, code]) => code === header)?.[0] ?? String(header),
|
|
@@ -86,7 +88,7 @@ const frameItem = (buffer) => {
|
|
|
86
88
|
value: Array.from(valueBytes, (byte) => byte.toString(16).padStart(2, "0").toUpperCase()).join(""),
|
|
87
89
|
valueBytes,
|
|
88
90
|
crc: crc.toString(16).toUpperCase().padStart(4, "0"),
|
|
89
|
-
crcOk: crc16Kermit(0, buffer.subarray(
|
|
91
|
+
crcOk: crc16Kermit(0, buffer.subarray(1, crcStart)) === crc,
|
|
90
92
|
freeHeap: typeof freeHeapValue === "number" ? String(freeHeapValue) : "",
|
|
91
93
|
},
|
|
92
94
|
};
|
|
@@ -110,7 +112,7 @@ export const createXPoeDebugParser = () => {
|
|
|
110
112
|
buffer = concat(buffer, chunk);
|
|
111
113
|
const items = [];
|
|
112
114
|
while (buffer.length) {
|
|
113
|
-
const start = buffer.indexOf(
|
|
115
|
+
const start = buffer.indexOf(PACKET_START);
|
|
114
116
|
if (start < 0) {
|
|
115
117
|
const cut = buffer.length > MAX_TEXT_TAIL ? buffer.length : buffer.lastIndexOf(0x0a);
|
|
116
118
|
if (cut >= 0) {
|