@springfield/ham-radio-utils 2.2.2 → 3.0.1
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/CHANGELOG.md +26 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/memory/segmented-memory.js +2 -2
- package/dist/memory/segmented-memory.js.map +1 -1
- package/dist/utils/memory-channel-utils.d.ts.map +1 -1
- package/dist/utils/memory-channel-utils.js +1 -3
- package/dist/utils/memory-channel-utils.js.map +1 -1
- package/dist/utils/memory-data-utils.d.ts.map +1 -1
- package/dist/utils/memory-data-utils.js +1 -3
- package/dist/utils/memory-data-utils.js.map +1 -1
- package/dist/utils/schema-validator.d.ts.map +1 -1
- package/dist/utils/schema-validator.js +8 -5
- package/dist/utils/schema-validator.js.map +1 -1
- package/dist/utils/to-hex-words.js +3 -3
- package/dist/utils/to-hex-words.js.map +1 -1
- package/dist/utils/ui-logger-factory.d.ts +11 -2
- package/dist/utils/ui-logger-factory.d.ts.map +1 -1
- package/dist/utils/ui-logger-factory.js +19 -9
- package/dist/utils/ui-logger-factory.js.map +1 -1
- package/dist/utils/ui-logger.d.ts +7 -5
- package/dist/utils/ui-logger.d.ts.map +1 -1
- package/dist/utils/ui-logger.js +42 -23
- package/dist/utils/ui-logger.js.map +1 -1
- package/package.json +8 -8
- package/src/index.ts +0 -2
- package/src/memory/segmented-memory.ts +2 -2
- package/src/utils/memory-channel-utils.ts +1 -3
- package/src/utils/memory-data-utils.ts +1 -3
- package/src/utils/schema-validator.ts +14 -7
- package/src/utils/to-hex-words.ts +3 -3
- package/src/utils/ui-logger-factory.ts +36 -16
- package/src/utils/ui-logger.ts +57 -29
- package/src/utils/bcd-to-number.ts +0 -10
- package/src/utils/number-to-bcd.ts +0 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
## <small>3.0.1 (2025-09-12)</small>
|
|
2
|
+
|
|
3
|
+
* Merge branch 'renovate/chai-5.x' into 'main' ([5e47735](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/commit/5e47735))
|
|
4
|
+
* Merge branch 'renovate/oxlint-1.x' into 'main' ([5c2dcc0](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/commit/5c2dcc0))
|
|
5
|
+
* Merge branch 'renovate/semantic-release-monorepo' into 'main' ([efbd8ed](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/commit/efbd8ed))
|
|
6
|
+
* Merge branch 'renovate/springfield-ham-radio-api-16.x' into 'main' ([9707285](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/commit/9707285))
|
|
7
|
+
* Merge branch 'renovate/yarn-monorepo' into 'main' ([a12a662](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/commit/a12a662))
|
|
8
|
+
* fix(deps): update dependency @springfield/ham-radio-api to ^16.1.4 ([275773f](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/commit/275773f))
|
|
9
|
+
* chore: better oxlint config ([2e3c6c2](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/commit/2e3c6c2))
|
|
10
|
+
* chore(deps): update dependencies in package.json and yarn.lock ([876fcce](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/commit/876fcce))
|
|
11
|
+
* chore(deps): update dependency @semantic-release/gitlab to v13.2.8 ([33d9e2a](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/commit/33d9e2a))
|
|
12
|
+
* chore(deps): update dependency chai to v5.3.3 ([aa14fcb](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/commit/aa14fcb))
|
|
13
|
+
* chore(deps): update dependency oxlint to v1.12.0 ([1bd7a3f](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/commit/1bd7a3f))
|
|
14
|
+
* chore(deps): update yarn to v4.9.4 ([de4403d](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/commit/de4403d))
|
|
15
|
+
|
|
16
|
+
## [3.0.0](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/compare/v2.2.2...v3.0.0) (2025-08-17)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### ⚠ BREAKING CHANGES
|
|
20
|
+
|
|
21
|
+
* update .oxlintrc.json and package.json for linting improvements and dependency upgrades
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* update .oxlintrc.json and package.json for linting improvements and dependency upgrades ([119f934](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/commit/119f934550846d86d034bce30d735463dcabe4eb))
|
|
26
|
+
|
|
1
27
|
## [2.2.2](https://gitlab.com/springfield-ham-radio/app/ham-radio-utils/compare/v2.2.1...v2.2.2) (2025-08-08)
|
|
2
28
|
|
|
3
29
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
export * from './memory/segmented-memory.js';
|
|
2
2
|
export * from './test-utils/radio-channel-factory.js';
|
|
3
3
|
export * from './test-utils/radio-programmed-channel-factory.js';
|
|
4
|
-
export * from './utils/bcd-to-number.js';
|
|
5
4
|
export * from './utils/band-plan.js';
|
|
6
5
|
export * from './utils/frequency-display.js';
|
|
7
6
|
export * from './utils/memory-channel-utils.js';
|
|
8
7
|
export * from './utils/memory-data-utils.js';
|
|
9
|
-
export * from './utils/number-to-bcd.js';
|
|
10
8
|
export * from './utils/schema-validator.js';
|
|
11
9
|
export * from './utils/to-hex-words.js';
|
|
12
10
|
export * from './utils/ui-logger.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAE7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AAEjE,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAE7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AAEjE,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAG7C,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,sCAAsC,CAAuB"}
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
export * from './memory/segmented-memory.js';
|
|
2
2
|
export * from './test-utils/radio-channel-factory.js';
|
|
3
3
|
export * from './test-utils/radio-programmed-channel-factory.js';
|
|
4
|
-
export * from './utils/bcd-to-number.js';
|
|
5
4
|
export * from './utils/band-plan.js';
|
|
6
5
|
export * from './utils/frequency-display.js';
|
|
7
6
|
export * from './utils/memory-channel-utils.js';
|
|
8
7
|
export * from './utils/memory-data-utils.js';
|
|
9
|
-
export * from './utils/number-to-bcd.js';
|
|
10
8
|
export * from './utils/schema-validator.js';
|
|
11
9
|
export * from './utils/to-hex-words.js';
|
|
12
10
|
export * from './utils/ui-logger.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAE7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AAEjE,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAE7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AAEjE,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAE7C,mCAAmC;AACnC,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,sCAAsC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC","sourcesContent":["export * from './memory/segmented-memory.js';\n\nexport * from './test-utils/radio-channel-factory.js';\nexport * from './test-utils/radio-programmed-channel-factory.js';\n\nexport * from './utils/band-plan.js';\nexport * from './utils/frequency-display.js';\nexport * from './utils/memory-channel-utils.js';\nexport * from './utils/memory-data-utils.js';\nexport * from './utils/schema-validator.js';\nexport * from './utils/to-hex-words.js';\nexport * from './utils/ui-logger.js';\nexport * from './utils/ui-logger-factory.js';\n\n// Export the radio protocol schema\nexport { default as radioProtocolSchema } from './schemas/radio-protocol-schema.json' with { type: 'json' };\n"]}
|
|
@@ -2,7 +2,7 @@ export class RadioSegmentedMemory {
|
|
|
2
2
|
segments;
|
|
3
3
|
constructor(segments) {
|
|
4
4
|
this.segments = new Map();
|
|
5
|
-
if (segments
|
|
5
|
+
if (segments) {
|
|
6
6
|
for (const segment of segments) {
|
|
7
7
|
this.segments.set(segment.startAddress, segment);
|
|
8
8
|
}
|
|
@@ -30,7 +30,7 @@ export class RadioSegmentedMemory {
|
|
|
30
30
|
}
|
|
31
31
|
result = iterator.next();
|
|
32
32
|
}
|
|
33
|
-
return
|
|
33
|
+
return {};
|
|
34
34
|
}
|
|
35
35
|
toArray() {
|
|
36
36
|
return [...this.segments.values()];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"segmented-memory.js","sourceRoot":"","sources":["../../src/memory/segmented-memory.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,oBAAoB;IACvB,QAAQ,CAAkC;IAElD,YAAY,QAA+B;QACzC,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC;QAE1B,IAAI,QAAQ,
|
|
1
|
+
{"version":3,"file":"segmented-memory.js","sourceRoot":"","sources":["../../src/memory/segmented-memory.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,oBAAoB;IACvB,QAAQ,CAAkC;IAElD,YAAY,QAA+B;QACzC,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC;QAE1B,IAAI,QAAQ,EAAE,CAAC;YACb,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;IACH,CAAC;IAEM,UAAU,CAAC,OAA2B;QAC3C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAEM,WAAW;QAChB,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;IAChC,CAAC;IAEM,UAAU,CAAC,YAAoB;QACpC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACzC,CAAC;IAEM,YAAY;QACjB,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC5B,CAAC;IAEM,WAAW,CAAC,OAAe;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QACxC,IAAI,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;QAE7B,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC;YAE7B,IAAI,OAAO,CAAC,YAAY,IAAI,OAAO,IAAI,OAAO,CAAC,UAAU,GAAG,OAAO,EAAE,CAAC;gBACpE,OAAO,OAAO,CAAC;YACjB,CAAC;YAED,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC3B,CAAC;QAED,OAAO,EAAwB,CAAC;IAClC,CAAC;IAEM,OAAO;QACZ,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACrC,CAAC;CACF","sourcesContent":["import type { RadioMemorySegment } from '@springfield/ham-radio-api';\n\nexport class RadioSegmentedMemory {\n private segments: Map<number, RadioMemorySegment>;\n\n constructor(segments?: RadioMemorySegment[]) {\n this.segments = new Map();\n\n if (segments) {\n for (const segment of segments) {\n this.segments.set(segment.startAddress, segment);\n }\n }\n }\n\n public addSegment(segment: RadioMemorySegment): void {\n this.segments.set(segment.startAddress, segment);\n }\n\n public getSegments(): IterableIterator<RadioMemorySegment> {\n return this.segments.values();\n }\n\n public getSegment(startAddress: number): RadioMemorySegment | undefined {\n return this.segments.get(startAddress);\n }\n\n public segmentCount(): number {\n return this.segments.size;\n }\n\n public findSegment(address: number): RadioMemorySegment | undefined {\n const iterator = this.segments.values();\n let result = iterator.next();\n\n while (!result.done) {\n const segment = result.value;\n\n if (segment.startAddress <= address && segment.endAddress > address) {\n return segment;\n }\n\n result = iterator.next();\n }\n\n return {} as RadioMemorySegment;\n }\n\n public toArray(): RadioMemorySegment[] {\n return [...this.segments.values()];\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-channel-utils.d.ts","sourceRoot":"","sources":["../../src/utils/memory-channel-utils.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa,GAAI,eAAe,MAAM,EAAE,MAAM,UAAU,KAAG,
|
|
1
|
+
{"version":3,"file":"memory-channel-utils.d.ts","sourceRoot":"","sources":["../../src/utils/memory-channel-utils.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa,GAAI,eAAe,MAAM,EAAE,MAAM,UAAU,KAAG,MAA8D,CAAC"}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { sprintf } from 'sprintf-js';
|
|
2
2
|
import { toHexWords } from './to-hex-words.js';
|
|
3
|
-
export const formatChannel = (channelNumber, data) =>
|
|
4
|
-
return sprintf('%2d : %s', channelNumber, toHexWords(data));
|
|
5
|
-
};
|
|
3
|
+
export const formatChannel = (channelNumber, data) => sprintf('%2d : %s', channelNumber, toHexWords(data));
|
|
6
4
|
//# sourceMappingURL=memory-channel-utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-channel-utils.js","sourceRoot":"","sources":["../../src/utils/memory-channel-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,aAAqB,EAAE,IAAgB,EAAU,EAAE
|
|
1
|
+
{"version":3,"file":"memory-channel-utils.js","sourceRoot":"","sources":["../../src/utils/memory-channel-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,aAAqB,EAAE,IAAgB,EAAU,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,aAAa,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC","sourcesContent":["import { sprintf } from 'sprintf-js';\nimport { toHexWords } from './to-hex-words.js';\n\nexport const formatChannel = (channelNumber: number, data: Uint8Array): string => sprintf('%2d : %s', channelNumber, toHexWords(data));\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-data-utils.d.ts","sourceRoot":"","sources":["../../src/utils/memory-data-utils.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,gBAAgB,GAAI,SAAS,MAAM,EAAE,MAAM,UAAU,KAAG,
|
|
1
|
+
{"version":3,"file":"memory-data-utils.d.ts","sourceRoot":"","sources":["../../src/utils/memory-data-utils.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,gBAAgB,GAAI,SAAS,MAAM,EAAE,MAAM,UAAU,KAAG,MAAyD,CAAC"}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { sprintf } from 'sprintf-js';
|
|
2
2
|
import { toHexWords } from './to-hex-words.js';
|
|
3
|
-
export const formatMemoryData = (address, data) =>
|
|
4
|
-
return sprintf('%04X : %s', address, toHexWords(data));
|
|
5
|
-
};
|
|
3
|
+
export const formatMemoryData = (address, data) => sprintf('%04X : %s', address, toHexWords(data));
|
|
6
4
|
//# sourceMappingURL=memory-data-utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-data-utils.js","sourceRoot":"","sources":["../../src/utils/memory-data-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAAe,EAAE,IAAgB,EAAU,EAAE
|
|
1
|
+
{"version":3,"file":"memory-data-utils.js","sourceRoot":"","sources":["../../src/utils/memory-data-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAAe,EAAE,IAAgB,EAAU,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC","sourcesContent":["import { sprintf } from 'sprintf-js';\nimport { toHexWords } from './to-hex-words.js';\n\nexport const formatMemoryData = (address: number, data: Uint8Array): string => sprintf('%04X : %s', address, toHexWords(data));\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-validator.d.ts","sourceRoot":"","sources":["../../src/utils/schema-validator.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;
|
|
1
|
+
{"version":3,"file":"schema-validator.d.ts","sourceRoot":"","sources":["../../src/utils/schema-validator.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAOD,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAM;;IAUjB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB;CAqBzD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Ajv from 'ajv';
|
|
1
|
+
import { Ajv } from 'ajv';
|
|
2
2
|
import addFormats from 'ajv-formats';
|
|
3
3
|
import radioProtocolSchema from '../schemas/radio-protocol-schema.json' with { type: 'json' };
|
|
4
4
|
export class SchemaValidator {
|
|
@@ -16,10 +16,13 @@ export class SchemaValidator {
|
|
|
16
16
|
if (valid) {
|
|
17
17
|
return { valid: true };
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
let errors = [];
|
|
20
|
+
if (validate.errors && validate.errors.length > 0) {
|
|
21
|
+
errors = validate.errors.map((error) => {
|
|
22
|
+
const path = error.instancePath || 'root';
|
|
23
|
+
return `${path}: ${error.message}`;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
23
26
|
return {
|
|
24
27
|
errors,
|
|
25
28
|
valid: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-validator.js","sourceRoot":"","sources":["../../src/utils/schema-validator.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"schema-validator.js","sourceRoot":"","sources":["../../src/utils/schema-validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,mBAAmB,MAAM,uCAAuC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAY9F,MAAM,OAAO,eAAe;IAClB,GAAG,CAAM;IAEjB;QACE,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC;YACjB,SAAS,EAAE,IAAI;YACf,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACF,UAA2C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,CAAC;IAED,qBAAqB,CAAC,MAAe;QACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;QAE/B,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACzB,CAAC;QAED,IAAI,MAAM,GAAa,EAAE,CAAC;QAC1B,IAAI,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClD,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAe,EAAE,EAAE;gBAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,IAAI,MAAM,CAAC;gBAC1C,OAAO,GAAG,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;YACrC,CAAC,CAAC,CAAC;QACL,CAAC;QAED,OAAO;YACL,MAAM;YACN,KAAK,EAAE,KAAK;SACb,CAAC;IACJ,CAAC;CACF","sourcesContent":["import { Ajv } from 'ajv';\nimport addFormats from 'ajv-formats';\nimport radioProtocolSchema from '../schemas/radio-protocol-schema.json' with { type: 'json' };\n\nexport interface ValidationResult {\n valid: boolean;\n errors?: string[];\n}\n\ninterface AjvError {\n instancePath?: string;\n message?: string;\n}\n\nexport class SchemaValidator {\n private ajv: Ajv;\n\n constructor() {\n this.ajv = new Ajv({\n allErrors: true,\n verbose: true,\n });\n (addFormats as unknown as (ajv: Ajv) => Ajv)(this.ajv);\n }\n\n validateRadioProtocol(config: unknown): ValidationResult {\n const validate = this.ajv.compile(radioProtocolSchema);\n const valid = validate(config);\n\n if (valid) {\n return { valid: true };\n }\n\n let errors: string[] = [];\n if (validate.errors && validate.errors.length > 0) {\n errors = validate.errors.map((error: AjvError) => {\n const path = error.instancePath || 'root';\n return `${path}: ${error.message}`;\n });\n }\n\n return {\n errors,\n valid: false,\n };\n }\n}\n"]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export const toHexWords = (data) => {
|
|
2
2
|
const hexData = Buffer.from(data).toString('hex');
|
|
3
3
|
let formattedData = '';
|
|
4
|
-
for (let
|
|
5
|
-
if (
|
|
4
|
+
for (let index = 0; index < hexData.length; index += 4) {
|
|
5
|
+
if (index !== 0) {
|
|
6
6
|
formattedData += ' ';
|
|
7
7
|
}
|
|
8
|
-
formattedData += hexData.
|
|
8
|
+
formattedData += hexData.slice(index, index + 4);
|
|
9
9
|
}
|
|
10
10
|
return formattedData;
|
|
11
11
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"to-hex-words.js","sourceRoot":"","sources":["../../src/utils/to-hex-words.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAgB,EAAU,EAAE;IACrD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,aAAa,GAAG,EAAE,CAAC;IAEvB,KAAK,IAAI,
|
|
1
|
+
{"version":3,"file":"to-hex-words.js","sourceRoot":"","sources":["../../src/utils/to-hex-words.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAgB,EAAU,EAAE;IACrD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,aAAa,GAAG,EAAE,CAAC;IAEvB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACvD,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,aAAa,IAAI,GAAG,CAAC;QACvB,CAAC;QAED,aAAa,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;IACnD,CAAC;IAED,OAAO,aAAa,CAAC;AACvB,CAAC,CAAC","sourcesContent":["export const toHexWords = (data: Uint8Array): string => {\n const hexData = Buffer.from(data).toString('hex');\n let formattedData = '';\n\n for (let index = 0; index < hexData.length; index += 4) {\n if (index !== 0) {\n formattedData += ' ';\n }\n\n formattedData += hexData.slice(index, index + 4);\n }\n\n return formattedData;\n};\n"]}
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
import type { LogLayerTransport } from '@loglayer/shared';
|
|
1
2
|
import { UILogger } from './ui-logger.js';
|
|
3
|
+
interface LogEntry {
|
|
4
|
+
data: unknown;
|
|
5
|
+
hasData: boolean;
|
|
6
|
+
level: string;
|
|
7
|
+
message: string;
|
|
8
|
+
timestamp: string;
|
|
9
|
+
}
|
|
2
10
|
export interface UILoggerConfig {
|
|
3
|
-
customTransport?:
|
|
11
|
+
customTransport?: LogLayerTransport;
|
|
4
12
|
enableConsoleDebug?: boolean;
|
|
5
13
|
}
|
|
6
14
|
export declare const createUILogger: (config?: UILoggerConfig) => UILogger;
|
|
7
|
-
export declare const createUILoggerWithCallback: (callback: (logEntry:
|
|
15
|
+
export declare const createUILoggerWithCallback: (callback: (logEntry: LogEntry) => void, config?: UILoggerConfig) => UILogger;
|
|
16
|
+
export {};
|
|
8
17
|
//# sourceMappingURL=ui-logger-factory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui-logger-factory.d.ts","sourceRoot":"","sources":["../../src/utils/ui-logger-factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ui-logger-factory.d.ts","sourceRoot":"","sources":["../../src/utils/ui-logger-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAI1D,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,UAAU,QAAQ;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAmCD,MAAM,WAAW,cAAc;IAC7B,eAAe,CAAC,EAAE,iBAAiB,CAAC;IACpC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAQD,eAAO,MAAM,cAAc,GAA2B,SAAQ,cAAmB,KAAG,QAQnF,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAuC,UAAU,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,EAAE,SAAQ,cAAmB,KAAG,QAQnJ,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// eslint-disable-next-line no-duplicate-imports, sort-imports
|
|
1
2
|
import { BaseTransport } from '@loglayer/transport';
|
|
2
3
|
import { LogLayer } from 'loglayer';
|
|
3
4
|
import { UILogger } from './ui-logger.js';
|
|
@@ -5,29 +6,38 @@ import { UILogger } from './ui-logger.js';
|
|
|
5
6
|
* Custom LogLayer transport for capturing UI log entries.
|
|
6
7
|
*/
|
|
7
8
|
const UILogTransport = class extends BaseTransport {
|
|
8
|
-
|
|
9
|
+
callback;
|
|
9
10
|
constructor(callback) {
|
|
10
11
|
// Pass a dummy logger to BaseTransport
|
|
11
|
-
super({
|
|
12
|
-
|
|
12
|
+
super({
|
|
13
|
+
enabled: true,
|
|
14
|
+
id: 'ui-logger-transport',
|
|
15
|
+
logger: {
|
|
16
|
+
log: () => {
|
|
17
|
+
// Empty function for dummy logger
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
this.callback = callback;
|
|
13
22
|
}
|
|
14
23
|
shipToLogger(params) {
|
|
15
24
|
const logEntry = {
|
|
16
25
|
data: params.data,
|
|
17
|
-
hasData: params.hasData,
|
|
26
|
+
hasData: params.hasData ?? false,
|
|
18
27
|
level: params.logLevel,
|
|
19
28
|
message: params.messages.join(' '),
|
|
20
29
|
timestamp: new Date().toISOString(),
|
|
21
30
|
};
|
|
22
|
-
this
|
|
31
|
+
this.callback(logEntry);
|
|
23
32
|
return [];
|
|
24
33
|
}
|
|
25
34
|
};
|
|
35
|
+
// Move defaultCallback to outer scope to avoid recreating it on every call
|
|
36
|
+
const defaultCallback = (logEntry) => {
|
|
37
|
+
// eslint-disable-next-line no-console
|
|
38
|
+
console.log(JSON.stringify(logEntry));
|
|
39
|
+
};
|
|
26
40
|
export const createUILogger = function createUILogger(config = {}) {
|
|
27
|
-
// By default, just print to console as JSON
|
|
28
|
-
const defaultCallback = (logEntry) => {
|
|
29
|
-
console.log(JSON.stringify(logEntry));
|
|
30
|
-
};
|
|
31
41
|
const transport = config.customTransport || new UILogTransport(defaultCallback);
|
|
32
42
|
const logger = new LogLayer({
|
|
33
43
|
consoleDebug: config.enableConsoleDebug ?? false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui-logger-factory.js","sourceRoot":"","sources":["../../src/utils/ui-logger-factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ui-logger-factory.js","sourceRoot":"","sources":["../../src/utils/ui-logger-factory.ts"],"names":[],"mappings":"AACA,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAgC,MAAM,qBAAqB,CAAC;AAClF,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAU1C;;GAEG;AACH,MAAM,cAAc,GAAG,KAAM,SAAQ,aAAsB;IACjD,QAAQ,CAA+B;IAE/C,YAAY,QAAsC;QAChD,uCAAuC;QACvC,KAAK,CAAC;YACJ,OAAO,EAAE,IAAI;YACb,EAAE,EAAE,qBAAqB;YACzB,MAAM,EAAE;gBACN,GAAG,EAAE,GAAS,EAAE;oBACd,kCAAkC;gBACpC,CAAC;aACF;SACF,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,YAAY,CAAC,MAA+B;QAC1C,MAAM,QAAQ,GAAa;YACzB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,KAAK;YAChC,KAAK,EAAE,MAAM,CAAC,QAAQ;YACtB,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;YAClC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACxB,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC;AAOF,2EAA2E;AAC3E,MAAM,eAAe,GAAG,CAAC,QAAkB,EAAQ,EAAE;IACnD,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,cAAc,CAAC,SAAyB,EAAE;IAC/E,MAAM,SAAS,GAAG,MAAM,CAAC,eAAe,IAAI,IAAI,cAAc,CAAC,eAAe,CAAC,CAAC;IAChF,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC;QAC1B,YAAY,EAAE,MAAM,CAAC,kBAAkB,IAAI,KAAK;QAChD,SAAS;KACV,CAAC,CAAC;IACH,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACzB,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,0BAA0B,GAAG,SAAS,0BAA0B,CAAC,QAAsC,EAAE,SAAyB,EAAE;IAC/I,MAAM,SAAS,GAAG,IAAI,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC;QAC1B,YAAY,EAAE,MAAM,CAAC,kBAAkB,IAAI,KAAK;QAChD,SAAS;KACV,CAAC,CAAC;IACH,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACzB,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC,CAAC","sourcesContent":["import type { LogLayerTransport } from '@loglayer/shared';\n// eslint-disable-next-line no-duplicate-imports, sort-imports\nimport { BaseTransport, type LogLayerTransportParams } from '@loglayer/transport';\nimport { LogLayer } from 'loglayer';\nimport { UILogger } from './ui-logger.js';\n\ninterface LogEntry {\n data: unknown;\n hasData: boolean;\n level: string;\n message: string;\n timestamp: string;\n}\n\n/**\n * Custom LogLayer transport for capturing UI log entries.\n */\nconst UILogTransport = class extends BaseTransport<unknown> {\n private callback: (logEntry: LogEntry) => void;\n\n constructor(callback: (logEntry: LogEntry) => void) {\n // Pass a dummy logger to BaseTransport\n super({\n enabled: true,\n id: 'ui-logger-transport',\n logger: {\n log: (): void => {\n // Empty function for dummy logger\n },\n },\n });\n this.callback = callback;\n }\n\n shipToLogger(params: LogLayerTransportParams): unknown[] {\n const logEntry: LogEntry = {\n data: params.data,\n hasData: params.hasData ?? false,\n level: params.logLevel,\n message: params.messages.join(' '),\n timestamp: new Date().toISOString(),\n };\n this.callback(logEntry);\n return [];\n }\n};\n\nexport interface UILoggerConfig {\n customTransport?: LogLayerTransport;\n enableConsoleDebug?: boolean;\n}\n\n// Move defaultCallback to outer scope to avoid recreating it on every call\nconst defaultCallback = (logEntry: LogEntry): void => {\n // eslint-disable-next-line no-console\n console.log(JSON.stringify(logEntry));\n};\n\nexport const createUILogger = function createUILogger(config: UILoggerConfig = {}): UILogger {\n const transport = config.customTransport || new UILogTransport(defaultCallback);\n const logger = new LogLayer({\n consoleDebug: config.enableConsoleDebug ?? false,\n transport,\n });\n logger.setLevel('debug');\n return new UILogger(logger);\n};\n\nexport const createUILoggerWithCallback = function createUILoggerWithCallback(callback: (logEntry: LogEntry) => void, config: UILoggerConfig = {}): UILogger {\n const transport = new UILogTransport(callback);\n const logger = new LogLayer({\n consoleDebug: config.enableConsoleDebug ?? false,\n transport,\n });\n logger.setLevel('debug');\n return new UILogger(logger);\n};\n"]}
|
|
@@ -20,12 +20,13 @@ import type { ILogLayer } from 'loglayer';
|
|
|
20
20
|
* - Structured data supports rich UI representations
|
|
21
21
|
* - Separate from debug logging avoids UI noise
|
|
22
22
|
*/
|
|
23
|
+
interface ProtocolContext {
|
|
24
|
+
variables?: Map<string, unknown>;
|
|
25
|
+
}
|
|
23
26
|
/**
|
|
24
27
|
* Generic protocol step interface for UI logging
|
|
25
28
|
*/
|
|
26
|
-
export
|
|
27
|
-
[key: string]: any;
|
|
28
|
-
}
|
|
29
|
+
export type UIProtocolStep = Record<string, unknown>;
|
|
29
30
|
/**
|
|
30
31
|
* UI Logger for capturing command-level information for UI display
|
|
31
32
|
*/
|
|
@@ -34,10 +35,11 @@ export declare class UILogger {
|
|
|
34
35
|
private commandStartTimes;
|
|
35
36
|
constructor(logger: ILogLayer);
|
|
36
37
|
startCommand(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep): void;
|
|
37
|
-
logCommandSuccess(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, context:
|
|
38
|
-
logCommandFailure(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, error: Error, context:
|
|
38
|
+
logCommandSuccess(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, context: ProtocolContext): void;
|
|
39
|
+
logCommandFailure(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, error: Error, context: ProtocolContext): void;
|
|
39
40
|
private getCommandType;
|
|
40
41
|
private getStepDescription;
|
|
41
42
|
private getExpectedData;
|
|
42
43
|
}
|
|
44
|
+
export {};
|
|
43
45
|
//# sourceMappingURL=ui-logger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui-logger.d.ts","sourceRoot":"","sources":["../../src/utils/ui-logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;GAEG;AACH,MAAM,
|
|
1
|
+
{"version":3,"file":"ui-logger.d.ts","sourceRoot":"","sources":["../../src/utils/ui-logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,UAAU,eAAe;IACvB,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA0BrD;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,iBAAiB,CAA6B;gBAE1C,MAAM,EAAE,SAAS;IAK7B,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI;IAsBlG,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI;IA2CjI,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI;IAmC/I,OAAO,CAAC,cAAc;IAsBtB,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,eAAe;CAoBxB"}
|
package/dist/utils/ui-logger.js
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guards for protocol step properties
|
|
3
|
+
*/
|
|
4
|
+
const hasDescription = (obj) => typeof obj === 'object' && obj !== null && 'description' in obj && typeof obj.description === 'string';
|
|
5
|
+
const hasReceive = (obj) => typeof obj === 'object' && obj !== null && 'receive' in obj;
|
|
6
|
+
const hasEndChunk = (obj) => typeof obj === 'object' &&
|
|
7
|
+
obj !== null &&
|
|
8
|
+
'endChunk' in obj &&
|
|
9
|
+
typeof obj.endChunk === 'object' &&
|
|
10
|
+
obj.endChunk !== null &&
|
|
11
|
+
'receive' in obj.endChunk;
|
|
12
|
+
const hasStartChunk = (obj) => typeof obj === 'object' &&
|
|
13
|
+
obj !== null &&
|
|
14
|
+
'startChunk' in obj &&
|
|
15
|
+
typeof obj.startChunk === 'object' &&
|
|
16
|
+
obj.startChunk !== null &&
|
|
17
|
+
'receive' in obj.startChunk;
|
|
1
18
|
/**
|
|
2
19
|
* UI Logger for capturing command-level information for UI display
|
|
3
20
|
*/
|
|
@@ -35,18 +52,16 @@ export class UILogger {
|
|
|
35
52
|
const commandType = this.getCommandType(step);
|
|
36
53
|
const description = this.getStepDescription(step);
|
|
37
54
|
// Extract sent and received data from context
|
|
38
|
-
const dataSent = context.variables
|
|
39
|
-
const dataReceived = context.variables
|
|
55
|
+
const dataSent = context.variables && context.variables.get('lastSentData');
|
|
56
|
+
const dataReceived = context.variables && context.variables.get('lastReceivedData');
|
|
40
57
|
const dataExpected = this.getExpectedData(step);
|
|
41
58
|
// For readSegment, also extract chunk logs if present
|
|
42
|
-
|
|
43
|
-
if ('readSegment' === commandType) {
|
|
44
|
-
dataChunks = context.variables?.get('lastReadSegmentChunks');
|
|
45
|
-
// For readSegment, we don't need the flattened dataSent/dataReceived since we have dataChunks
|
|
46
|
-
}
|
|
59
|
+
const dataChunks = commandType === 'readSegment' && context.variables && context.variables.get('lastReadSegmentChunks');
|
|
47
60
|
// Convert dataSent and dataReceived to byte array format if they exist
|
|
48
|
-
|
|
49
|
-
const
|
|
61
|
+
// eslint-disable-next-line unicorn/prefer-spread
|
|
62
|
+
const dataSentArray = dataSent && Array.from(dataSent);
|
|
63
|
+
// eslint-disable-next-line unicorn/prefer-spread
|
|
64
|
+
const dataReceivedArray = dataReceived && Array.from(dataReceived);
|
|
50
65
|
this.logger
|
|
51
66
|
.withMetadata({
|
|
52
67
|
commandType,
|
|
@@ -74,10 +89,11 @@ export class UILogger {
|
|
|
74
89
|
const commandType = this.getCommandType(step);
|
|
75
90
|
const description = this.getStepDescription(step);
|
|
76
91
|
// Extract sent data from context
|
|
77
|
-
const dataSent = context.variables
|
|
92
|
+
const dataSent = context.variables && context.variables.get('lastSentData');
|
|
78
93
|
const dataExpected = this.getExpectedData(step);
|
|
79
94
|
// Convert dataSent to byte array format if it exists
|
|
80
|
-
|
|
95
|
+
// eslint-disable-next-line unicorn/prefer-spread
|
|
96
|
+
const dataSentArray = dataSent && Array.from(dataSent);
|
|
81
97
|
this.logger
|
|
82
98
|
.withMetadata({
|
|
83
99
|
commandType,
|
|
@@ -117,25 +133,25 @@ export class UILogger {
|
|
|
117
133
|
return 'unknown';
|
|
118
134
|
}
|
|
119
135
|
getStepDescription(step) {
|
|
120
|
-
if ('sendReceive' in step && step.sendReceive
|
|
136
|
+
if ('sendReceive' in step && hasDescription(step.sendReceive)) {
|
|
121
137
|
return step.sendReceive.description;
|
|
122
138
|
}
|
|
123
|
-
if ('send' in step && step.send
|
|
139
|
+
if ('send' in step && hasDescription(step.send)) {
|
|
124
140
|
return step.send.description;
|
|
125
141
|
}
|
|
126
|
-
if ('receive' in step && step.receive
|
|
142
|
+
if ('receive' in step && hasDescription(step.receive)) {
|
|
127
143
|
return step.receive.description;
|
|
128
144
|
}
|
|
129
|
-
if ('readSegment' in step && step.readSegment
|
|
145
|
+
if ('readSegment' in step && hasDescription(step.readSegment)) {
|
|
130
146
|
return step.readSegment.description;
|
|
131
147
|
}
|
|
132
|
-
if ('writeSegment' in step && step.writeSegment
|
|
148
|
+
if ('writeSegment' in step && hasDescription(step.writeSegment)) {
|
|
133
149
|
return step.writeSegment.description;
|
|
134
150
|
}
|
|
135
151
|
return 'No description';
|
|
136
152
|
}
|
|
137
153
|
getExpectedData(step) {
|
|
138
|
-
if ('sendReceive' in step && step.sendReceive
|
|
154
|
+
if ('sendReceive' in step && hasReceive(step.sendReceive)) {
|
|
139
155
|
return step.sendReceive.receive;
|
|
140
156
|
}
|
|
141
157
|
if ('receive' in step) {
|
|
@@ -143,13 +159,16 @@ export class UILogger {
|
|
|
143
159
|
}
|
|
144
160
|
if ('readSegment' in step) {
|
|
145
161
|
// For readSegment, return both start and end chunk receive patterns
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
162
|
+
const { readSegment } = step;
|
|
163
|
+
if (hasEndChunk(readSegment) && hasStartChunk(readSegment)) {
|
|
164
|
+
return {
|
|
165
|
+
endChunk: readSegment.endChunk.receive,
|
|
166
|
+
startChunk: readSegment.startChunk.receive,
|
|
167
|
+
type: 'readSegment',
|
|
168
|
+
};
|
|
169
|
+
}
|
|
151
170
|
}
|
|
152
|
-
return
|
|
171
|
+
return {};
|
|
153
172
|
}
|
|
154
173
|
}
|
|
155
174
|
//# sourceMappingURL=ui-logger.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui-logger.js","sourceRoot":"","sources":["../../src/utils/ui-logger.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ILogLayer } from 'loglayer';\n\n/**\n * UI Logger: Command-Level Logging for UI Display\n *\n * This module provides a specialized logger for capturing command-level information\n * that can be easily parsed and displayed in a UI. It captures details about\n * commands, data sent, data expected, and data received in a structured format.\n *\n * Purpose:\n * - Captures command-level information for UI display\n * - Provides structured JSON logging for easy parsing\n * - Tracks command execution details including timing\n * - Supports protocol debugging in UI environments\n * - Maintains separation from debug logging\n *\n * Design Rationale:\n * - Single log entry per command provides clean UI display\n * - JSON structure enables easy parsing and filtering\n * - Command-level granularity is appropriate for UI debugging\n * - Structured data supports rich UI representations\n * - Separate from debug logging avoids UI noise\n */\n\n/**\n * Generic protocol step interface for UI logging\n */\nexport interface UIProtocolStep {\n [key: string]: any;\n}\n\n/**\n * UI Logger for capturing command-level information for UI display\n */\nexport class UILogger {\n private logger: ILogLayer;\n private commandStartTimes = new Map<string, number>();\n\n constructor(logger: ILogLayer) {\n this.logger = logger;\n }\n\n // eslint-disable-next-line max-params\n startCommand(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep): void {\n const commandId = `${operation}-${stepIndex}`;\n const startTime = Date.now();\n this.commandStartTimes.set(commandId, startTime);\n\n const commandType = this.getCommandType(step);\n const description = this.getStepDescription(step);\n\n this.logger\n .withMetadata({\n commandId,\n commandType,\n description,\n operation,\n startTime,\n stepIndex,\n totalSteps,\n })\n .info('Command started');\n }\n\n // eslint-disable-next-line max-params\n logCommandSuccess(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, context: any): void {\n const commandId = `${operation}-${stepIndex}`;\n const startTime = this.commandStartTimes.get(commandId) || Date.now();\n const endTime = Date.now();\n const duration = endTime - startTime;\n\n const commandType = this.getCommandType(step);\n const description = this.getStepDescription(step);\n\n // Extract sent and received data from context\n const dataSent = context.variables?.get('lastSentData');\n const dataReceived = context.variables?.get('lastReceivedData');\n const dataExpected = this.getExpectedData(step);\n\n // For readSegment, also extract chunk logs if present\n let dataChunks = undefined;\n if ('readSegment' === commandType) {\n dataChunks = context.variables?.get('lastReadSegmentChunks');\n // For readSegment, we don't need the flattened dataSent/dataReceived since we have dataChunks\n }\n\n // Convert dataSent and dataReceived to byte array format if they exist\n const dataSentArray = dataSent ? [...dataSent] : undefined;\n const dataReceivedArray = dataReceived ? [...dataReceived] : undefined;\n\n this.logger\n .withMetadata({\n commandType,\n dataChunks,\n dataExpected,\n dataReceived: dataReceivedArray,\n dataSent: dataSentArray,\n description,\n duration,\n endTime,\n operation,\n startTime,\n stepIndex,\n success: true,\n totalSteps,\n })\n .info('Command completed successfully');\n }\n\n // eslint-disable-next-line max-params\n logCommandFailure(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, error: Error, context: any): void {\n const commandId = `${operation}-${stepIndex}`;\n const startTime = this.commandStartTimes.get(commandId) || Date.now();\n const endTime = Date.now();\n const duration = endTime - startTime;\n\n const commandType = this.getCommandType(step);\n const description = this.getStepDescription(step);\n\n // Extract sent data from context\n const dataSent = context.variables?.get('lastSentData');\n const dataExpected = this.getExpectedData(step);\n\n // Convert dataSent to byte array format if it exists\n const dataSentArray = dataSent ? [...dataSent] : undefined;\n\n this.logger\n .withMetadata({\n commandType,\n dataExpected,\n dataSent: dataSentArray,\n description,\n duration,\n endTime,\n error: error.message,\n operation,\n startTime,\n stepIndex,\n success: false,\n totalSteps,\n })\n .info('Command failed');\n }\n\n private getCommandType(step: UIProtocolStep): string {\n if ('sendReceive' in step) {\n return 'sendReceive';\n }\n if ('send' in step) {\n return 'send';\n }\n if ('receive' in step) {\n return 'receive';\n }\n if ('readSegment' in step) {\n return 'readSegment';\n }\n if ('writeSegment' in step) {\n return 'writeSegment';\n }\n if ('setVariable' in step) {\n return 'setVariable';\n }\n return 'unknown';\n }\n\n private getStepDescription(step: UIProtocolStep): string {\n if ('sendReceive' in step && step.sendReceive.description) {\n return step.sendReceive.description;\n }\n if ('send' in step && step.send.description) {\n return step.send.description;\n }\n if ('receive' in step && step.receive.description) {\n return step.receive.description;\n }\n if ('readSegment' in step && step.readSegment.description) {\n return step.readSegment.description;\n }\n if ('writeSegment' in step && step.writeSegment.description) {\n return step.writeSegment.description;\n }\n return 'No description';\n }\n\n private getExpectedData(step: UIProtocolStep): any {\n if ('sendReceive' in step && step.sendReceive.receive) {\n return step.sendReceive.receive;\n }\n if ('receive' in step) {\n return step.receive;\n }\n if ('readSegment' in step) {\n // For readSegment, return both start and end chunk receive patterns\n return {\n endChunk: step.readSegment.endChunk.receive,\n startChunk: step.readSegment.startChunk.receive,\n type: 'readSegment',\n };\n }\n return undefined;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ui-logger.js","sourceRoot":"","sources":["../../src/utils/ui-logger.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ILogLayer } from 'loglayer';\n\n/**\n * UI Logger: Command-Level Logging for UI Display\n *\n * This module provides a specialized logger for capturing command-level information\n * that can be easily parsed and displayed in a UI. It captures details about\n * commands, data sent, data expected, and data received in a structured format.\n *\n * Purpose:\n * - Captures command-level information for UI display\n * - Provides structured JSON logging for easy parsing\n * - Tracks command execution details including timing\n * - Supports protocol debugging in UI environments\n * - Maintains separation from debug logging\n *\n * Design Rationale:\n * - Single log entry per command provides clean UI display\n * - JSON structure enables easy parsing and filtering\n * - Command-level granularity is appropriate for UI debugging\n * - Structured data supports rich UI representations\n * - Separate from debug logging avoids UI noise\n */\n\ninterface ProtocolContext {\n variables?: Map<string, unknown>;\n}\n\n/**\n * Generic protocol step interface for UI logging\n */\nexport type UIProtocolStep = Record<string, unknown>;\n\n/**\n * Type guards for protocol step properties\n */\nconst hasDescription = (obj: unknown): obj is { description: string } =>\n typeof obj === 'object' && obj !== null && 'description' in obj && typeof (obj as Record<string, unknown>).description === 'string';\n\nconst hasReceive = (obj: unknown): obj is { receive: unknown } => typeof obj === 'object' && obj !== null && 'receive' in obj;\n\nconst hasEndChunk = (obj: unknown): obj is { endChunk: { receive: unknown } } =>\n typeof obj === 'object' &&\n obj !== null &&\n 'endChunk' in obj &&\n typeof (obj as Record<string, unknown>).endChunk === 'object' &&\n (obj as Record<string, unknown>).endChunk !== null &&\n 'receive' in ((obj as Record<string, unknown>).endChunk as Record<string, unknown>);\n\nconst hasStartChunk = (obj: unknown): obj is { startChunk: { receive: unknown } } =>\n typeof obj === 'object' &&\n obj !== null &&\n 'startChunk' in obj &&\n typeof (obj as Record<string, unknown>).startChunk === 'object' &&\n (obj as Record<string, unknown>).startChunk !== null &&\n 'receive' in ((obj as Record<string, unknown>).startChunk as Record<string, unknown>);\n\n/**\n * UI Logger for capturing command-level information for UI display\n */\nexport class UILogger {\n private logger: ILogLayer;\n private commandStartTimes = new Map<string, number>();\n\n constructor(logger: ILogLayer) {\n this.logger = logger;\n }\n\n // eslint-disable-next-line max-params\n startCommand(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep): void {\n const commandId = `${operation}-${stepIndex}`;\n const startTime = Date.now();\n this.commandStartTimes.set(commandId, startTime);\n\n const commandType = this.getCommandType(step);\n const description = this.getStepDescription(step);\n\n this.logger\n .withMetadata({\n commandId,\n commandType,\n description,\n operation,\n startTime,\n stepIndex,\n totalSteps,\n })\n .info('Command started');\n }\n\n // eslint-disable-next-line max-params\n logCommandSuccess(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, context: ProtocolContext): void {\n const commandId = `${operation}-${stepIndex}`;\n const startTime = this.commandStartTimes.get(commandId) || Date.now();\n const endTime = Date.now();\n const duration = endTime - startTime;\n\n const commandType = this.getCommandType(step);\n const description = this.getStepDescription(step);\n\n // Extract sent and received data from context\n const dataSent = context.variables && context.variables.get('lastSentData');\n const dataReceived = context.variables && context.variables.get('lastReceivedData');\n const dataExpected = this.getExpectedData(step);\n\n // For readSegment, also extract chunk logs if present\n const dataChunks = commandType === 'readSegment' && context.variables && context.variables.get('lastReadSegmentChunks');\n\n // Convert dataSent and dataReceived to byte array format if they exist\n // eslint-disable-next-line unicorn/prefer-spread\n const dataSentArray = dataSent && Array.from(dataSent as Uint8Array);\n // eslint-disable-next-line unicorn/prefer-spread\n const dataReceivedArray = dataReceived && Array.from(dataReceived as Uint8Array);\n\n this.logger\n .withMetadata({\n commandType,\n dataChunks,\n dataExpected,\n dataReceived: dataReceivedArray,\n dataSent: dataSentArray,\n description,\n duration,\n endTime,\n operation,\n startTime,\n stepIndex,\n success: true,\n totalSteps,\n })\n .info('Command completed successfully');\n }\n\n // eslint-disable-next-line max-params\n logCommandFailure(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, error: Error, context: ProtocolContext): void {\n const commandId = `${operation}-${stepIndex}`;\n const startTime = this.commandStartTimes.get(commandId) || Date.now();\n const endTime = Date.now();\n const duration = endTime - startTime;\n\n const commandType = this.getCommandType(step);\n const description = this.getStepDescription(step);\n\n // Extract sent data from context\n const dataSent = context.variables && context.variables.get('lastSentData');\n const dataExpected = this.getExpectedData(step);\n\n // Convert dataSent to byte array format if it exists\n // eslint-disable-next-line unicorn/prefer-spread\n const dataSentArray = dataSent && Array.from(dataSent as Uint8Array);\n\n this.logger\n .withMetadata({\n commandType,\n dataExpected,\n dataSent: dataSentArray,\n description,\n duration,\n endTime,\n error: error.message,\n operation,\n startTime,\n stepIndex,\n success: false,\n totalSteps,\n })\n .info('Command failed');\n }\n\n private getCommandType(step: UIProtocolStep): string {\n if ('sendReceive' in step) {\n return 'sendReceive';\n }\n if ('send' in step) {\n return 'send';\n }\n if ('receive' in step) {\n return 'receive';\n }\n if ('readSegment' in step) {\n return 'readSegment';\n }\n if ('writeSegment' in step) {\n return 'writeSegment';\n }\n if ('setVariable' in step) {\n return 'setVariable';\n }\n return 'unknown';\n }\n\n private getStepDescription(step: UIProtocolStep): string {\n if ('sendReceive' in step && hasDescription(step.sendReceive)) {\n return step.sendReceive.description;\n }\n if ('send' in step && hasDescription(step.send)) {\n return step.send.description;\n }\n if ('receive' in step && hasDescription(step.receive)) {\n return step.receive.description;\n }\n if ('readSegment' in step && hasDescription(step.readSegment)) {\n return step.readSegment.description;\n }\n if ('writeSegment' in step && hasDescription(step.writeSegment)) {\n return step.writeSegment.description;\n }\n return 'No description';\n }\n\n private getExpectedData(step: UIProtocolStep): unknown {\n if ('sendReceive' in step && hasReceive(step.sendReceive)) {\n return step.sendReceive.receive;\n }\n if ('receive' in step) {\n return step.receive;\n }\n if ('readSegment' in step) {\n // For readSegment, return both start and end chunk receive patterns\n const { readSegment } = step;\n if (hasEndChunk(readSegment) && hasStartChunk(readSegment)) {\n return {\n endChunk: readSegment.endChunk.receive,\n startChunk: readSegment.startChunk.receive,\n type: 'readSegment',\n };\n }\n }\n return {};\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@springfield/ham-radio-utils",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"author": "Bryan Hunt",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"packageManager": "yarn@4.9.
|
|
6
|
+
"packageManager": "yarn@4.9.4",
|
|
7
7
|
"readme": "README.md",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "dist/index.js",
|
|
@@ -51,11 +51,11 @@
|
|
|
51
51
|
"access": "public"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@springfield/ham-radio-api": "^16.1.
|
|
54
|
+
"@springfield/ham-radio-api": "^16.1.4",
|
|
55
55
|
"ajv": "^8.17.1",
|
|
56
56
|
"ajv-formats": "^3.0.1",
|
|
57
57
|
"fishery": "^2.3.1",
|
|
58
|
-
"loglayer": "^6.
|
|
58
|
+
"loglayer": "^6.7.0",
|
|
59
59
|
"sprintf-js": "^1.1.3"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
@@ -63,13 +63,13 @@
|
|
|
63
63
|
"@commitlint/config-conventional": "19.8.1",
|
|
64
64
|
"@semantic-release/changelog": "6.0.3",
|
|
65
65
|
"@semantic-release/git": "10.0.1",
|
|
66
|
-
"@semantic-release/gitlab": "13.2.
|
|
66
|
+
"@semantic-release/gitlab": "13.2.8",
|
|
67
67
|
"@types/chai": "5.2.2",
|
|
68
68
|
"@types/sprintf-js": "1.1.4",
|
|
69
|
-
"chai": "
|
|
69
|
+
"chai": "6.0.1",
|
|
70
70
|
"husky": "9.1.7",
|
|
71
|
-
"oxlint": "1.
|
|
72
|
-
"semantic-release": "
|
|
71
|
+
"oxlint": "1.15.0",
|
|
72
|
+
"semantic-release": "24.2.8",
|
|
73
73
|
"semantic-release-yarn": "3.0.2",
|
|
74
74
|
"typescript": "5.9.2"
|
|
75
75
|
}
|
package/src/index.ts
CHANGED
|
@@ -3,12 +3,10 @@ export * from './memory/segmented-memory.js';
|
|
|
3
3
|
export * from './test-utils/radio-channel-factory.js';
|
|
4
4
|
export * from './test-utils/radio-programmed-channel-factory.js';
|
|
5
5
|
|
|
6
|
-
export * from './utils/bcd-to-number.js';
|
|
7
6
|
export * from './utils/band-plan.js';
|
|
8
7
|
export * from './utils/frequency-display.js';
|
|
9
8
|
export * from './utils/memory-channel-utils.js';
|
|
10
9
|
export * from './utils/memory-data-utils.js';
|
|
11
|
-
export * from './utils/number-to-bcd.js';
|
|
12
10
|
export * from './utils/schema-validator.js';
|
|
13
11
|
export * from './utils/to-hex-words.js';
|
|
14
12
|
export * from './utils/ui-logger.js';
|
|
@@ -6,7 +6,7 @@ export class RadioSegmentedMemory {
|
|
|
6
6
|
constructor(segments?: RadioMemorySegment[]) {
|
|
7
7
|
this.segments = new Map();
|
|
8
8
|
|
|
9
|
-
if (segments
|
|
9
|
+
if (segments) {
|
|
10
10
|
for (const segment of segments) {
|
|
11
11
|
this.segments.set(segment.startAddress, segment);
|
|
12
12
|
}
|
|
@@ -43,7 +43,7 @@ export class RadioSegmentedMemory {
|
|
|
43
43
|
result = iterator.next();
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
return
|
|
46
|
+
return {} as RadioMemorySegment;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
public toArray(): RadioMemorySegment[] {
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { sprintf } from 'sprintf-js';
|
|
2
2
|
import { toHexWords } from './to-hex-words.js';
|
|
3
3
|
|
|
4
|
-
export const formatChannel = (channelNumber: number, data: Uint8Array): string =>
|
|
5
|
-
return sprintf('%2d : %s', channelNumber, toHexWords(data));
|
|
6
|
-
};
|
|
4
|
+
export const formatChannel = (channelNumber: number, data: Uint8Array): string => sprintf('%2d : %s', channelNumber, toHexWords(data));
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { sprintf } from 'sprintf-js';
|
|
2
2
|
import { toHexWords } from './to-hex-words.js';
|
|
3
3
|
|
|
4
|
-
export const formatMemoryData = (address: number, data: Uint8Array): string =>
|
|
5
|
-
return sprintf('%04X : %s', address, toHexWords(data));
|
|
6
|
-
};
|
|
4
|
+
export const formatMemoryData = (address: number, data: Uint8Array): string => sprintf('%04X : %s', address, toHexWords(data));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Ajv from 'ajv';
|
|
1
|
+
import { Ajv } from 'ajv';
|
|
2
2
|
import addFormats from 'ajv-formats';
|
|
3
3
|
import radioProtocolSchema from '../schemas/radio-protocol-schema.json' with { type: 'json' };
|
|
4
4
|
|
|
@@ -7,15 +7,20 @@ export interface ValidationResult {
|
|
|
7
7
|
errors?: string[];
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
interface AjvError {
|
|
11
|
+
instancePath?: string;
|
|
12
|
+
message?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
export class SchemaValidator {
|
|
11
|
-
private ajv:
|
|
16
|
+
private ajv: Ajv;
|
|
12
17
|
|
|
13
18
|
constructor() {
|
|
14
|
-
this.ajv = new
|
|
19
|
+
this.ajv = new Ajv({
|
|
15
20
|
allErrors: true,
|
|
16
21
|
verbose: true,
|
|
17
22
|
});
|
|
18
|
-
(addFormats as
|
|
23
|
+
(addFormats as unknown as (ajv: Ajv) => Ajv)(this.ajv);
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
validateRadioProtocol(config: unknown): ValidationResult {
|
|
@@ -26,11 +31,13 @@ export class SchemaValidator {
|
|
|
26
31
|
return { valid: true };
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
let errors: string[] = [];
|
|
35
|
+
if (validate.errors && validate.errors.length > 0) {
|
|
36
|
+
errors = validate.errors.map((error: AjvError) => {
|
|
31
37
|
const path = error.instancePath || 'root';
|
|
32
38
|
return `${path}: ${error.message}`;
|
|
33
|
-
})
|
|
39
|
+
});
|
|
40
|
+
}
|
|
34
41
|
|
|
35
42
|
return {
|
|
36
43
|
errors,
|
|
@@ -2,12 +2,12 @@ export const toHexWords = (data: Uint8Array): string => {
|
|
|
2
2
|
const hexData = Buffer.from(data).toString('hex');
|
|
3
3
|
let formattedData = '';
|
|
4
4
|
|
|
5
|
-
for (let
|
|
6
|
-
if (
|
|
5
|
+
for (let index = 0; index < hexData.length; index += 4) {
|
|
6
|
+
if (index !== 0) {
|
|
7
7
|
formattedData += ' ';
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
formattedData += hexData.
|
|
10
|
+
formattedData += hexData.slice(index, index + 4);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
return formattedData;
|
|
@@ -1,42 +1,62 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { LogLayerTransport } from '@loglayer/shared';
|
|
2
|
+
// eslint-disable-next-line no-duplicate-imports, sort-imports
|
|
3
|
+
import { BaseTransport, type LogLayerTransportParams } from '@loglayer/transport';
|
|
2
4
|
import { LogLayer } from 'loglayer';
|
|
3
5
|
import { UILogger } from './ui-logger.js';
|
|
4
6
|
|
|
7
|
+
interface LogEntry {
|
|
8
|
+
data: unknown;
|
|
9
|
+
hasData: boolean;
|
|
10
|
+
level: string;
|
|
11
|
+
message: string;
|
|
12
|
+
timestamp: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
5
15
|
/**
|
|
6
16
|
* Custom LogLayer transport for capturing UI log entries.
|
|
7
17
|
*/
|
|
8
|
-
const UILogTransport = class extends BaseTransport<
|
|
9
|
-
|
|
18
|
+
const UILogTransport = class extends BaseTransport<unknown> {
|
|
19
|
+
private callback: (logEntry: LogEntry) => void;
|
|
10
20
|
|
|
11
|
-
constructor(callback: (logEntry:
|
|
21
|
+
constructor(callback: (logEntry: LogEntry) => void) {
|
|
12
22
|
// Pass a dummy logger to BaseTransport
|
|
13
|
-
super({
|
|
14
|
-
|
|
23
|
+
super({
|
|
24
|
+
enabled: true,
|
|
25
|
+
id: 'ui-logger-transport',
|
|
26
|
+
logger: {
|
|
27
|
+
log: (): void => {
|
|
28
|
+
// Empty function for dummy logger
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
this.callback = callback;
|
|
15
33
|
}
|
|
16
34
|
|
|
17
|
-
shipToLogger(params:
|
|
18
|
-
const logEntry = {
|
|
35
|
+
shipToLogger(params: LogLayerTransportParams): unknown[] {
|
|
36
|
+
const logEntry: LogEntry = {
|
|
19
37
|
data: params.data,
|
|
20
|
-
hasData: params.hasData,
|
|
38
|
+
hasData: params.hasData ?? false,
|
|
21
39
|
level: params.logLevel,
|
|
22
40
|
message: params.messages.join(' '),
|
|
23
41
|
timestamp: new Date().toISOString(),
|
|
24
42
|
};
|
|
25
|
-
this
|
|
43
|
+
this.callback(logEntry);
|
|
26
44
|
return [];
|
|
27
45
|
}
|
|
28
46
|
};
|
|
29
47
|
|
|
30
48
|
export interface UILoggerConfig {
|
|
31
|
-
customTransport?:
|
|
49
|
+
customTransport?: LogLayerTransport;
|
|
32
50
|
enableConsoleDebug?: boolean;
|
|
33
51
|
}
|
|
34
52
|
|
|
53
|
+
// Move defaultCallback to outer scope to avoid recreating it on every call
|
|
54
|
+
const defaultCallback = (logEntry: LogEntry): void => {
|
|
55
|
+
// eslint-disable-next-line no-console
|
|
56
|
+
console.log(JSON.stringify(logEntry));
|
|
57
|
+
};
|
|
58
|
+
|
|
35
59
|
export const createUILogger = function createUILogger(config: UILoggerConfig = {}): UILogger {
|
|
36
|
-
// By default, just print to console as JSON
|
|
37
|
-
const defaultCallback = (logEntry: any) => {
|
|
38
|
-
console.log(JSON.stringify(logEntry));
|
|
39
|
-
};
|
|
40
60
|
const transport = config.customTransport || new UILogTransport(defaultCallback);
|
|
41
61
|
const logger = new LogLayer({
|
|
42
62
|
consoleDebug: config.enableConsoleDebug ?? false,
|
|
@@ -46,7 +66,7 @@ export const createUILogger = function createUILogger(config: UILoggerConfig = {
|
|
|
46
66
|
return new UILogger(logger);
|
|
47
67
|
};
|
|
48
68
|
|
|
49
|
-
export const createUILoggerWithCallback = function createUILoggerWithCallback(callback: (logEntry:
|
|
69
|
+
export const createUILoggerWithCallback = function createUILoggerWithCallback(callback: (logEntry: LogEntry) => void, config: UILoggerConfig = {}): UILogger {
|
|
50
70
|
const transport = new UILogTransport(callback);
|
|
51
71
|
const logger = new LogLayer({
|
|
52
72
|
consoleDebug: config.enableConsoleDebug ?? false,
|
package/src/utils/ui-logger.ts
CHANGED
|
@@ -22,12 +22,38 @@ import type { ILogLayer } from 'loglayer';
|
|
|
22
22
|
* - Separate from debug logging avoids UI noise
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
+
interface ProtocolContext {
|
|
26
|
+
variables?: Map<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
/**
|
|
26
30
|
* Generic protocol step interface for UI logging
|
|
27
31
|
*/
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
export type UIProtocolStep = Record<string, unknown>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Type guards for protocol step properties
|
|
36
|
+
*/
|
|
37
|
+
const hasDescription = (obj: unknown): obj is { description: string } =>
|
|
38
|
+
typeof obj === 'object' && obj !== null && 'description' in obj && typeof (obj as Record<string, unknown>).description === 'string';
|
|
39
|
+
|
|
40
|
+
const hasReceive = (obj: unknown): obj is { receive: unknown } => typeof obj === 'object' && obj !== null && 'receive' in obj;
|
|
41
|
+
|
|
42
|
+
const hasEndChunk = (obj: unknown): obj is { endChunk: { receive: unknown } } =>
|
|
43
|
+
typeof obj === 'object' &&
|
|
44
|
+
obj !== null &&
|
|
45
|
+
'endChunk' in obj &&
|
|
46
|
+
typeof (obj as Record<string, unknown>).endChunk === 'object' &&
|
|
47
|
+
(obj as Record<string, unknown>).endChunk !== null &&
|
|
48
|
+
'receive' in ((obj as Record<string, unknown>).endChunk as Record<string, unknown>);
|
|
49
|
+
|
|
50
|
+
const hasStartChunk = (obj: unknown): obj is { startChunk: { receive: unknown } } =>
|
|
51
|
+
typeof obj === 'object' &&
|
|
52
|
+
obj !== null &&
|
|
53
|
+
'startChunk' in obj &&
|
|
54
|
+
typeof (obj as Record<string, unknown>).startChunk === 'object' &&
|
|
55
|
+
(obj as Record<string, unknown>).startChunk !== null &&
|
|
56
|
+
'receive' in ((obj as Record<string, unknown>).startChunk as Record<string, unknown>);
|
|
31
57
|
|
|
32
58
|
/**
|
|
33
59
|
* UI Logger for capturing command-level information for UI display
|
|
@@ -63,7 +89,7 @@ export class UILogger {
|
|
|
63
89
|
}
|
|
64
90
|
|
|
65
91
|
// eslint-disable-next-line max-params
|
|
66
|
-
logCommandSuccess(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, context:
|
|
92
|
+
logCommandSuccess(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, context: ProtocolContext): void {
|
|
67
93
|
const commandId = `${operation}-${stepIndex}`;
|
|
68
94
|
const startTime = this.commandStartTimes.get(commandId) || Date.now();
|
|
69
95
|
const endTime = Date.now();
|
|
@@ -73,20 +99,18 @@ export class UILogger {
|
|
|
73
99
|
const description = this.getStepDescription(step);
|
|
74
100
|
|
|
75
101
|
// Extract sent and received data from context
|
|
76
|
-
const dataSent = context.variables
|
|
77
|
-
const dataReceived = context.variables
|
|
102
|
+
const dataSent = context.variables && context.variables.get('lastSentData');
|
|
103
|
+
const dataReceived = context.variables && context.variables.get('lastReceivedData');
|
|
78
104
|
const dataExpected = this.getExpectedData(step);
|
|
79
105
|
|
|
80
106
|
// For readSegment, also extract chunk logs if present
|
|
81
|
-
|
|
82
|
-
if ('readSegment' === commandType) {
|
|
83
|
-
dataChunks = context.variables?.get('lastReadSegmentChunks');
|
|
84
|
-
// For readSegment, we don't need the flattened dataSent/dataReceived since we have dataChunks
|
|
85
|
-
}
|
|
107
|
+
const dataChunks = commandType === 'readSegment' && context.variables && context.variables.get('lastReadSegmentChunks');
|
|
86
108
|
|
|
87
109
|
// Convert dataSent and dataReceived to byte array format if they exist
|
|
88
|
-
|
|
89
|
-
const
|
|
110
|
+
// eslint-disable-next-line unicorn/prefer-spread
|
|
111
|
+
const dataSentArray = dataSent && Array.from(dataSent as Uint8Array);
|
|
112
|
+
// eslint-disable-next-line unicorn/prefer-spread
|
|
113
|
+
const dataReceivedArray = dataReceived && Array.from(dataReceived as Uint8Array);
|
|
90
114
|
|
|
91
115
|
this.logger
|
|
92
116
|
.withMetadata({
|
|
@@ -108,7 +132,7 @@ export class UILogger {
|
|
|
108
132
|
}
|
|
109
133
|
|
|
110
134
|
// eslint-disable-next-line max-params
|
|
111
|
-
logCommandFailure(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, error: Error, context:
|
|
135
|
+
logCommandFailure(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, error: Error, context: ProtocolContext): void {
|
|
112
136
|
const commandId = `${operation}-${stepIndex}`;
|
|
113
137
|
const startTime = this.commandStartTimes.get(commandId) || Date.now();
|
|
114
138
|
const endTime = Date.now();
|
|
@@ -118,11 +142,12 @@ export class UILogger {
|
|
|
118
142
|
const description = this.getStepDescription(step);
|
|
119
143
|
|
|
120
144
|
// Extract sent data from context
|
|
121
|
-
const dataSent = context.variables
|
|
145
|
+
const dataSent = context.variables && context.variables.get('lastSentData');
|
|
122
146
|
const dataExpected = this.getExpectedData(step);
|
|
123
147
|
|
|
124
148
|
// Convert dataSent to byte array format if it exists
|
|
125
|
-
|
|
149
|
+
// eslint-disable-next-line unicorn/prefer-spread
|
|
150
|
+
const dataSentArray = dataSent && Array.from(dataSent as Uint8Array);
|
|
126
151
|
|
|
127
152
|
this.logger
|
|
128
153
|
.withMetadata({
|
|
@@ -165,26 +190,26 @@ export class UILogger {
|
|
|
165
190
|
}
|
|
166
191
|
|
|
167
192
|
private getStepDescription(step: UIProtocolStep): string {
|
|
168
|
-
if ('sendReceive' in step && step.sendReceive
|
|
193
|
+
if ('sendReceive' in step && hasDescription(step.sendReceive)) {
|
|
169
194
|
return step.sendReceive.description;
|
|
170
195
|
}
|
|
171
|
-
if ('send' in step && step.send
|
|
196
|
+
if ('send' in step && hasDescription(step.send)) {
|
|
172
197
|
return step.send.description;
|
|
173
198
|
}
|
|
174
|
-
if ('receive' in step && step.receive
|
|
199
|
+
if ('receive' in step && hasDescription(step.receive)) {
|
|
175
200
|
return step.receive.description;
|
|
176
201
|
}
|
|
177
|
-
if ('readSegment' in step && step.readSegment
|
|
202
|
+
if ('readSegment' in step && hasDescription(step.readSegment)) {
|
|
178
203
|
return step.readSegment.description;
|
|
179
204
|
}
|
|
180
|
-
if ('writeSegment' in step && step.writeSegment
|
|
205
|
+
if ('writeSegment' in step && hasDescription(step.writeSegment)) {
|
|
181
206
|
return step.writeSegment.description;
|
|
182
207
|
}
|
|
183
208
|
return 'No description';
|
|
184
209
|
}
|
|
185
210
|
|
|
186
|
-
private getExpectedData(step: UIProtocolStep):
|
|
187
|
-
if ('sendReceive' in step && step.sendReceive
|
|
211
|
+
private getExpectedData(step: UIProtocolStep): unknown {
|
|
212
|
+
if ('sendReceive' in step && hasReceive(step.sendReceive)) {
|
|
188
213
|
return step.sendReceive.receive;
|
|
189
214
|
}
|
|
190
215
|
if ('receive' in step) {
|
|
@@ -192,12 +217,15 @@ export class UILogger {
|
|
|
192
217
|
}
|
|
193
218
|
if ('readSegment' in step) {
|
|
194
219
|
// For readSegment, return both start and end chunk receive patterns
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
220
|
+
const { readSegment } = step;
|
|
221
|
+
if (hasEndChunk(readSegment) && hasStartChunk(readSegment)) {
|
|
222
|
+
return {
|
|
223
|
+
endChunk: readSegment.endChunk.receive,
|
|
224
|
+
startChunk: readSegment.startChunk.receive,
|
|
225
|
+
type: 'readSegment',
|
|
226
|
+
};
|
|
227
|
+
}
|
|
200
228
|
}
|
|
201
|
-
return
|
|
229
|
+
return {};
|
|
202
230
|
}
|
|
203
231
|
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export const bcdToNumber = (data: Uint8Array, offset?: number, length?: number): number => {
|
|
2
|
-
let bcd = 0;
|
|
3
|
-
const size = length ?? data.length;
|
|
4
|
-
|
|
5
|
-
for (let i = offset ?? 0; i < size; i++) {
|
|
6
|
-
bcd |= data[i] << (8 * i);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
return parseInt(bcd.toString(16), 10) * 10;
|
|
10
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export const numberToBcd = (value: number): Uint8Array => {
|
|
2
|
-
const digits = value.toString(10);
|
|
3
|
-
const numberOfBytes = Math.ceil(digits.length / 2);
|
|
4
|
-
|
|
5
|
-
const data = new Uint8Array(numberOfBytes);
|
|
6
|
-
|
|
7
|
-
let bcd = parseInt(digits, 16);
|
|
8
|
-
|
|
9
|
-
for (let i = 0; i < numberOfBytes; i++) {
|
|
10
|
-
data[i] = bcd & 0xff;
|
|
11
|
-
bcd >>= 8;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return data;
|
|
15
|
-
};
|