Safepolling (#915)
* headstart on safe polling * poke in sensors * more poking * typo
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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());
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user