Skip to content

Commit 025de81

Browse files
authored
fix: routing tweaks (#34)
1 parent e556a03 commit 025de81

File tree

2 files changed

+66
-16
lines changed

2 files changed

+66
-16
lines changed

src/drivers/ot-rcp-driver.ts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2633,6 +2633,16 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
26332633
const linkCount = options & ZigbeeNWKConsts.CMD_LINK_OPTION_COUNT_MASK;
26342634
const links: ZigbeeNWKLinkStatus[] = [];
26352635

2636+
let device = nwkHeader.source64 !== undefined ? this.deviceTable.get(nwkHeader.source64) : undefined;
2637+
2638+
if (!device && nwkHeader.source16 !== undefined) {
2639+
const source64 = this.address16ToAddress64.get(nwkHeader.source16);
2640+
2641+
if (source64 !== undefined) {
2642+
device = this.deviceTable.get(source64);
2643+
}
2644+
}
2645+
26362646
for (let i = 0; i < linkCount; i++) {
26372647
const address = data.readUInt16LE(offset);
26382648
offset += 2;
@@ -2644,6 +2654,25 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
26442654
incomingCost: costByte & ZigbeeNWKConsts.CMD_LINK_INCOMING_COST_MASK,
26452655
outgoingCost: (costByte & ZigbeeNWKConsts.CMD_LINK_OUTGOING_COST_MASK) >> 4,
26462656
});
2657+
2658+
if (device) {
2659+
if (address === ZigbeeConsts.COORDINATOR_ADDRESS) {
2660+
// if neighbor is coordinator, update device table
2661+
device.neighbor = true;
2662+
}
2663+
2664+
const entry: SourceRouteTableEntry =
2665+
address === ZigbeeConsts.COORDINATOR_ADDRESS
2666+
? { relayAddresses: [], pathCost: 1 /* TODO ? */ }
2667+
: { relayAddresses: [address], pathCost: 2 /* TODO ? */ };
2668+
const entries = this.sourceRouteTable.get(device.address16);
2669+
2670+
if (entries === undefined) {
2671+
this.sourceRouteTable.set(device.address16, [entry]);
2672+
} else if (!this.hasSourceRoute(device.address16, entry, entries)) {
2673+
entries.push(entry);
2674+
}
2675+
}
26472676
}
26482677

26492678
logger.debug(() => {
@@ -4979,6 +5008,8 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
49795008
this.emit("deviceLeft", source16, source64);
49805009
});
49815010

5011+
// force new MTORR
5012+
await this.sendPeriodicManyToOneRouteRequest();
49825013
// force saving after device change
49835014
await this.savePeriodicState();
49845015
}
@@ -5066,9 +5097,9 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
50665097

50675098
if (!this.deviceTable.get(destination64 ?? this.address16ToAddress64.get(destination16)!)!.neighbor) {
50685099
// force immediate MTORR
5100+
logger.warning("No known route to destination, forcing discovery", NS);
50695101
setImmediate(this.sendPeriodicManyToOneRouteRequest.bind(this));
5070-
5071-
throw new Error("No known route to destination", { cause: SpinelStatus.UNKNOWN_NEIGHBOR });
5102+
// will send direct as "last resort"
50725103
}
50735104

