Skip to content

Commit

Permalink
fix: consider max LR payload size in firmware updates
Browse files Browse the repository at this point in the history
  • Loading branch information
AlCalzone committed Apr 11, 2024
1 parent 049da0a commit fe3bbb8
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 53 deletions.
25 changes: 16 additions & 9 deletions packages/zwave-js/src/lib/controller/Controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,18 @@ export class ZWaveController
return this._supportsLongRange;
}

private _maxPayloadSize: MaybeNotKnown<number>;
/** The maximum payload size that can be transmitted with a Z-Wave explorer frame */
public get maxPayloadSize(): MaybeNotKnown<number> {
return this._maxPayloadSize;
}

private _maxPayloadSizeLR: MaybeNotKnown<number>;
/** The maximum payload size that can be transmitted with a Z-Wave Long Range frame */
public get maxPayloadSizeLR(): MaybeNotKnown<number> {
return this._maxPayloadSizeLR;
}

private _nodes: ThrowingMap<number, ZWaveNode>;
/** A dictionary of the nodes connected to this controller */
public get nodes(): ReadonlyThrowingMap<number, ZWaveNode> {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand Down
50 changes: 49 additions & 1 deletion packages/zwave-js/src/lib/driver/Driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
MessagePriority,
type MessageRecord,
type MulticastDestination,
NUM_NODEMASK_BYTES,
NodeIDType,
RFRegion,
SPANState,
Expand Down Expand Up @@ -6234,7 +6235,54 @@ ${handlers.length} left`,
msg.command = this.encapsulateCommands(
msg.command,
) as SinglecastCC<CommandClass>;
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 */
Expand Down
7 changes: 3 additions & 4 deletions packages/zwave-js/src/lib/driver/MessageGenerators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ import {
createDeferredPromise,
} from "alcalzone-shared/deferred-promise";
import {
exceedsMaxPayloadLength,
isSendData,
isTransmitReport,
} from "../serialapi/transport/SendDataShared";
Expand Down Expand Up @@ -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 = () => {
Expand All @@ -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();
}
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,6 @@ export class SendDataBridgeRequest<CCType extends CommandClass = CommandClass>
};
}

/** 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
Expand Down Expand Up @@ -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
Expand Down
18 changes: 0 additions & 18 deletions packages/zwave-js/src/lib/serialapi/transport/SendDataMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,6 @@ export class SendDataRequest<CCType extends CommandClass = CommandClass>
};
}

/** 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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,3 @@ export function hasTXReport(
&& !!msg.txReport
);
}

export function exceedsMaxPayloadLength(msg: SendDataMessage): boolean {
return msg.serializeCC().length > msg.getMaxPayloadLength();
}

0 comments on commit fe3bbb8

Please sign in to comment.