domotz-remote-pawn
Version:
Domotz Agent
154 lines (139 loc) • 5.46 kB
JavaScript
/** This file is part of Domotz Agent.
* Copyright (C) 2025 Domotz Ltd
*
* Domotz Agent is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Domotz Agent is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Domotz Agent. If not, see <http://www.gnu.org/licenses/>.
**/
const SPEED_CONVERSION_FACTOR = 1e6;
module.exports.factory = function (q, platform, path, procUtils, fileManager, log) {
var myConsole = log.decorateLogs();
var commandPath = path.join(process.env.DOMOTZ_BIN_DIR, 'domotz_ndt7');
var command = 'domotz_ndt7';
/* Here we have the entire output of the executable
* the output is formatted in the following line:
* multiple dicts, each on a new line --> no array!
* The function generates an array of multiple json dicts
*/
function parseOutput(data) {
var deferred = q.defer();
try {
const parseLine = function (line) {
return JSON.parse(line);
};
const parsedData = data
.split('\n')
.filter(function (line) {
return line.trim() !== ''
})
.map(parseLine)
.filter(function (parsedLine) {
return parsedLine !== null
});
deferred.resolve(parsedData);
} catch (error) {
const errorMessage = 'Parsing error: ' + error.toString();
myConsole.error(errorMessage);
deferred.reject(errorMessage);
}
myConsole.debug('Results ==> %s', data);
return deferred.promise;
}
// The function receives the array of multiple json dicts produced by parseOutput
function formatOutput(ndt7Result) {
for (var i = 0; i < ndt7Result.length; i++) {
var line = ndt7Result[i];
if (line && line.ServerFQDN) {
return {
raw: null,
server: {
url: line.ServerFQDN,
sponsor: 'MLAB-NDT7',
url2: line.ServerFQDN,
},
'speed.upload': line.Upload.Throughput.Value * SPEED_CONVERSION_FACTOR,
'speed.download': line.Download.Throughput.Value * SPEED_CONVERSION_FACTOR,
MaxRTT: '0',
MinRTT: '0',
rttsec: (line.Upload.Latency.Value + line.Download.Latency.Value) / 2,
};
}
}
return {};
}
function isAvailable() {
var execFilePath = commandPath;
if (platform.isWindows()) {
execFilePath += '.exe';
}
if (!fileManager.getFileStats(execFilePath)) {
myConsole.warn('cannot stat %s, ndt7_client is not available', execFilePath);
return false;
}
return true;
}
return {
performSpeedTest: function () {
var execFile = command;
if (platform.isWindows()) {
execFile += '.exe';
}
return procUtils
.exec(execFile + ' -format=json', {onStdout: stdOutCallback})
.then(parseOutput)
.catch(function (error) {
myConsole.error('Error during NDT7 speed test execution %s', error.toString());
return {status: 500, message: error.message};
});
},
formatOutput: formatOutput,
isAvailable: isAvailable,
_parseOutput: parseOutput,
_stdOutCallback: stdOutCallback,
};
function stdOutCallback(data, deferred) {
if (Buffer.isBuffer(data)) {
try {
data = data.toString('utf8');
} catch (error) {
myConsole.error('Error converting buffer to string: %s', error.message);
return;
}
}
myConsole.verbose('JSON output string: %s', data);
// It may happen that in stdout we found more than one JSON dict, not embedded in an array
const parsedData = data
.split('\n')
.filter(function (line) {
return line.trim() !== ''
}) // Skip empty lines
.map(function (line) {
try {
return JSON.parse(line);
} catch (error) {
myConsole.error('Error parsing line: %s', line, error);
return null;
}
})
.filter(function (parsedLine) {
return parsedLine !== null
}); // Remove null entries
parsedData.forEach(function (parsedLine) {
if (parsedLine.Key === 'starting') {
const testType = parsedLine.Value && parsedLine.Value.Test;
if (testType === 'upload' || testType === 'download') {
deferred.notify(testType);
}
}
});
}
};