50745105
return [undefined, undefined, undefined];
@@ -5105,9 +5136,9 @@ export class OTRCPDriver extends EventEmitter<AdapterDriverEventMap> {
51055136

51065137
if (!this.deviceTable.get(destination64 ?? this.address16ToAddress64.get(destination16)!)!.neighbor) {
51075138
// force immediate MTORR
5139+
logger.warning("No known route to destination, forcing discovery", NS);
51085140
setImmediate(this.sendPeriodicManyToOneRouteRequest.bind(this));
5109-
5110-
throw new Error("No known route to destination", { cause: SpinelStatus.UNKNOWN_NEIGHBOR });
5141+
// will send direct as "last resort"
51115142
}
51125143

51135144
// no more source route, bail

test/ot-rcp-driver.test.ts

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,8 @@ describe("OT RCP Driver", () => {
11171117
});
11181118

11191119
it("disassociates", async () => {
1120+
// no-op, not relevant for this test
1121+
vi.spyOn(driver, "sendPeriodicManyToOneRouteRequest").mockImplementation(async () => {});
11201122
driver.allowJoins(0xfe, true);
11211123

11221124
// neighbor FFD
@@ -1687,6 +1689,8 @@ describe("OT RCP Driver", () => {
16871689
});
16881690

16891691
it("performs a join & authorize - ROUTER", async () => {
1692+
// no-op, not relevant for this test
1693+
vi.spyOn(driver, "sendPeriodicManyToOneRouteRequest").mockImplementation(async () => {});
16901694
// Expected flow (APS acks requested from device are skipped for brevity):
16911695
// - NET2_BEACON_REQ_FROM_DEVICE
16921696
// - NET2_BEACON_RESP_FROM_COORD
@@ -2855,9 +2859,9 @@ describe("OT RCP Driver", () => {
28552859

28562860
expect(driver.sourceRouteTable.get(0x4b8e)).toBeUndefined();
28572861
await vi.advanceTimersByTimeAsync(11000); // past concentrator min time
2858-
expect(() => driver.findBestSourceRoute(0x4b8e, undefined)).toThrow("No known route to destination");
2862+
expect(driver.findBestSourceRoute(0x4b8e, undefined)).toStrictEqual([undefined, undefined, undefined]);
28592863
await vi.advanceTimersByTimeAsync(10); // flush
2860-
expect(sendPeriodicManyToOneRouteRequestSpy).toHaveBeenCalledTimes(1);
2864+
expect(sendPeriodicManyToOneRouteRequestSpy).toHaveBeenCalledTimes(1 + 1 /* disassociate */);
28612865
expect(sendZigbeeNWKRouteReqSpy).toHaveBeenCalledTimes(1);
28622866

28632867
//-- too many NO_ACK
@@ -2871,18 +2875,31 @@ describe("OT RCP Driver", () => {
28712875
await vi.advanceTimersByTimeAsync(5000); // not past concentrator min time
28722876
expect(driver.findBestSourceRoute(0x6887, undefined)).toStrictEqual([2, [0x6, 0x7, 0x8], 4]);
28732877
await vi.advanceTimersByTimeAsync(10); // flush
2874-
expect(sendPeriodicManyToOneRouteRequestSpy).toHaveBeenCalledTimes(2);
2878+
expect(sendPeriodicManyToOneRouteRequestSpy).toHaveBeenCalledTimes(2 + 1 /* disassociate */);
28752879
expect(sendZigbeeNWKRouteReqSpy).toHaveBeenCalledTimes(1); // too soon
28762880
expect(driver.sourceRouteTable.get(0x6887)).toStrictEqual([{ relayAddresses: [0x6, 0x7, 0x8], pathCost: 4 }]);
28772881

28782882
//-- too many NO_ACK, no more route
28792883
driver.macNoACKs.set(0x8, 4);
28802884
await vi.advanceTimersByTimeAsync(6000); // past concentrator min time
2881-
expect(() => driver.findBestSourceRoute(0x6887, undefined)).toThrow("No known route to destination");
2885+
expect(driver.findBestSourceRoute(0x6887, undefined)).toStrictEqual([undefined, undefined, undefined]);
28822886
await vi.advanceTimersByTimeAsync(10); // flush
2883-
expect(sendPeriodicManyToOneRouteRequestSpy).toHaveBeenCalledTimes(3);
2887+
expect(sendPeriodicManyToOneRouteRequestSpy).toHaveBeenCalledTimes(3 + 1 /* disassociate */);
28842888
expect(sendZigbeeNWKRouteReqSpy).toHaveBeenCalledTimes(2);
28852889
expect(driver.sourceRouteTable.get(0x6887)).toBeUndefined();
2890+
2891+
//--- received LINK_STATUS indicating direct link to coordinator available
2892+
expect(driver.deviceTable.get(driver.address16ToAddress64.get(0x6887)!)!.neighbor).toStrictEqual(false);
2893+
driver.processZigbeeNWKLinkStatus(
2894+
Buffer.from([97, 0x00, 0x00, 17]),
2895+
0,
2896+
// @ts-expect-error minimal mock
2897+
{ source16: 0x6887, source64: 5149013643361676n },
2898+
{ source16: 0x6887, source64: 5149013643361676n },
2899+
);
2900+
await vi.advanceTimersByTimeAsync(10); // flush
2901+
expect(driver.sourceRouteTable.get(0x6887)).toStrictEqual([{ relayAddresses: [], pathCost: 1 }]);
2902+
expect(driver.deviceTable.get(driver.address16ToAddress64.get(0x6887)!)!.neighbor).toStrictEqual(true);
28862903
});
28872904

28882905
it("checks if source route exists in entries for a given device", () => {
@@ -3013,8 +3030,8 @@ describe("OT RCP Driver", () => {
30133030
Buffer.from([2, dest16 & 0xff, (dest16 >> 8) & 0xff]),
30143031
0,
30153032
// @ts-expect-error minimal mock
3016-
{ source16: 1, source64: 1n },
3017-
{ source16: 1, source64: 1n },
3033+
{ source16: 0x9ed5, source64: 5149013578478658n },
3034+
{ source16: 0x9ed5, source64: 5149013578478658n },
30183035
);
30193036

30203037
expect(driver.routeFailures.get(0x91d2)).toStrictEqual(1);
@@ -3023,8 +3040,8 @@ describe("OT RCP Driver", () => {
30233040
Buffer.from([2, dest16 & 0xff, (dest16 >> 8) & 0xff]),
30243041
0,
30253042
// @ts-expect-error minimal mock
3026-
{ source16: 1, source64: 1n },
3027-
{ source16: 1, source64: 1n },
3043+
{ source16: 0x9ed5, source64: 5149013578478658n },
3044+
{ source16: 0x9ed5, source64: 5149013578478658n },
30283045
);
30293046

30303047
expect(driver.routeFailures.get(0x91d2)).toStrictEqual(2);
@@ -3033,13 +3050,13 @@ describe("OT RCP Driver", () => {
30333050
Buffer.from([2, dest16 & 0xff, (dest16 >> 8) & 0xff]),
30343051
0,
30353052
// @ts-expect-error minimal mock
3036-
{ source16: 1, source64: 1n },
3037-
{ source16: 1, source64: 1n },
3053+
{ source16: 0x9ed5, source64: 5149013578478658n },
3054+
{ source16: 0x9ed5, source64: 5149013578478658n },
30383055
);
30393056

30403057
expect(driver.routeFailures.get(0x91d2)).toStrictEqual(0);
30413058
expect(driver.sourceRouteTable.get(0x9ed5)).toBeUndefined();
3042-
expect(() => driver.findBestSourceRoute(0x9ed5, undefined)).toThrow("No known route to destination");
3059+
expect(driver.findBestSourceRoute(0x9ed5, undefined)).toStrictEqual([undefined, undefined, undefined]);
30433060
await vi.advanceTimersByTimeAsync(10); // flush
30443061
expect(sendPeriodicManyToOneRouteRequestSpy).toHaveBeenCalledTimes(1);
30453062
expect(sendZigbeeNWKRouteReqSpy).toHaveBeenCalledTimes(1);
@@ -3162,6 +3179,7 @@ describe("OT RCP Driver", () => {
31623179
);
31633180

31643181
//-- mock LEAVE
3182+
vi.spyOn(driver, "sendPeriodicManyToOneRouteRequest").mockImplementationOnce(async () => {}); // no-op for disassociate
31653183
await driver.disassociate(0x91d2, 8118874123826907736n);
31663184

31673185
expect(driver.sourceRouteTable.size).toStrictEqual(5);
@@ -3427,6 +3445,7 @@ describe("OT RCP Driver", () => {
34273445

34283446
expect(lqiTable).toStrictEqual(expectedLQITable.subarray(0, 5 + 3 * 22));
34293447

3448+
vi.spyOn(driver, "sendPeriodicManyToOneRouteRequest").mockImplementationOnce(async () => {}); // no-op for disassociate
34303449
await driver.disassociate(0xcb47, 5149013569626593n);
34313450
expect(driver.deviceTable.size).toStrictEqual(5);
34323451

0 commit comments

Comments
 (0)