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