From 6f34887c948f25b45d5c8e4f545cdd8eb16e24ac Mon Sep 17 00:00:00 2001 From: Peli de Halleux Date: Tue, 17 Sep 2019 14:30:02 -0700 Subject: [PATCH] Safepolling (#915) * headstart on safe polling * poke in sensors * more poking * typo --- libs/color-sensor/color.ts | 4 + libs/core/buttons.ts | 13 ++- libs/core/input.ts | 129 +++++++++++++++++---------- libs/core/linux.cpp | 5 -- libs/gyro-sensor/gyro.ts | 2 + libs/infrared-sensor/ir.ts | 2 + libs/touch-sensor/touch.ts | 2 + libs/ultrasonic-sensor/ultrasonic.ts | 1 + 8 files changed, 105 insertions(+), 53 deletions(-) diff --git a/libs/color-sensor/color.ts b/libs/color-sensor/color.ts index 7824f8e0..411bc988 100644 --- a/libs/color-sensor/color.ts +++ b/libs/color-sensor/color.ts @@ -179,6 +179,7 @@ namespace sensors { //% group="Color Sensor" //% blockGap=8 color(): ColorSensorColor { + this.poke(); this.setMode(ColorSensorMode.Color) return this.getNumber(NumberFormat.UInt8LE, 0) } @@ -196,6 +197,7 @@ namespace sensors { //% group="Color Sensor" //% blockGap=8 rgbRaw(): number[] { + this.poke(); this.setMode(ColorSensorMode.RgbRaw); return [this.getNumber(NumberFormat.UInt16LE, 0), this.getNumber(NumberFormat.UInt16LE, 2), this.getNumber(NumberFormat.UInt16LE, 4)]; } @@ -249,6 +251,7 @@ namespace sensors { //% weight=87 blockGap=8 //% group="Color Sensor" light(mode: LightIntensityMode) { + this.poke(); this.setMode(mode) switch(mode) { case LightIntensityMode.ReflectedRaw: @@ -279,6 +282,7 @@ namespace sensors { */ //% reflectedLightRaw(): number { + this.poke(); this.setMode(ColorSensorMode.RefRaw); return this.getNumber(NumberFormat.UInt16LE, 0); } diff --git a/libs/core/buttons.ts b/libs/core/buttons.ts index 8bf5638b..134ef4e7 100644 --- a/libs/core/buttons.ts +++ b/libs/core/buttons.ts @@ -55,6 +55,10 @@ namespace brick { this._wasPressed = false } + protected poke() { + + } + //% hidden _update(curr: boolean) { if (this == null) return @@ -85,6 +89,7 @@ namespace brick { //% group="Buttons" //% button.fieldEditor="brickbuttons" isPressed() { + this.poke(); return this._isPressed } @@ -102,6 +107,7 @@ namespace brick { //% group="Buttons" //% button.fieldEditor="brickbuttons" wasPressed() { + this.poke(); const r = this._wasPressed this._wasPressed = false return r @@ -144,6 +150,7 @@ namespace brick { namespace brick { let btnsMM: MMap let buttons: DevButton[] + let buttonPoller: sensors.internal.Poller; export namespace internal { export function getBtnsMM() { @@ -167,7 +174,7 @@ namespace brick { btnsMM = control.mmap("/dev/lms_ui", DAL.NUM_BUTTONS, 0) if (!btnsMM) control.fail("no buttons?") buttons = [] - sensors.internal.unsafePollForChanges(50, readButtons, (prev, curr) => { + buttonPoller = new sensors.internal.Poller(50, readButtons, (prev, curr) => { for (let b of buttons) b._update(!!(curr & b.mask)) }) @@ -182,6 +189,10 @@ namespace brick { initBtns() buttons.push(this) } + + protected poke() { + buttonPoller.poke(); + } } initBtns() // always ON as it handles ESCAPE button diff --git a/libs/core/input.ts b/libs/core/input.ts index 50908954..a33f8310 100644 --- a/libs/core/input.ts +++ b/libs/core/input.ts @@ -1,28 +1,51 @@ namespace sensors.internal { - //% shim=pxt::unsafePollForChanges - export function unsafePollForChanges( - periodMs: number, - query: () => number, - changeHandler: (prev: number, curr: number) => void - ) { - // This is implemented in C++ without blocking the regular JS when query() is runnning - // which is generally unsafe. Query should not update globally visible state, and cannot - // call any yielding functions, like sleep(). + export class Poller { + private query: () => number; + private update: (previous: number, current: number) => void; + public interval: number; - // This is implementation for the simulator. + private previousValue: number; + private currentValue: number; + private lastQuery: number; // track down the last time we did a query/update cycle + private lastPause: number; // track down the last time we pause in the sensor polling loop - control.runInParallel(() => { - let prev = query() - changeHandler(prev, prev) - while (true) { - pause(periodMs) - let curr = query() - if (prev !== curr) { - changeHandler(prev, curr) - prev = curr - } + constructor(interval: number, query: () => number, update: (previous: number, current: number) => void) { + this.interval = interval | 0; + this.query = query; + this.update = update; + + this.poll(); + } + + poke(): void { + const now = control.millis(); + if (now - this.lastQuery >= this.interval * 2) + this.queryAndUpdate(); // sensor poller is not allowed to run + if (now - this.lastPause >= this.interval * 5) + pause(1); // allow events to trigger + } + + private queryAndUpdate() { + this.lastQuery = control.millis(); + this.currentValue = this.query(); + if (this.previousValue != this.currentValue) { + this.update(this.previousValue, this.currentValue); + this.previousValue = this.currentValue; } - }) + } + + private poll() { + control.runInBackground(() => { + this.lastQuery = this.lastPause = control.millis(); + this.previousValue = this.currentValue = this.query(); + this.update(this.previousValue, this.currentValue); + while (true) { + this.lastPause = control.millis(); + pause(this.interval); + this.queryAndUpdate(); + } + }) + } } export function bufferToString(buf: Buffer): string { @@ -38,6 +61,7 @@ namespace sensors.internal { let IICMM: MMap let powerMM: MMap let devcon: Buffer + let devPoller: Poller let sensorInfos: SensorInfo[]; let batteryInfo: { @@ -55,6 +79,7 @@ namespace sensors.internal { connType: number devType: number iicid: string + poller: Poller; constructor(p: number) { this.port = p @@ -62,6 +87,20 @@ namespace sensors.internal { this.devType = DAL.DEVICE_TYPE_NONE this.iicid = '' this.sensors = [] + this.poller = new Poller(50, () => this.query(), (prev, curr) => this.update(prev, curr)); + } + + poke() { + this.poller.poke(); + } + + private query() { + if (this.sensor) return this.sensor._query(); + return 0; + } + + private update(prev: number, curr: number) { + if (this.sensor) this.sensor._update(prev, curr) } } @@ -82,20 +121,11 @@ namespace sensors.internal { powerMM = control.mmap("/dev/lms_power", 2, 0) - unsafePollForChanges(500, - () => { return hashDevices(); }, + devPoller = new Poller(500, () => { return hashDevices(); }, (prev, curr) => { detectDevices(); }); - sensorInfos.forEach(info => { - unsafePollForChanges(50, () => { - if (info.sensor) return info.sensor._query() - return 0 - }, (prev, curr) => { - if (info.sensor) info.sensor._update(prev, curr) - }) - }) - } + } export function getActiveSensors(): Sensor[] { init(); @@ -164,13 +194,13 @@ namespace sensors.internal { batteryVMax = ACCU_INDICATOR_HIGH; } } - batteryInfo = { - CinCnt: CinCnt, + batteryInfo = { + CinCnt: CinCnt, CoutCnt: CoutCnt, VinCnt: VinCnt }; // update in background - control.runInParallel(() => forever(updateBatteryInfo)); + control.runInParallel(() => forever(updateBatteryInfo)); } else { CinCnt = batteryInfo.CinCnt = ((batteryInfo.CinCnt * (AVR_CIN - 1)) + CinCnt) / AVR_CIN; CoutCnt = batteryInfo.CoutCnt = ((batteryInfo.CoutCnt * (AVR_COUT - 1)) + CoutCnt) / AVR_COUT; @@ -178,11 +208,11 @@ namespace sensors.internal { } } - export function getBatteryInfo(): { - level: number; - Ibatt: number, - Vbatt: number, - Imotor: number + export function getBatteryInfo(): { + level: number; + Ibatt: number, + Vbatt: number, + Imotor: number } { init(); if (!batteryInfo) updateBatteryInfo(); @@ -225,13 +255,13 @@ void cUiUpdatePower(void) #endif } */ - const CinV = CNT_V(CinCnt) / AMP_CIN; - const Vbatt = CNT_V(VinCnt) / AMP_VIN + CinV + VCE; - const Ibatt = CinV / SHUNT_IN; - const CoutV = CNT_V(CoutCnt) / AMP_COUT; - const Imotor = CoutV / SHUNT_OUT; - const level = Math.max(0, Math.min(100, Math.floor((Vbatt * 1000.0 - batteryVMin) - / (batteryVMax - batteryVMin) * 100))); + const CinV = CNT_V(CinCnt) / AMP_CIN; + const Vbatt = CNT_V(VinCnt) / AMP_VIN + CinV + VCE; + const Ibatt = CinV / SHUNT_IN; + const CoutV = CNT_V(CoutCnt) / AMP_COUT; + const Imotor = CoutV / SHUNT_OUT; + const level = Math.max(0, Math.min(100, Math.floor((Vbatt * 1000.0 - batteryVMin) + / (batteryVMax - batteryVMin) * 100))); return { level: level, @@ -337,6 +367,11 @@ void cUiUpdatePower(void) this.markUsed(); } + poke() { + if (this.isActive()) + sensorInfos[this._port].poke(); + } + markUsed() { sensors.__sensorUsed(this._port, this._deviceType()); } diff --git a/libs/core/linux.cpp b/libs/core/linux.cpp index 5d6ce589..2c707a61 100644 --- a/libs/core/linux.cpp +++ b/libs/core/linux.cpp @@ -446,11 +446,6 @@ static void runPoller(Thread *thr) { // disposeThread(thr); } -//% -void unsafePollForChanges(int ms, Action query, Action handler) { - setupThread(handler, 0, runPoller, query, fromInt(ms)); -} - uint32_t afterProgramPage() { return 0; } diff --git a/libs/gyro-sensor/gyro.ts b/libs/gyro-sensor/gyro.ts index 2f220507..6fe5e120 100644 --- a/libs/gyro-sensor/gyro.ts +++ b/libs/gyro-sensor/gyro.ts @@ -45,6 +45,7 @@ namespace sensors { //% weight=64 blockGap=8 //% group="Gyro Sensor" angle(): number { + this.poke(); if (this.calibrating) pauseUntil(() => !this.calibrating, 2000); @@ -65,6 +66,7 @@ namespace sensors { //% weight=65 blockGap=8 //% group="Gyro Sensor" rate(): number { + this.poke(); if (this.calibrating) pauseUntil(() => !this.calibrating, 2000); diff --git a/libs/infrared-sensor/ir.ts b/libs/infrared-sensor/ir.ts index 85866fd0..cadecac7 100644 --- a/libs/infrared-sensor/ir.ts +++ b/libs/infrared-sensor/ir.ts @@ -235,6 +235,7 @@ namespace sensors { //% group="Infrared Sensor" //% this.fieldEditor="ports" proximity(): number { + this.poke(); this._setMode(InfraredSensorMode.Proximity) return this.getNumber(NumberFormat.UInt8LE, 0) } @@ -284,6 +285,7 @@ namespace sensors { // TODO private getDirectionAndDistance() { + this.poke(); this._setMode(InfraredSensorMode.Seek) return this.getNumber(NumberFormat.UInt16LE, this._channel * 2) } diff --git a/libs/touch-sensor/touch.ts b/libs/touch-sensor/touch.ts index 07605352..854220b4 100644 --- a/libs/touch-sensor/touch.ts +++ b/libs/touch-sensor/touch.ts @@ -73,6 +73,7 @@ namespace sensors { //% weight=81 blockGap=8 //% group="Touch Sensor" isPressed() { + this.poke(); return this.button.isPressed(); } @@ -90,6 +91,7 @@ namespace sensors { //% weight=81 //% group="Touch Sensor" wasPressed() { + this.poke(); return this.button.wasPressed(); } } diff --git a/libs/ultrasonic-sensor/ultrasonic.ts b/libs/ultrasonic-sensor/ultrasonic.ts index 7df02881..8ed8bc22 100644 --- a/libs/ultrasonic-sensor/ultrasonic.ts +++ b/libs/ultrasonic-sensor/ultrasonic.ts @@ -84,6 +84,7 @@ namespace sensors { //% weight=65 //% group="Ultrasonic Sensor" distance(): number { + this.poke(); // it supposedly also has an inch mode, but we stick to cm this._setMode(0) return this._query();