Skip to content

Commit

Permalink
feat: skip rebuilding routes for nodes with priority return routes (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
AlCalzone authored Oct 8, 2024
1 parent 0b2342e commit 6b35559
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 0 deletions.
6 changes: 6 additions & 0 deletions docs/api/controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,8 @@ async rebuildNodeRoutes(nodeId: number): Promise<boolean>

Rebuilds routes for a single alive node in the network, updating the neighbor list and assigning fresh routes to association targets. The returned promise resolves to `true` if the process was completed, or `false` if it was unsuccessful.

> [!ATTENTION] Rebuilding routes for a single node will delete existing priority return routes to end nodes and the SUC. It is recommended to first check if priority return routes are known to exist using `getPriorityReturnRoutesCached` and `getPrioritySUCReturnRouteCached` and asking for confirmation before proceeding.
#### `beginRebuildingRoutes`

```ts
Expand All @@ -449,6 +451,8 @@ The `options` argument can be used to skip sleeping nodes:
interface RebuildRoutesOptions {
/** Whether the routes of sleeping nodes should be rebuilt too at the end of the process. Default: true */
includeSleeping?: boolean;
/** Whether nodes with priority return routes should be included, as those will be deleted. Default: false */
deletePriorityReturnRoutes?: boolean;
}
```

Expand Down Expand Up @@ -628,10 +632,12 @@ As mentioned before, there is unfortunately no way to query return routes from a

```ts
getPriorityReturnRouteCached(nodeId: number, destinationNodeId: number): MaybeUnknown<Route> | undefined;
getPriorityReturnRoutesCached(nodeId: number): Record<number, Route>;
getPrioritySUCReturnRouteCached(nodeId: number): MaybeUnknown<Route> | undefined;
```

- `getPriorityReturnRouteCached` returns a priority return route that was set using `assignPriorityReturnRoute`. If a non-priority return route has been set since assigning the priority route, this will return `UNKNOWN_STATE` (`null`).
- `getPriorityReturnRoutesCached` returns an object containing the IDs of all known end node destinations a node has priority return routes for and their respective routes.
- `getPrioritySUCReturnRouteCached` does the same for a route set through `assignPrioritySUCReturnRoute`.

The return type `Route` has the following shape:
Expand Down
39 changes: 39 additions & 0 deletions packages/zwave-js/src/lib/controller/Controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4579,6 +4579,7 @@ export class ZWaveController
if (existingTask) return false;

options.includeSleeping ??= true;
options.deletePriorityReturnRoutes ??= false;

this.driver.controllerLog.print(
`rebuilding routes${
Expand All @@ -4605,6 +4606,21 @@ export class ZWaveController
);
this._rebuildRoutesProgress.set(id, "skipped");
} else if (!options.includeSleeping && node.canSleep) {
this.driver.controllerLog.logNode(
id,
`Skipping route rebuild because the node is sleeping.`,
);
this._rebuildRoutesProgress.set(id, "skipped");
} else if (
!options.deletePriorityReturnRoutes
&& (this.getPrioritySUCReturnRouteCached(id)
|| Object.keys(this.getPriorityReturnRoutesCached(id))
.length > 0)
) {
this.driver.controllerLog.logNode(
id,
`Skipping route rebuild because the node has priority return routes.`,
);
this._rebuildRoutesProgress.set(id, "skipped");
} else {
this._rebuildRoutesProgress.set(id, "pending");
Expand Down Expand Up @@ -5804,6 +5820,29 @@ export class ZWaveController
);
}

/**
* For the given node, returns all end node destinations and the priority routes to them.
*
* **Note:** This is using cached information, since there's no way to query priority routes from a node.
* If another controller has assigned routes in the meantime, this information may be out of date.
*/
public getPriorityReturnRoutesCached(
nodeId: number,
): Record<number, Route> {
const ret: Record<number, Route> = {};

const routes = this.driver.cacheList<Route>(
cacheKeys.node(nodeId)._priorityReturnRouteBaseKey,
);
for (const [key, route] of Object.entries(routes)) {
const destination = cacheKeyUtils
.destinationFromPriorityReturnRouteKey(key);
if (destination !== undefined) ret[destination] = route;
}

return ret;
}

/**
* Assigns a priority route from an end node to the SUC. This route will always be used for the first transmission attempt.
* @param nodeId The ID of the end node for which to assign the route
Expand Down
2 changes: 2 additions & 0 deletions packages/zwave-js/src/lib/controller/_Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export type RebuildRoutesStatus = "pending" | "done" | "failed" | "skipped";
export interface RebuildRoutesOptions {
/** Whether the routes of sleeping nodes should be rebuilt too at the end of the process. Default: true */
includeSleeping?: boolean;
/** Whether nodes with priority return routes should be included, as those will be deleted. Default: false */
deletePriorityReturnRoutes?: boolean;
}

export type SDKVersion =
Expand Down
29 changes: 29 additions & 0 deletions packages/zwave-js/src/lib/driver/Driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6731,6 +6731,35 @@ ${handlers.length} left`,
}
}

/**
* @internal
* Helper function to find multiple existing values from the network cache
*/
public cacheList<T>(
prefix: string,
options?: {
reviver?: (value: any) => T;
},
): Record<string, T> {
const ret: Record<string, T> = {};
for (const entry of this.networkCache.entries()) {
const key = entry[0];
if (!key.startsWith(prefix)) continue;
let value = entry[1];
if (value === undefined) continue;
if (typeof options?.reviver === "function") {
try {
value = options.reviver(value);
} catch {
// invalid entry
continue;
}
}
ret[key] = value;
}
return ret;
}

private cachePurge(prefix: string): void {
for (const key of this.networkCache.keys()) {
if (key.startsWith(prefix)) {
Expand Down
9 changes: 9 additions & 0 deletions packages/zwave-js/src/lib/driver/NetworkCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const cacheKeys = {
return {
_baseKey: nodeBaseKey,
_securityClassBaseKey: `${nodeBaseKey}securityClasses`,
_priorityReturnRouteBaseKey: `${nodeBaseKey}priorityReturnRoute`,
interviewStage: `${nodeBaseKey}interviewStage`,
deviceClass: `${nodeBaseKey}deviceClass`,
isListening: `${nodeBaseKey}isListening`,
Expand Down Expand Up @@ -117,6 +118,14 @@ export const cacheKeyUtils = {
return parseInt(match.groups!.index, 10);
}
},
destinationFromPriorityReturnRouteKey: (
key: string,
): number | undefined => {
const match = /\.priorityReturnRoute\.(?<nodeId>\d+)$/.exec(key);
if (match) {
return parseInt(match.groups!.nodeId, 10);
}
},
} as const;

function tryParseInterviewStage(value: unknown): InterviewStage | undefined {
Expand Down

0 comments on commit 6b35559

Please sign in to comment.