From fe3bbb8553fd65ba4ce073d4d842e1bf23a78c64 Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Thu, 11 Apr 2024 09:55:53 +0200 Subject: [PATCH] fix: consider max LR payload size in firmware updates --- .../zwave-js/src/lib/controller/Controller.ts | 25 ++++++---- packages/zwave-js/src/lib/driver/Driver.ts | 50 ++++++++++++++++++- .../src/lib/driver/MessageGenerators.ts | 7 ++- .../transport/SendDataBridgeMessages.ts | 17 ------- .../serialapi/transport/SendDataMessages.ts | 18 ------- .../lib/serialapi/transport/SendDataShared.ts | 4 -- 6 files changed, 68 insertions(+), 53 deletions(-) diff --git a/packages/zwave-js/src/lib/controller/Controller.ts b/packages/zwave-js/src/lib/controller/Controller.ts index c6adbd1aaf8a..573d1d9bf08f 100644 --- a/packages/zwave-js/src/lib/controller/Controller.ts +++ b/packages/zwave-js/src/lib/controller/Controller.ts @@ -690,6 +690,18 @@ export class ZWaveController return this._supportsLongRange; } + private _maxPayloadSize: MaybeNotKnown; + /** The maximum payload size that can be transmitted with a Z-Wave explorer frame */ + public get maxPayloadSize(): MaybeNotKnown { + return this._maxPayloadSize; + } + + private _maxPayloadSizeLR: MaybeNotKnown; + /** The maximum payload size that can be transmitted with a Z-Wave Long Range frame */ + public get maxPayloadSizeLR(): MaybeNotKnown { + return this._maxPayloadSizeLR; + } + private _nodes: ThrowingMap; /** A dictionary of the nodes connected to this controller */ public get nodes(): ReadonlyThrowingMap { @@ -1100,18 +1112,16 @@ export class ZWaveController } // Figure out the maximum payload size for outgoing commands - let maxPayloadSize: number | undefined; if ( this.isSerialAPISetupCommandSupported( SerialAPISetupCommand.GetMaximumPayloadSize, ) ) { this.driver.controllerLog.print(`querying max. payload size...`); - maxPayloadSize = await this.getMaxPayloadSize(); + this._maxPayloadSize = await this.getMaxPayloadSize(); this.driver.controllerLog.print( - `maximum payload size: ${maxPayloadSize} bytes`, + `maximum payload size: ${this._maxPayloadSize} bytes`, ); - // TODO: cache this information } this.driver.controllerLog.print( @@ -1147,15 +1157,12 @@ export class ZWaveController // Fetch the list of Long Range nodes const lrNodeIds = await this.getLongRangeNodes(); - let maxPayloadSizeLR: number | undefined; if ( this.isSerialAPISetupCommandSupported( SerialAPISetupCommand.GetLongRangeMaximumPayloadSize, ) ) { - maxPayloadSizeLR = await this.getMaxPayloadSizeLongRange(); - - // TODO: cache this information + this._maxPayloadSizeLR = await this.getMaxPayloadSizeLongRange(); } let lrChannel: LongRangeChannel | undefined; @@ -1171,7 +1178,7 @@ export class ZWaveController this.driver.controllerLog.print( `received Z-Wave Long Range capabilities: - max. payload size: ${maxPayloadSizeLR} bytes + max. payload size: ${this._maxPayloadSizeLR} bytes channel: ${ lrChannel ? getEnumMemberName(LongRangeChannel, lrChannel) diff --git a/packages/zwave-js/src/lib/driver/Driver.ts b/packages/zwave-js/src/lib/driver/Driver.ts index 7727b3feb17f..bf8b31813f6d 100644 --- a/packages/zwave-js/src/lib/driver/Driver.ts +++ b/packages/zwave-js/src/lib/driver/Driver.ts @@ -60,6 +60,7 @@ import { MessagePriority, type MessageRecord, type MulticastDestination, + NUM_NODEMASK_BYTES, NodeIDType, RFRegion, SPANState, @@ -6234,7 +6235,54 @@ ${handlers.length} left`, msg.command = this.encapsulateCommands( msg.command, ) as SinglecastCC; - return msg.command.getMaxPayloadLength(msg.getMaxPayloadLength()); + return msg.command.getMaxPayloadLength(this.getMaxPayloadLength(msg)); + } + + /** Computes the maximum payload size that can be transmitted with the given message */ + public getMaxPayloadLength(msg: SendDataMessage): number { + const nodeId = msg.getNodeId(); + + // For ZWLR, the result is simply the maximum payload size + if ( + nodeId != undefined + && isLongRangeNodeId(nodeId) + && this._controller?.maxPayloadSizeLR + ) { + return this._controller.maxPayloadSizeLR; + } + + // For ZW Classic, it depends on the frame type and transmit options + const maxExplorerPayloadSinglecast = this._controller?.maxPayloadSize + ?? 46; + if (isSendDataSinglecast(msg)) { + // From INS13954-7, chapter 4.3.3.1.5 + if (msg.transmitOptions & TransmitOptions.Explore) { + return maxExplorerPayloadSinglecast; + } + if (msg.transmitOptions & TransmitOptions.AutoRoute) { + return maxExplorerPayloadSinglecast + 2; + } + return maxExplorerPayloadSinglecast + 8; + } else { + // Multicast needs space for the nodes bitmask + const maxExplorerPayloadMulticast = maxExplorerPayloadSinglecast + - NUM_NODEMASK_BYTES; + + // From INS13954-13, chapter 4.3.3.6 + if (msg.transmitOptions & TransmitOptions.ACK) { + if (msg.transmitOptions & TransmitOptions.Explore) { + return maxExplorerPayloadMulticast; + } + if (msg.transmitOptions & TransmitOptions.AutoRoute) { + return maxExplorerPayloadMulticast + 2; + } + } + return maxExplorerPayloadMulticast + 8; + } + } + + public exceedsMaxPayloadLength(msg: SendDataMessage): boolean { + return msg.serializeCC().length > this.getMaxPayloadLength(msg); } /** Determines time in milliseconds to wait for a report from a node */ diff --git a/packages/zwave-js/src/lib/driver/MessageGenerators.ts b/packages/zwave-js/src/lib/driver/MessageGenerators.ts index 7bcc0c8041cd..0841ac7b3628 100644 --- a/packages/zwave-js/src/lib/driver/MessageGenerators.ts +++ b/packages/zwave-js/src/lib/driver/MessageGenerators.ts @@ -50,7 +50,6 @@ import { createDeferredPromise, } from "alcalzone-shared/deferred-promise"; import { - exceedsMaxPayloadLength, isSendData, isTransmitReport, } from "../serialapi/transport/SendDataShared"; @@ -134,7 +133,7 @@ export const simpleMessageGenerator: MessageGeneratorImplementation = additionalCommandTimeoutMs = 0, ) { // Make sure we can send this message - if (isSendData(msg) && exceedsMaxPayloadLength(msg)) { + if (isSendData(msg) && driver.exceedsMaxPayloadLength(msg)) { // We use explorer frames by default, but this reduces the maximum payload length by 2 bytes compared to AUTO_ROUTE // Try disabling explorer frames for this message and see if it fits now. const fail = () => { @@ -145,7 +144,7 @@ export const simpleMessageGenerator: MessageGeneratorImplementation = }; if (msg.transmitOptions & TransmitOptions.Explore) { msg.transmitOptions &= ~TransmitOptions.Explore; - if (exceedsMaxPayloadLength(msg)) { + if (driver.exceedsMaxPayloadLength(msg)) { // Still too large throw fail(); } @@ -226,7 +225,7 @@ export const maybeTransportServiceGenerator: MessageGeneratorImplementation = node?.supportsCC(CommandClasses["Transport Service"]) && node.getCCVersion(CommandClasses["Transport Service"]) >= 2; - if (!mayUseTransportService || !exceedsMaxPayloadLength(msg)) { + if (!mayUseTransportService || !driver.exceedsMaxPayloadLength(msg)) { // Transport Service isn't needed for this message return yield* simpleMessageGenerator( driver, diff --git a/packages/zwave-js/src/lib/serialapi/transport/SendDataBridgeMessages.ts b/packages/zwave-js/src/lib/serialapi/transport/SendDataBridgeMessages.ts index 7dffb9dd5a2b..94277d88cae2 100644 --- a/packages/zwave-js/src/lib/serialapi/transport/SendDataBridgeMessages.ts +++ b/packages/zwave-js/src/lib/serialapi/transport/SendDataBridgeMessages.ts @@ -156,14 +156,6 @@ export class SendDataBridgeRequest }; } - /** Computes the maximum payload size that can be transmitted with this message */ - public getMaxPayloadLength(): number { - // From INS13954-7, chapter 4.3.3.1.5 - if (this.transmitOptions & TransmitOptions.Explore) return 46; - if (this.transmitOptions & TransmitOptions.AutoRoute) return 48; - return 54; - } - public expectsNodeUpdate(): boolean { return ( // Only true singlecast commands may expect a response @@ -406,15 +398,6 @@ export class SendDataMulticastBridgeRequest< }, }; } - /** Computes the maximum payload size that can be transmitted with this message */ - public getMaxPayloadLength(): number { - // From INS13954-13, chapter 4.3.3.6 - if (this.transmitOptions & TransmitOptions.ACK) { - if (this.transmitOptions & TransmitOptions.Explore) return 17; - if (this.transmitOptions & TransmitOptions.AutoRoute) return 19; - } - return 25; - } } interface SendDataMulticastBridgeRequestTransmitReportOptions diff --git a/packages/zwave-js/src/lib/serialapi/transport/SendDataMessages.ts b/packages/zwave-js/src/lib/serialapi/transport/SendDataMessages.ts index 2f22632cfe9a..f2f7f169a4ec 100644 --- a/packages/zwave-js/src/lib/serialapi/transport/SendDataMessages.ts +++ b/packages/zwave-js/src/lib/serialapi/transport/SendDataMessages.ts @@ -191,14 +191,6 @@ export class SendDataRequest }; } - /** Computes the maximum payload size that can be transmitted with this message */ - public getMaxPayloadLength(): number { - // From INS13954-7, chapter 4.3.3.1.5 - if (this.transmitOptions & TransmitOptions.Explore) return 46; - if (this.transmitOptions & TransmitOptions.AutoRoute) return 48; - return 54; - } - public expectsNodeUpdate(): boolean { return ( // Only true singlecast commands may expect a response @@ -506,16 +498,6 @@ export class SendDataMulticastRequest< }, }; } - - /** Computes the maximum payload size that can be transmitted with this message */ - public getMaxPayloadLength(): number { - // From INS13954-13, chapter 4.3.3.6 - if (this.transmitOptions & TransmitOptions.ACK) { - if (this.transmitOptions & TransmitOptions.Explore) return 17; - if (this.transmitOptions & TransmitOptions.AutoRoute) return 19; - } - return 25; - } } interface SendDataMulticastRequestTransmitReportOptions diff --git a/packages/zwave-js/src/lib/serialapi/transport/SendDataShared.ts b/packages/zwave-js/src/lib/serialapi/transport/SendDataShared.ts index 367bb73d86d6..801392fb3c89 100644 --- a/packages/zwave-js/src/lib/serialapi/transport/SendDataShared.ts +++ b/packages/zwave-js/src/lib/serialapi/transport/SendDataShared.ts @@ -324,7 +324,3 @@ export function hasTXReport( && !!msg.txReport ); } - -export function exceedsMaxPayloadLength(msg: SendDataMessage): boolean { - return msg.serializeCC().length > msg.getMaxPayloadLength(); -}