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
8 changed files with 105 additions and 53 deletions

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

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