Include network routes on the map (#1641)

* Initial inclusion of routes on map

* Forgot to include zigbee.js

* Hex string for route address

* Rebaseline to dev

* Cleaner merging of scan arrays

* style fixes
This commit is contained in:
clockbrain authored and Koen Kanters committed 2019-06-16 13:49:15 +02:00
1 parent c48f73e36b
commit 3981c1c6e4
2 files changed
+89 -29

No files matched your search

+3 -1
View File
@@ -89,7 +89,9 @@ class NetworkMap {
*/
topology.filter((e) => (e.ieeeAddr === device.ieeeAddr) || (e.nwkAddr === device.nwkAddr)).forEach((e) => {
const lineStyle = (e.lqi==0) ? `style="dashed", ` : ``;
text += ` "${device.ieeeAddr}" -> "${e.parent}" [`+lineStyle+`label="${e.lqi}"]\n`;
const textRoutes = e.routes.map((r) => `0x${r.toString(16)}`);
const lineLabels = e.lqi + '\\n[' + textRoutes.join(']\\n[') + ']';
text += ` "${e.parent}" -> "${device.ieeeAddr}" [`+lineStyle+`label="${lineLabels}"]\n`;
});
});
+86 -28
View File
@@ -292,51 +292,110 @@ class Zigbee {
networkScan(callback) {
logger.info('Starting network scan...');
const scanList = new Set();
const lqiScanList = new Set();
const rtgScanList = new Set();
const linkMap = [];
const routeMap = [];
const processResponse = (error, rsp, parent) => {
// Gather the lqi and the route info into separate lists and only collate them when done.
const collateMap = () => {
linkMap.sort((a, b) => (a.key > b.key) ? 1 : 0);
logger.debug(`Link map: %j`, linkMap);
logger.debug(`Route map: %j`, routeMap);
// Merge the routes into the linkMap
linkMap.forEach((link) => {
routeMap.filter((e) => e.key === link.key).forEach((e) => {
link.routes.push(e.destAddr);
});
delete link.key;
});
logger.debug(`Merged map: %j`, linkMap);
callback(linkMap);
};
const processLqiResponse = (error, rsp, parent) => {
if (error) {
logger.warn(`Failed network scan for device: '${parent}' with error: '${error}'`);
logger.warn(`Failed network lqi scan for device: '${parent}' with error: '${error}'`);
} else {
if (scanList.has(parent)) {
if (lqiScanList.has(parent)) {
// Haven't processed this one yet
if (rsp && rsp.status === 0 && rsp.neighborlqilist) {
logger.debug(`Network scan ok for: '${parent}' with '${rsp.neighborlqilistcount}' neighbors`);
logger.debug(`lqi scan ok for: '${parent}' with '${rsp.neighborlqilistcount}' neighbors`);
rsp.neighborlqilist.forEach(function(neighbor) {
const key = parent + '|' + neighbor.nwkAddr;
linkMap.push({
parent: parent, ieeeAddr: neighbor.extAddr, nwkAddr: neighbor.nwkAddr,
lqi: neighbor.lqi, depth: neighbor.depth});
key: key, parent: parent, ieeeAddr: neighbor.extAddr, nwkAddr: neighbor.nwkAddr,
lqi: neighbor.lqi, depth: neighbor.depth, routes: []});
});
// Remove from list and if this was the last one return the completed network map
scanList.delete(parent);
if (scanList.size === 0) {
lqiScanList.delete(parent);
if (lqiScanList.size === 0 && rtgScanList === 0) {
logger.info('Network scan completed');
linkMap.sort((a, b) => ((a.parent + '|' + a.ieeeAddr) > (b.parent + '|' + b.ieeeAddr)) ? 1
: (((b.parent + '|' + b.ieeeAddr) > (a.parent + '|' + a.ieeeAddr)) ? -1 : 0));
logger.debug(`Link map (complete): %j`, linkMap);
callback(linkMap);
collateMap();
} else {
logger.debug(`Still waiting for network scans for devices: '${[...scanList].join(' ')}'`);
logger.debug(`Outstanding network lqi scans for devices: '${[...lqiScanList].join(' ')}'`);
logger.debug(`Outstanding network rtg scans for devices: '${[...rtgScanList].join(' ')}'`);
}
} else {
logger.warn(`Empty network scan result for: '${parent}'`);
logger.warn(`Empty network lqi scan result for: '${parent}'`);
}
} else {
// This ieeeAddr has already been removed due to timeout so don't add to result network map
logger.warn(`Ignoring late network scan result for: '${parent}'`);
logger.warn(`Ignoring late network lqi scan result for: '${parent}'`);
}
}
};
// Queue up an lqi scan for coordinator and each router
const processRtgResponse = (error, rsp, parent) => {
if (error) {
logger.warn(`Failed network rtg scan for device: '${parent}' with error: '${error}'`);
} else {
if (rtgScanList.has(parent)) {
// Haven't processed this one yet
if (rsp && rsp.status === 0 && rsp.routingtablelist) {
logger.debug(`rtg scan ok for: '${parent}' with '${rsp.routingtablelistcount}' entries`);
rsp.routingtablelist.forEach(function(route) {
if (route.routeStatus === 0) {
const key = parent + '|' + route.nextHopNwkAddr;
routeMap.push({
key: key, destAddr: route.destNwkAddr});
}
});
// Remove from list and if this was the last one return the completed network map
rtgScanList.delete(parent);
if (lqiScanList.size === 0 && rtgScanList.size === 0) {
logger.info('Network scan completed');
collateMap();
} else {
logger.debug(`Outstanding network lqi scans for devices: '${[...lqiScanList].join(' ')}'`);
logger.debug(`Outstanding network rtg scans for devices: '${[...rtgScanList].join(' ')}'`);
}
} else {
logger.warn(`Empty network rtg scan result for: '${parent}'`);
}
} else {
// This ieeeAddr has already been removed due to timeout so don't add to result network map
logger.warn(`Ignoring late network rtg scan result for: '${parent}'`);
}
}
};
// Queue up an lqi scan and an rtg for coordinator and each router
this.getScanable().forEach((dev) => {
logger.debug(`Queing network scan for device: '${dev.ieeeAddr}'`);
scanList.add(dev.ieeeAddr);
logger.debug(`Queing network scans for device: '${dev.ieeeAddr}'`);
lqiScanList.add(dev.ieeeAddr);
this.queue.push(dev.ieeeAddr, (queueCallback) => {
this.shepherd.controller.request('ZDO', 'mgmtLqiReq', {dstaddr: dev.nwkAddr, startindex: 0},
(error, rsp, parent) => {
processResponse(error, rsp, dev.ieeeAddr);
processLqiResponse(error, rsp, dev.ieeeAddr);
queueCallback(error);
});
});
rtgScanList.add(dev.ieeeAddr);
this.queue.push(dev.ieeeAddr, (queueCallback) => {
this.shepherd.controller.request('ZDO', 'mgmtRtgReq', {dstaddr: dev.nwkAddr, startindex: 0},
(error, rsp, parent) => {
processRtgResponse(error, rsp, dev.ieeeAddr);
queueCallback(error);
});
});
@@ -344,18 +403,17 @@ class Zigbee {
// Wait for all device scans before forcing map with whatever results are already in
setTimeout(() => {
if (scanList.size === 0) {
if (lqiScanList.size === 0 && rtgScanList.size === 0) {
logger.info('Network scan timeout no outstanding requests');
} else {
logger.warn(`Network scan timeout, skipping outstanding scans for '${[...scanList].join(' ')}'`);
logger.warn(`Network scan timeout, skipping outstanding lqi scans for '${[...lqiScanList].join(' ')}'`);
logger.warn(`Network scan timeout, skipping outstanding rtg scans for '${[...rtgScanList].join(' ')}'`);
// Clear remaining devices so they don't process when/if they eventually complete
scanList.clear();
logger.debug(`Link map (timeout): %j`, linkMap);
linkMap.sort((a, b) => ((a.parent + '|' + a.ieeeAddr) > (b.parent + '|' + b.ieeeAddr)) ? 1
: (((b.parent + '|' + b.ieeeAddr) > (a.parent + '|' + a.ieeeAddr)) ? -1 : 0));
callback(linkMap);
lqiScanList.clear();
rtgScanList.clear();
collateMap();
}
}, scanList.size * 1000);
}, lqiScanList.size * 1000);
}
getEndpoint(ieeeAddr, ep) {