feat(device-plane): add typed B2 service ping transport
This commit is contained in:
@@ -51,8 +51,8 @@ export const ARUSNAVI_B2_MODEL_PROFILE = deepFreeze({
|
||||
package: "package-number-only",
|
||||
},
|
||||
commandTransport: {
|
||||
status: "disabled",
|
||||
exportedCommandBuilders: 0,
|
||||
status: "typed-service-ping-v1",
|
||||
exportedCommandBuilders: 1,
|
||||
},
|
||||
routeCompatibility: {
|
||||
gelios: "parallel-preserved",
|
||||
@@ -76,6 +76,8 @@ export const ARUSNAVI_B2_ADAPTER = defineDeviceAdapter({
|
||||
buildMessageAcknowledgement(message) {
|
||||
return buildB2PackageAcknowledgement(message.packageNumber);
|
||||
},
|
||||
buildTypedCommand: buildB2TypedCommand,
|
||||
parseTypedCommandResponse: tryParseB2TypedCommandResponse,
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -238,6 +240,36 @@ export function buildB2PackageAcknowledgement(packageNumber) {
|
||||
]);
|
||||
}
|
||||
|
||||
export function buildB2TypedCommand({ commandType, accessCode } = {}) {
|
||||
if (commandType !== "service.ping") {
|
||||
throw new TypeError("b2_typed_command_unsupported");
|
||||
}
|
||||
if (typeof accessCode !== "string" || !/^\d{6}$/.test(accessCode)) {
|
||||
throw new TypeError("b2_command_access_code_invalid");
|
||||
}
|
||||
return Buffer.from(`${accessCode}*SERV*1.1`, "ascii");
|
||||
}
|
||||
|
||||
export function tryParseB2TypedCommandResponse(input, { commandType } = {}) {
|
||||
assertBuffer(input, "b2_command_response_buffer_required");
|
||||
if (commandType !== "service.ping") {
|
||||
throw new TypeError("b2_typed_command_unsupported");
|
||||
}
|
||||
const expected = Buffer.from("SERV OK", "ascii");
|
||||
const compared = Math.min(input.length, expected.length);
|
||||
if (!input.subarray(0, compared).equals(expected.subarray(0, compared))) {
|
||||
return Object.freeze({ status: "not-command" });
|
||||
}
|
||||
if (input.length < expected.length) {
|
||||
return Object.freeze({ status: "incomplete", minimumBytes: expected.length });
|
||||
}
|
||||
return Object.freeze({
|
||||
status: "acknowledged",
|
||||
bytesConsumed: expected.length,
|
||||
resultCode: "serv_ok",
|
||||
});
|
||||
}
|
||||
|
||||
export function assertB2ProfileInvariant(profile = ARUSNAVI_B2_MODEL_PROFILE) {
|
||||
if (profile.monitoringServerSlots !== 4) {
|
||||
throw new TypeError("b2_server_slot_count_invalid");
|
||||
@@ -257,8 +289,8 @@ export function assertB2ProfileInvariant(profile = ARUSNAVI_B2_MODEL_PROFILE) {
|
||||
) {
|
||||
throw new TypeError("b2_framing_specification_invalid");
|
||||
}
|
||||
if (profile.commandTransport.status !== "disabled") {
|
||||
throw new TypeError("b2_command_transport_must_be_disabled");
|
||||
if (profile.commandTransport.status !== "typed-service-ping-v1") {
|
||||
throw new TypeError("b2_command_transport_profile_invalid");
|
||||
}
|
||||
if (profile.routeCompatibility.gelios !== "parallel-preserved") {
|
||||
throw new TypeError("b2_gelios_route_must_be_preserved");
|
||||
|
||||
@@ -7,8 +7,10 @@ import {
|
||||
assertB2ProfileInvariant,
|
||||
buildB2HeaderAcknowledgement,
|
||||
buildB2PackageAcknowledgement,
|
||||
buildB2TypedCommand,
|
||||
tryParseB2Header2,
|
||||
tryParseB2Package,
|
||||
tryParseB2TypedCommandResponse,
|
||||
} from "../src/index.mjs";
|
||||
|
||||
const specificationHeader = Buffer.from(
|
||||
@@ -131,10 +133,32 @@ test("fails closed on unsupported headers and malformed packages", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("exports no command builder and keeps transport disabled", () => {
|
||||
assert.equal(ARUSNAVI_B2_MODEL_PROFILE.commandTransport.status, "disabled");
|
||||
test("exports only the typed service-ping command", () => {
|
||||
assert.equal(
|
||||
ARUSNAVI_B2_MODEL_PROFILE.commandTransport.status,
|
||||
"typed-service-ping-v1",
|
||||
);
|
||||
assert.equal(
|
||||
ARUSNAVI_B2_MODEL_PROFILE.commandTransport.exportedCommandBuilders,
|
||||
0,
|
||||
1,
|
||||
);
|
||||
assert.equal(
|
||||
buildB2TypedCommand({ commandType: "service.ping", accessCode: "123456" })
|
||||
.toString("ascii"),
|
||||
"123456*SERV*1.1",
|
||||
);
|
||||
assert.deepEqual(
|
||||
tryParseB2TypedCommandResponse(Buffer.from("SERV OK", "ascii"), {
|
||||
commandType: "service.ping",
|
||||
}),
|
||||
{ status: "acknowledged", bytesConsumed: 7, resultCode: "serv_ok" },
|
||||
);
|
||||
assert.throws(
|
||||
() => buildB2TypedCommand({ commandType: "service.ping", accessCode: "12345" }),
|
||||
/b2_command_access_code_invalid/,
|
||||
);
|
||||
assert.throws(
|
||||
() => buildB2TypedCommand({ commandType: "firmware.update", accessCode: "123456" }),
|
||||
/b2_typed_command_unsupported/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ export const EDGE_TO_CORE_MESSAGE_KINDS = Object.freeze([
|
||||
"discovery.observed",
|
||||
"adapter.message",
|
||||
"delivery.acknowledged",
|
||||
"command.status",
|
||||
"channel.counters",
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user