Safepolling (#915)

* headstart on safe polling

* poke in sensors

* more poking

* typo
This commit is contained in:
Peli de Halleux 2019-09-17 14:30:02 -07:00 committed by GitHub
parent 64a9930c2e
commit 6f34887c94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 105 additions and 53 deletions

View File

@ -179,6 +179,7 @@ namespace sensors {
//% group="Color Sensor" //% group="Color Sensor"
//% blockGap=8 //% blockGap=8
color(): ColorSensorColor { color(): ColorSensorColor {
this.poke();
this.setMode(ColorSensorMode.Color) this.setMode(ColorSensorMode.Color)
return this.getNumber(NumberFormat.UInt8LE, 0) return this.getNumber(NumberFormat.UInt8LE, 0)
} }
@ -196,6 +197,7 @@ namespace sensors {
//% group="Color Sensor" //% group="Color Sensor"
//% blockGap=8 //% blockGap=8
rgbRaw(): number[] { rgbRaw(): number[] {
this.poke();
this.setMode(ColorSensorMode.RgbRaw); this.setMode(ColorSensorMode.RgbRaw);
return [this.getNumber(NumberFormat.UInt16LE, 0), this.getNumber(NumberFormat.UInt16LE, 2), this.getNumber(NumberFormat.UInt16LE, 4)]; 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 //% weight=87 blockGap=8
//% group="Color Sensor" //% group="Color Sensor"
light(mode: LightIntensityMode) { light(mode: LightIntensityMode) {
this.poke();
this.setMode(<ColorSensorMode><number>mode) this.setMode(<ColorSensorMode><number>mode)
switch(mode) { switch(mode) {
case LightIntensityMode.ReflectedRaw: case LightIntensityMode.ReflectedRaw:
@ -279,6 +282,7 @@ namespace sensors {
*/ */
//% //%
reflectedLightRaw(): number { reflectedLightRaw(): number {
this.poke();
this.setMode(ColorSensorMode.RefRaw); this.setMode(ColorSensorMode.RefRaw);
return this.getNumber(NumberFormat.UInt16LE, 0); return this.getNumber(NumberFormat.UInt16LE, 0);
} }

View File

@ -55,6 +55,10 @@ namespace brick {
this._wasPressed = false this._wasPressed = false
} }
protected poke() {
}
//% hidden //% hidden
_update(curr: boolean) { _update(curr: boolean) {
if (this == null) return if (this == null) return
@ -85,6 +89,7 @@ namespace brick {
//% group="Buttons" //% group="Buttons"
//% button.fieldEditor="brickbuttons" //% button.fieldEditor="brickbuttons"
isPressed() { isPressed() {
this.poke();
return this._isPressed return this._isPressed
} }
@ -102,6 +107,7 @@ namespace brick {
//% group="Buttons" //% group="Buttons"
//% button.fieldEditor="brickbuttons" //% button.fieldEditor="brickbuttons"
wasPressed() { wasPressed() {
this.poke();
const r = this._wasPressed const r = this._wasPressed
this._wasPressed = false this._wasPressed = false
return r return r
@ -144,6 +150,7 @@ namespace brick {
namespace brick { namespace brick {
let btnsMM: MMap let btnsMM: MMap
let buttons: DevButton[] let buttons: DevButton[]
let buttonPoller: sensors.internal.Poller;
export namespace internal { export namespace internal {
export function getBtnsMM() { export function getBtnsMM() {
@ -167,7 +174,7 @@ namespace brick {
btnsMM = control.mmap("/dev/lms_ui", DAL.NUM_BUTTONS, 0) btnsMM = control.mmap("/dev/lms_ui", DAL.NUM_BUTTONS, 0)
if (!btnsMM) control.fail("no buttons?") if (!btnsMM) control.fail("no buttons?")
buttons = [] buttons = []
sensors.internal.unsafePollForChanges(50, readButtons, (prev, curr) => { buttonPoller = new sensors.internal.Poller(50, readButtons, (prev, curr) => {
for (let b of buttons) for (let b of buttons)
b._update(!!(curr & b.mask)) b._update(!!(curr & b.mask))
}) })
@ -182,6 +189,10 @@ namespace brick {
initBtns() initBtns()
buttons.push(this) buttons.push(this)
} }
protected poke() {
buttonPoller.poke();
}
} }
initBtns() // always ON as it handles ESCAPE button initBtns() // always ON as it handles ESCAPE button

View File

@ -1,28 +1,51 @@
namespace sensors.internal { namespace sensors.internal {
//% shim=pxt::unsafePollForChanges export class Poller {
export function unsafePollForChanges( private query: () => number;
periodMs: number, private update: (previous: number, current: number) => void;
query: () => number, public interval: 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().
// 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(() => { constructor(interval: number, query: () => number, update: (previous: number, current: number) => void) {
let prev = query() this.interval = interval | 0;
changeHandler(prev, prev) this.query = query;
while (true) { this.update = update;
pause(periodMs)
let curr = query() this.poll();
if (prev !== curr) { }
changeHandler(prev, curr)
prev = curr 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 { export function bufferToString(buf: Buffer): string {
@ -38,6 +61,7 @@ namespace sensors.internal {
let IICMM: MMap let IICMM: MMap
let powerMM: MMap let powerMM: MMap
let devcon: Buffer let devcon: Buffer
let devPoller: Poller
let sensorInfos: SensorInfo[]; let sensorInfos: SensorInfo[];
let batteryInfo: { let batteryInfo: {
@ -55,6 +79,7 @@ namespace sensors.internal {
connType: number connType: number
devType: number devType: number
iicid: string iicid: string
poller: Poller;
constructor(p: number) { constructor(p: number) {
this.port = p this.port = p
@ -62,6 +87,20 @@ namespace sensors.internal {
this.devType = DAL.DEVICE_TYPE_NONE this.devType = DAL.DEVICE_TYPE_NONE
this.iicid = '' this.iicid = ''
this.sensors = [] 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) powerMM = control.mmap("/dev/lms_power", 2, 0)
unsafePollForChanges(500, devPoller = new Poller(500, () => { return hashDevices(); },
() => { return hashDevices(); },
(prev, curr) => { (prev, curr) => {
detectDevices(); 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[] { export function getActiveSensors(): Sensor[] {
init(); init();
@ -164,13 +194,13 @@ namespace sensors.internal {
batteryVMax = ACCU_INDICATOR_HIGH; batteryVMax = ACCU_INDICATOR_HIGH;
} }
} }
batteryInfo = { batteryInfo = {
CinCnt: CinCnt, CinCnt: CinCnt,
CoutCnt: CoutCnt, CoutCnt: CoutCnt,
VinCnt: VinCnt VinCnt: VinCnt
}; };
// update in background // update in background
control.runInParallel(() => forever(updateBatteryInfo)); control.runInParallel(() => forever(updateBatteryInfo));
} else { } else {
CinCnt = batteryInfo.CinCnt = ((batteryInfo.CinCnt * (AVR_CIN - 1)) + CinCnt) / AVR_CIN; CinCnt = batteryInfo.CinCnt = ((batteryInfo.CinCnt * (AVR_CIN - 1)) + CinCnt) / AVR_CIN;
CoutCnt = batteryInfo.CoutCnt = ((batteryInfo.CoutCnt * (AVR_COUT - 1)) + CoutCnt) / AVR_COUT; CoutCnt = batteryInfo.CoutCnt = ((batteryInfo.CoutCnt * (AVR_COUT - 1)) + CoutCnt) / AVR_COUT;
@ -178,11 +208,11 @@ namespace sensors.internal {
} }
} }
export function getBatteryInfo(): { export function getBatteryInfo(): {
level: number; level: number;
Ibatt: number, Ibatt: number,
Vbatt: number, Vbatt: number,
Imotor: number Imotor: number
} { } {
init(); init();
if (!batteryInfo) updateBatteryInfo(); if (!batteryInfo) updateBatteryInfo();
@ -225,13 +255,13 @@ void cUiUpdatePower(void)
#endif #endif
} }
*/ */
const CinV = CNT_V(CinCnt) / AMP_CIN; const CinV = CNT_V(CinCnt) / AMP_CIN;
const Vbatt = CNT_V(VinCnt) / AMP_VIN + CinV + VCE; const Vbatt = CNT_V(VinCnt) / AMP_VIN + CinV + VCE;
const Ibatt = CinV / SHUNT_IN; const Ibatt = CinV / SHUNT_IN;
const CoutV = CNT_V(CoutCnt) / AMP_COUT; const CoutV = CNT_V(CoutCnt) / AMP_COUT;
const Imotor = CoutV / SHUNT_OUT; const Imotor = CoutV / SHUNT_OUT;
const level = Math.max(0, Math.min(100, Math.floor((Vbatt * 1000.0 - batteryVMin) const level = Math.max(0, Math.min(100, Math.floor((Vbatt * 1000.0 - batteryVMin)
/ (batteryVMax - batteryVMin) * 100))); / (batteryVMax - batteryVMin) * 100)));
return { return {
level: level, level: level,
@ -337,6 +367,11 @@ void cUiUpdatePower(void)
this.markUsed(); this.markUsed();
} }
poke() {
if (this.isActive())
sensorInfos[this._port].poke();
}
markUsed() { markUsed() {
sensors.__sensorUsed(this._port, this._deviceType()); sensors.__sensorUsed(this._port, this._deviceType());
} }

View File

@ -446,11 +446,6 @@ static void runPoller(Thread *thr) {
// disposeThread(thr); // disposeThread(thr);
} }
//%
void unsafePollForChanges(int ms, Action query, Action handler) {
setupThread(handler, 0, runPoller, query, fromInt(ms));
}
uint32_t afterProgramPage() { uint32_t afterProgramPage() {
return 0; return 0;
} }

View File

@ -45,6 +45,7 @@ namespace sensors {
//% weight=64 blockGap=8 //% weight=64 blockGap=8
//% group="Gyro Sensor" //% group="Gyro Sensor"
angle(): number { angle(): number {
this.poke();
if (this.calibrating) if (this.calibrating)
pauseUntil(() => !this.calibrating, 2000); pauseUntil(() => !this.calibrating, 2000);
@ -65,6 +66,7 @@ namespace sensors {
//% weight=65 blockGap=8 //% weight=65 blockGap=8
//% group="Gyro Sensor" //% group="Gyro Sensor"
rate(): number { rate(): number {
this.poke();
if (this.calibrating) if (this.calibrating)
pauseUntil(() => !this.calibrating, 2000); pauseUntil(() => !this.calibrating, 2000);

View File

@ -235,6 +235,7 @@ namespace sensors {
//% group="Infrared Sensor" //% group="Infrared Sensor"
//% this.fieldEditor="ports" //% this.fieldEditor="ports"
proximity(): number { proximity(): number {
this.poke();
this._setMode(InfraredSensorMode.Proximity) this._setMode(InfraredSensorMode.Proximity)
return this.getNumber(NumberFormat.UInt8LE, 0) return this.getNumber(NumberFormat.UInt8LE, 0)
} }
@ -284,6 +285,7 @@ namespace sensors {
// TODO // TODO
private getDirectionAndDistance() { private getDirectionAndDistance() {
this.poke();
this._setMode(InfraredSensorMode.Seek) this._setMode(InfraredSensorMode.Seek)
return this.getNumber(NumberFormat.UInt16LE, this._channel * 2) return this.getNumber(NumberFormat.UInt16LE, this._channel * 2)
} }

View File

@ -73,6 +73,7 @@ namespace sensors {
//% weight=81 blockGap=8 //% weight=81 blockGap=8
//% group="Touch Sensor" //% group="Touch Sensor"
isPressed() { isPressed() {
this.poke();
return this.button.isPressed(); return this.button.isPressed();
} }
@ -90,6 +91,7 @@ namespace sensors {
//% weight=81 //% weight=81
//% group="Touch Sensor" //% group="Touch Sensor"
wasPressed() { wasPressed() {
this.poke();
return this.button.wasPressed(); return this.button.wasPressed();
} }
} }

View File

@ -84,6 +84,7 @@ namespace sensors {
//% weight=65 //% weight=65
//% group="Ultrasonic Sensor" //% group="Ultrasonic Sensor"
distance(): number { distance(): number {
this.poke();
// it supposedly also has an inch mode, but we stick to cm // it supposedly also has an inch mode, but we stick to cm
this._setMode(0) this._setMode(0)
return this._query(); return this._query();