Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: consider max LR payload size in firmware updates #6759

Merged
merged 1 commit into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
}
Loading