mirror of
https://github.com/Hypfer/Valetudo.git
synced 2025-10-26 11:27:27 +00:00
refactor(core): Move map polling orchestration logic into ValetudoRobot base class
This commit is contained in:
parent
453ac70f53
commit
b12fb03136
@ -7,9 +7,13 @@ const entities = require("../entities");
|
||||
const ErrorStateValetudoEvent = require("../valetudo_events/events/ErrorStateValetudoEvent");
|
||||
const Logger = require("../Logger");
|
||||
const NotImplementedError = require("./NotImplementedError");
|
||||
const Semaphore = require("semaphore");
|
||||
const Tools = require("../utils/Tools");
|
||||
const {ConsumableStateAttribute, StatusStateAttribute} = require("../entities/state/attributes");
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
*/
|
||||
class ValetudoRobot {
|
||||
/**
|
||||
*
|
||||
@ -33,6 +37,10 @@ class ValetudoRobot {
|
||||
hugeMap: false
|
||||
};
|
||||
|
||||
this.mapPollMutex = Semaphore(1);
|
||||
this.mapPollTimeout = undefined;
|
||||
this.postActiveStateMapPollCooldownCredits = 0;
|
||||
|
||||
this.initInternalSubscriptions();
|
||||
}
|
||||
|
||||
@ -133,18 +141,18 @@ class ValetudoRobot {
|
||||
|
||||
/*
|
||||
This will be displayed only once after a map larger than 120 m² has been uploaded to a new Valetudo process
|
||||
|
||||
|
||||
It should serve as an unobtrusive reminder that while you can use Valetudo in a commercial environment
|
||||
without any limitations whatsoever, doing so and saving money because of that without giving anything
|
||||
back is simply not a very nice thing to do.
|
||||
|
||||
|
||||
While there would be the option to introduce something like license keys or a paid version, not only
|
||||
would that be futile in an open source project, but it would also likely harm perfectly fine non-commercial
|
||||
uses of Valetudo in e.g., your local hackerspace, art installations, etc.
|
||||
|
||||
|
||||
In the end, I'd rather have some people take advantage of this permissive system than making
|
||||
the project worse for all of its users to prevent that.
|
||||
|
||||
|
||||
You're welcome
|
||||
*/
|
||||
Logger.info("Based on your map size, it looks like you might be using Valetudo in a commercial environment.");
|
||||
@ -164,6 +172,86 @@ class ValetudoRobot {
|
||||
//intentional
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @protected
|
||||
* @abstract
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
async executeMapPoll() {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @param {any} pollResponse Implementation specific
|
||||
* @return {number} seconds
|
||||
*/
|
||||
determineNextMapPollInterval(pollResponse) {
|
||||
let repollSeconds = ValetudoRobot.MAP_POLLING_INTERVALS.DEFAULT;
|
||||
|
||||
let statusStateAttribute = this.state.getFirstMatchingAttribute({
|
||||
attributeClass: StatusStateAttribute.name
|
||||
});
|
||||
|
||||
|
||||
let isActive = false;
|
||||
|
||||
if (statusStateAttribute && statusStateAttribute.isActiveState) {
|
||||
isActive = true;
|
||||
this.postActiveStateMapPollCooldownCredits = 3;
|
||||
}
|
||||
|
||||
if (!isActive && this.postActiveStateMapPollCooldownCredits > 0) {
|
||||
// Pretend that we're still in an active state to ensure that we catch map updates e.g. after docking
|
||||
isActive = true;
|
||||
this.postActiveStateMapPollCooldownCredits--;
|
||||
}
|
||||
|
||||
|
||||
if (isActive) {
|
||||
repollSeconds = ValetudoRobot.MAP_POLLING_INTERVALS.ACTIVE;
|
||||
|
||||
if (this.flags.lowmemHost) {
|
||||
repollSeconds *= 2;
|
||||
}
|
||||
if (this.flags.hugeMap) {
|
||||
repollSeconds *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return repollSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @returns {void}
|
||||
*/
|
||||
pollMap() {
|
||||
this.mapPollMutex.take(() => {
|
||||
let repollSeconds = ValetudoRobot.MAP_POLLING_INTERVALS.DEFAULT;
|
||||
|
||||
// Clear pending timeout, since we’re starting a new poll right now.
|
||||
if (this.mapPollTimeout) {
|
||||
clearTimeout(this.mapPollTimeout);
|
||||
|
||||
this.mapPollTimeout = undefined;
|
||||
}
|
||||
|
||||
this.executeMapPoll().then((response) => {
|
||||
repollSeconds = this.determineNextMapPollInterval(response);
|
||||
}).catch(() => {
|
||||
repollSeconds = ValetudoRobot.MAP_POLLING_INTERVALS.ERROR;
|
||||
}).finally(() => {
|
||||
this.mapPollTimeout = setTimeout(() => {
|
||||
this.pollMap();
|
||||
}, repollSeconds * 1000);
|
||||
|
||||
this.mapPollMutex.leave();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async shutdown() {
|
||||
//intentional
|
||||
@ -283,6 +371,13 @@ ValetudoRobot.WELL_KNOWN_PROPERTIES = {
|
||||
FIRMWARE_VERSION: "firmwareVersion"
|
||||
};
|
||||
|
||||
const HUGE_MAP_THRESHOLD = 145 * 10000; //145m² in cm²
|
||||
ValetudoRobot.MAP_POLLING_INTERVALS = Object.freeze({
|
||||
DEFAULT: 60,
|
||||
ACTIVE: 2,
|
||||
ERROR: 30
|
||||
});
|
||||
|
||||
|
||||
const HUGE_MAP_THRESHOLD = 145 * 10_000; //145m² in cm²
|
||||
|
||||
module.exports = ValetudoRobot;
|
||||
|
||||
@ -9,18 +9,17 @@ const Semaphore = require("semaphore");
|
||||
|
||||
const Dummycloud = require("../miio/Dummycloud");
|
||||
const Logger = require("../Logger");
|
||||
const MiioDummycloudNotConnectedError = require("../miio/MiioDummycloudNotConnectedError");
|
||||
const MiioSocket = require("../miio/MiioSocket");
|
||||
const NotImplementedError = require("../core/NotImplementedError");
|
||||
const RetryWrapper = require("../miio/RetryWrapper");
|
||||
const ValetudoRobot = require("../core/ValetudoRobot");
|
||||
|
||||
const entities = require("../entities");
|
||||
const MiioDummycloudNotConnectedError = require("../miio/MiioDummycloudNotConnectedError");
|
||||
const stateAttrs = entities.state.attributes;
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
*/
|
||||
class MiioValetudoRobot extends ValetudoRobot {
|
||||
/**
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {import("../Configuration")} options.config
|
||||
* @param {import("../ValetudoEventStore")} options.valetudoEventStore
|
||||
@ -33,7 +32,7 @@ class MiioValetudoRobot extends ValetudoRobot {
|
||||
this.robotConfig = this.config.get("robot");
|
||||
this.implConfig = (this.robotConfig && this.robotConfig.implementationSpecificConfig) ?? {};
|
||||
|
||||
this.ip = this.implConfig.ip ?? "127.0.0.1";
|
||||
this.ip = this.implConfig["ip"] ?? "127.0.0.1";
|
||||
this.embeddedDummycloudIp = this.implConfig["dummycloudIp"] ?? "127.0.0.1";
|
||||
this.dummycloudBindIp = this.implConfig["dummycloudBindIp"] ?? (this.config.get("embedded") ? "127.0.0.1" : "0.0.0.0");
|
||||
|
||||
@ -73,9 +72,6 @@ class MiioValetudoRobot extends ValetudoRobot {
|
||||
});
|
||||
|
||||
this.fdsUploadSemaphore = Semaphore(2);
|
||||
this.mapPollMutex = Semaphore(1);
|
||||
this.mapPollTimeout = undefined;
|
||||
this.postActiveStateMapPollCooldownCredits = 0;
|
||||
this.expressApp = express();
|
||||
|
||||
this.fdsMockServer = http.createServer(this.expressApp);
|
||||
@ -448,90 +444,10 @@ class MiioValetudoRobot extends ValetudoRobot {
|
||||
*/
|
||||
onCloudConnected() {
|
||||
Logger.info("Dummycloud connected");
|
||||
// start polling the map after a brief delay of 3.5s
|
||||
// start polling the map after a brief delay of 5s
|
||||
setTimeout(() => {
|
||||
return this.pollMap();
|
||||
}, 3500);
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @returns {void}
|
||||
*/
|
||||
pollMap() {
|
||||
this.mapPollMutex.take(() => {
|
||||
let repollSeconds = MiioValetudoRobot.MAP_POLLING_INTERVALS.DEFAULT;
|
||||
|
||||
// Clear pending timeout, since we’re starting a new poll right now.
|
||||
if (this.mapPollTimeout) {
|
||||
clearTimeout(this.mapPollTimeout);
|
||||
|
||||
this.mapPollTimeout = undefined;
|
||||
}
|
||||
|
||||
this.executeMapPoll().then((response) => {
|
||||
repollSeconds = this.determineNextMapPollInterval(response);
|
||||
}).catch(() => {
|
||||
repollSeconds = MiioValetudoRobot.MAP_POLLING_INTERVALS.ERROR;
|
||||
}).finally(() => {
|
||||
this.mapPollTimeout = setTimeout(() => {
|
||||
this.pollMap();
|
||||
}, repollSeconds * 1000);
|
||||
|
||||
this.mapPollMutex.leave();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @protected
|
||||
* @abstract
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
async executeMapPoll() {
|
||||
throw new NotImplementedError();
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @param {any} pollResponse Implementation specific
|
||||
* @return {number} seconds
|
||||
*/
|
||||
determineNextMapPollInterval(pollResponse) {
|
||||
let repollSeconds = MiioValetudoRobot.MAP_POLLING_INTERVALS.DEFAULT;
|
||||
|
||||
let StatusStateAttribute = this.state.getFirstMatchingAttribute({
|
||||
attributeClass: stateAttrs.StatusStateAttribute.name
|
||||
});
|
||||
|
||||
|
||||
let isActive = false;
|
||||
|
||||
if (StatusStateAttribute && StatusStateAttribute.isActiveState) {
|
||||
isActive = true;
|
||||
this.postActiveStateMapPollCooldownCredits = 3;
|
||||
}
|
||||
|
||||
if (!isActive && this.postActiveStateMapPollCooldownCredits > 0) {
|
||||
// Pretend that we're still in an active state to ensure that we catch map updates e.g. after docking
|
||||
isActive = true;
|
||||
this.postActiveStateMapPollCooldownCredits--;
|
||||
}
|
||||
|
||||
|
||||
if (isActive) {
|
||||
repollSeconds = MiioValetudoRobot.MAP_POLLING_INTERVALS.ACTIVE;
|
||||
|
||||
if (this.flags.lowmemHost) {
|
||||
repollSeconds *= 2;
|
||||
}
|
||||
if (this.flags.hugeMap) {
|
||||
repollSeconds *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return repollSeconds;
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -630,10 +546,4 @@ class MiioValetudoRobot extends ValetudoRobot {
|
||||
const DEVICE_CONF_KEY_VALUE_REGEX = /^(?<key>[A-Za-z\d:.]+)=(?<value>[A-Za-z\d:.]+)$/;
|
||||
const MAX_UPLOAD_FILESIZE = 4 * 1024 * 1024; // 4 MiB
|
||||
|
||||
MiioValetudoRobot.MAP_POLLING_INTERVALS = Object.freeze({
|
||||
DEFAULT: 60,
|
||||
ACTIVE: 2,
|
||||
ERROR: 30
|
||||
});
|
||||
|
||||
module.exports = MiioValetudoRobot;
|
||||
|
||||
@ -17,7 +17,6 @@ const MiioValetudoRobot = require("../MiioValetudoRobot");
|
||||
const MopAttachmentReminderValetudoEvent = require("../../valetudo_events/events/MopAttachmentReminderValetudoEvent");
|
||||
const PendingMapChangeValetudoEvent = require("../../valetudo_events/events/PendingMapChangeValetudoEvent");
|
||||
const ValetudoMap = require("../../entities/map/ValetudoMap");
|
||||
const ValetudoRobot = require("../../core/ValetudoRobot");
|
||||
const ValetudoRobotError = require("../../entities/core/ValetudoRobotError");
|
||||
|
||||
const stateAttrs = entities.state.attributes;
|
||||
@ -284,7 +283,7 @@ class DreameValetudoRobot extends MiioValetudoRobot {
|
||||
const firmwareVersion = this.getFirmwareVersion();
|
||||
|
||||
if (firmwareVersion.valid) {
|
||||
ourProps[ValetudoRobot.WELL_KNOWN_PROPERTIES.FIRMWARE_VERSION] = firmwareVersion.arm;
|
||||
ourProps[DreameValetudoRobot.WELL_KNOWN_PROPERTIES.FIRMWARE_VERSION] = firmwareVersion.arm;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -94,7 +94,7 @@ class MockRobot extends ValetudoRobot {
|
||||
getProperties() {
|
||||
const superProps = super.getProperties();
|
||||
const ourProps = {
|
||||
[ValetudoRobot.WELL_KNOWN_PROPERTIES.FIRMWARE_VERSION]: Tools.GET_VALETUDO_VERSION()
|
||||
[MockRobot.WELL_KNOWN_PROPERTIES.FIRMWARE_VERSION]: Tools.GET_VALETUDO_VERSION()
|
||||
};
|
||||
|
||||
return Object.assign(
|
||||
|
||||
@ -13,7 +13,6 @@ const MiioDummycloudNotConnectedError = require("../../miio/MiioDummycloudNotCon
|
||||
const MiioValetudoRobot = require("../MiioValetudoRobot");
|
||||
const PendingMapChangeValetudoEvent = require("../../valetudo_events/events/PendingMapChangeValetudoEvent");
|
||||
const ValetudoMap = require("../../entities/map/ValetudoMap");
|
||||
const ValetudoRobot = require("../../core/ValetudoRobot");
|
||||
const ValetudoRobotError = require("../../entities/core/ValetudoRobotError");
|
||||
const ValetudoSelectionPreset = require("../../entities/core/ValetudoSelectionPreset");
|
||||
|
||||
@ -485,7 +484,7 @@ class RoborockValetudoRobot extends MiioValetudoRobot {
|
||||
repollSeconds += 1;
|
||||
} else {
|
||||
// This fixes the map not being available on boot for another 60 seconds
|
||||
repollSeconds = MiioValetudoRobot.MAP_POLLING_INTERVALS.ACTIVE;
|
||||
repollSeconds = RoborockValetudoRobot.MAP_POLLING_INTERVALS.ACTIVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -596,7 +595,7 @@ class RoborockValetudoRobot extends MiioValetudoRobot {
|
||||
const firmwareVersion = this.getFirmwareVersion();
|
||||
|
||||
if (firmwareVersion) {
|
||||
ourProps[ValetudoRobot.WELL_KNOWN_PROPERTIES.FIRMWARE_VERSION] = firmwareVersion;
|
||||
ourProps[RoborockValetudoRobot.WELL_KNOWN_PROPERTIES.FIRMWARE_VERSION] = firmwareVersion;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ const Logger = require("../../Logger");
|
||||
const miioCapabilities = require("../common/miioCapabilities");
|
||||
const MiioValetudoRobot = require("../MiioValetudoRobot");
|
||||
const ThreeIRobotixMapParser = require("../3irobotix/ThreeIRobotixMapParser");
|
||||
const ValetudoRobot = require("../../core/ValetudoRobot");
|
||||
const ValetudoRobotError = require("../../entities/core/ValetudoRobotError");
|
||||
const ValetudoSelectionPreset = require("../../entities/core/ValetudoSelectionPreset");
|
||||
|
||||
@ -561,7 +560,7 @@ class ViomiValetudoRobot extends MiioValetudoRobot {
|
||||
const firmwareVersion = this.getFirmwareVersion();
|
||||
|
||||
if (firmwareVersion) {
|
||||
ourProps[ValetudoRobot.WELL_KNOWN_PROPERTIES.FIRMWARE_VERSION] = firmwareVersion;
|
||||
ourProps[ViomiValetudoRobot.WELL_KNOWN_PROPERTIES.FIRMWARE_VERSION] = firmwareVersion;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user