You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
inav-configurator/js/serial_queue.js

211 lines
6.2 KiB
JavaScript

'use strict';
var helper = helper || {};
helper.mspQueue = (function (serial, MSP) {
var publicScope = {},
privateScope = {};
privateScope.handlerFrequency = 100;
privateScope.balancerFrequency = 10;
privateScope.loadFilter = new classes.SimpleSmoothFilter(0.5, 0.996);
privateScope.roundtripFilter = new classes.SimpleSmoothFilter(20, 0.996);
privateScope.hardwareRoundtripFilter = new classes.SimpleSmoothFilter(5, 0.996);
privateScope.targetLoad = 1.5;
privateScope.statusDropFactor = 0.75;
privateScope.currentLoad = 0;
privateScope.loadPid = {
gains: {
P: 10,
I: 4,
D: 2
},
Iterm: 0,
ItermLimit: 85,
previousError: 0,
output: {
min: 0,
max: 97,
minThreshold: 0
}
};
privateScope.dropRatio = 0;
publicScope.computeDropRatio = function () {
var error = privateScope.currentLoad - privateScope.targetLoad;
var Pterm = error * privateScope.loadPid.gains.P,
Dterm = (error - privateScope.loadPid.previousError) * privateScope.loadPid.gains.D;
privateScope.loadPid.previousError = error;
privateScope.loadPid.Iterm += error * privateScope.loadPid.gains.I;
if (privateScope.loadPid.Iterm > privateScope.loadPid.ItermLimit) {
privateScope.loadPid.Iterm = privateScope.loadPid.ItermLimit;
} else if (privateScope.loadPid.Iterm < -privateScope.loadPid.ItermLimit) {
privateScope.loadPid.Iterm = -privateScope.loadPid.ItermLimit;
}
privateScope.dropRatio = Pterm + privateScope.loadPid.Iterm + Dterm;
if (privateScope.dropRatio < privateScope.loadPid.output.minThreshold) {
privateScope.dropRatio = privateScope.loadPid.output.min;
}
if (privateScope.dropRatio > privateScope.loadPid.output.max) {
privateScope.dropRatio = privateScope.loadPid.output.max;
}
};
publicScope.getDropRatio = function () {
return privateScope.dropRatio;
};
privateScope.queue = [];
privateScope.portInUse = false;
/**
* This method is periodically executed and moves MSP request
* from a queue to serial port. This allows to throttle requests,
* adjust rate of new frames being sent and prohibit situation in which
* serial port is saturated, virtually overloaded, with outgoing data
*
* This also implements serial port sharing problem: only 1 frame can be transmitted
* at once
*
* MSP class no longer implements blocking, it is queue responsibility
*/
publicScope.executor = function () {
privateScope.loadFilter.apply(privateScope.queue.length);
/*
* if port is blocked or there is no connection, do not process the queue
*/
if (privateScope.portInUse || serial.connectionId === false) {
return false;
}
var request = privateScope.get();
if (request !== undefined) {
/*
* Lock serial port as being in use right now
*/
privateScope.portInUse = true;
request.timer = setTimeout(function () {
console.log('MSP data request timed-out: ' + request.code);
/*
* Remove current callback
*/
MSP.removeCallback(request.code);
/*
* Create new entry in the queue
*/
publicScope.put(request);
}, serial.getTimeout());
if (request.sentOn === null) {
request.sentOn = new Date().getTime();
}
/*
* Set receive callback here
*/
MSP.putCallback(request);
/*
* Send data to serial port
*/
serial.send(request.messageBody, function (sendInfo) {
if (sendInfo.bytesSent == request.messageBody.byteLength) {
/*
* message has been sent, check callbacks and free resource
*/
if (request.onSend) {
request.onSend();
}
privateScope.portInUse = false;
}
});
}
};
privateScope.get = function () {
return privateScope.queue.shift();
};
publicScope.flush = function () {
privateScope.queue = [];
};
publicScope.freeSerialPort = function () {
privateScope.portInUse = false;
};
publicScope.put = function (mspRequest) {
privateScope.queue.push(mspRequest);
};
publicScope.getLength = function () {
return privateScope.queue.length;
};
/**
* 1s MSP load computed as number of messages in a queue in given period
* @returns {number}
*/
publicScope.getLoad = function () {
return privateScope.loadFilter.get();
};
publicScope.getRoundtrip = function () {
return privateScope.roundtripFilter.get();
};
/**
*
* @param {number} number
*/
publicScope.putRoundtrip = function (number) {
privateScope.roundtripFilter.apply(number);
};
publicScope.getHardwareRoundtrip = function () {
return privateScope.hardwareRoundtripFilter.get();
};
/**
*
* @param {number} number
*/
publicScope.putHardwareRoundtrip = function (number) {
privateScope.hardwareRoundtripFilter.apply(number);
};
publicScope.balancer = function () {
privateScope.currentLoad = privateScope.loadFilter.get();
helper.mspQueue.computeDropRatio();
};
publicScope.shouldDrop = function () {
return (Math.round(Math.random()*100) < privateScope.dropRatio);
};
publicScope.shouldDropStatus = function () {
return (Math.round(Math.random()*100) < (privateScope.dropRatio * privateScope.statusDropFactor));
};
setInterval(publicScope.executor, Math.round(1000 / privateScope.handlerFrequency));
setInterval(publicScope.balancer, Math.round(1000 / privateScope.balancerFrequency));
return publicScope;
})(serial, MSP);