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"
//% 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(<ColorSensorMode><number>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);
}

View File

@ -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

View File

@ -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();
@ -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());
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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)
}

View File

@ -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();
}
}

View File

@ -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();