mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-08-23 11:09:52 +00:00
Network map rewrite using queue (#1626)
* Initial rewrite of network map scan using queue * Rename routerList to scanList * eslist fixes * More eslint fixes * Timeout now dynamic based on number of scanable devices
This commit is contained in:
+58
-49
@@ -292,61 +292,70 @@ class Zigbee {
|
||||
|
||||
networkScan(callback) {
|
||||
logger.info('Starting network scan...');
|
||||
const scanList = new Set();
|
||||
const linkMap = [];
|
||||
|
||||
Promise.delay = function(t, val) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve.bind(null, val), t);
|
||||
});
|
||||
};
|
||||
|
||||
Promise.raceAll = function(promises, timeoutTime, timeoutVal) {
|
||||
return Promise.all(promises.map((p) => {
|
||||
return Promise.race([p, Promise.delay(timeoutTime, timeoutVal)]);
|
||||
}));
|
||||
};
|
||||
|
||||
const processResponse = function(parent) {
|
||||
logger.debug(`Scanning device: '${parent}'`);
|
||||
return function(data) {
|
||||
const linkSet = [];
|
||||
return new Promise((resolve) => {
|
||||
logger.debug(`Processing scan for: '${parent}'`);
|
||||
if (data) {
|
||||
data.forEach(function(devinfo) {
|
||||
devinfo.parent = parent;
|
||||
linkSet.push(devinfo);
|
||||
const processResponse = (error, rsp, parent) => {
|
||||
if (error) {
|
||||
logger.warn(`Failed network scan for device: '${parent}' with error: '${error}'`);
|
||||
} else {
|
||||
if (scanList.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`);
|
||||
rsp.neighborlqilist.forEach(function(neighbor) {
|
||||
linkMap.push({
|
||||
parent: parent, ieeeAddr: neighbor.extAddr, nwkAddr: neighbor.nwkAddr,
|
||||
lqi: neighbor.lqi, depth: neighbor.depth});
|
||||
});
|
||||
// Remove from list and if this was the last one return the completed network map
|
||||
scanList.delete(parent);
|
||||
if (scanList.size === 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);
|
||||
} else {
|
||||
logger.debug(`Still waiting for network scans for devices: '${[...scanList].join(' ')}'`);
|
||||
}
|
||||
} else {
|
||||
logger.warn(`Empty network scan result for: '${parent}'`);
|
||||
}
|
||||
resolve(linkSet);
|
||||
logger.debug(`Processed device: '${parent}', linkSet: %j`, linkSet);
|
||||
});
|
||||
};
|
||||
} 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}'`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const allScans = this.getScanable().map((dev) => {
|
||||
logger.debug(`Preparing asynch network scan for '${dev.ieeeAddr}'`);
|
||||
// Delay the start of each device scan by a random time to avoid network congestion
|
||||
return Promise.delay(Math.random()*3000, this.shepherd.lqi(dev.ieeeAddr))
|
||||
.then(processResponse(dev.ieeeAddr))
|
||||
.catch(() => {
|
||||
return new Promise((resolve) => []);
|
||||
});
|
||||
}, this);
|
||||
logger.debug('All network map promises created');
|
||||
// Collect all lqi scan results but timeout after specified miliseconds if any haven't completed
|
||||
Promise.raceAll(allScans, 8000, []).then((linkSets) => {
|
||||
// Assemble the individual scan results then sort by ieeeAddr to generate consistent maps
|
||||
const linkMap = [].concat(...linkSets)
|
||||
.sort((a, b) => ((a.parent + '|' + a.ieeeAddr) > (b.parent + '|' + b.ieeeAddr)) ? 1
|
||||
: (((b.parent + '|' + b.ieeeAddr) > (a.parent + '|' + a.ieeeAddr)) ? -1 : 0));
|
||||
logger.info('Network scan completed');
|
||||
logger.debug(`Link map: %j`, linkMap);
|
||||
callback(linkMap);
|
||||
})
|
||||
.catch(function(result) {
|
||||
logger.info(`Network scan failed: '${result}'`);
|
||||
callback([]);
|
||||
// Queue up an lqi scan for coordinator and each router
|
||||
this.getScanable().forEach((dev) => {
|
||||
logger.debug(`Queing network scan for device: '${dev.ieeeAddr}'`);
|
||||
scanList.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);
|
||||
queueCallback(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Wait for all device scans before forcing map with whatever results are already in
|
||||
setTimeout(() => {
|
||||
if (scanList.size === 0) {
|
||||
logger.info('Network scan timeout no outstanding requests');
|
||||
} else {
|
||||
logger.warn(`Network scan timeout, skipping outstanding scans for '${[...scanList].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);
|
||||
}
|
||||
}, scanList.size * 1000);
|
||||
}
|
||||
|
||||
getEndpoint(ieeeAddr, ep) {
|
||||
|
||||
Reference in New Issue
Block a user