mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-24 23:12:24 -05:00
Major change of motor state handling to increase reliability, clear responsibility of handling state, and readability of the code.
BoostMotor now has a status getter/setter that replaces isOn() and is responsible for clearing various motor state parameters. A new BoostMotorState-enum contains the possible states a motor can be in. Since time-based motor commands really just trigger a BoostMotor.turnOn(), it's the opcodes that are responsible for setting the motor state.
This commit is contained in:
parent
66ff92433e
commit
63726044e4
1 changed files with 79 additions and 31 deletions
|
@ -219,6 +219,17 @@ const BoostMode = {
|
||||||
UNKNOWN: 0 // Anything else will use the default mode (mode 0)
|
UNKNOWN: 0 // Anything else will use the default mode (mode 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum for Boost motor states.
|
||||||
|
* @param {number}
|
||||||
|
*/
|
||||||
|
const BoostMotorState = {
|
||||||
|
OFF: 0,
|
||||||
|
ON_FOREVER: 1,
|
||||||
|
ON_FOR_TIME: 2,
|
||||||
|
ON_FOR_ROTATION: 3
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function for converting a JavaScript number to an INT32-number
|
* Helper function for converting a JavaScript number to an INT32-number
|
||||||
* @param {number} number - a number
|
* @param {number} number - a number
|
||||||
|
@ -297,7 +308,7 @@ class BoostMotor {
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this._status = BoostPortFeedback.IDLE;
|
this._status = BoostMotorState.OFF;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the motor has been turned on or is actively braking for a specific duration, this is the timeout ID for
|
* If the motor has been turned on or is actively braking for a specific duration, this is the timeout ID for
|
||||||
|
@ -394,13 +405,31 @@ class BoostMotor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {boolean} - true if this motor is currently moving, false if this motor is off or braking.
|
* @return {BoostMotorState} - the motor's current state.
|
||||||
*/
|
*/
|
||||||
get isOn () {
|
get status () {
|
||||||
if (this._status & (BoostPortFeedback.BUSY_OR_FULL ^ BoostPortFeedback.IN_PROGRESS)) {
|
return this._status;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
/**
|
||||||
|
* @param {BoostMotorState} value - set this motor's state.
|
||||||
|
*/
|
||||||
|
set status (value) {
|
||||||
|
switch (value) {
|
||||||
|
case BoostMotorState.OFF:
|
||||||
|
case BoostMotorState.ON_FOREVER:
|
||||||
|
this._clearRotationState();
|
||||||
|
this._clearTimeout();
|
||||||
|
break;
|
||||||
|
case BoostMotorState.ON_FOR_TIME:
|
||||||
|
this._clearRotationState();
|
||||||
|
break;
|
||||||
|
case BoostMotorState.ON_FOR_ROTATION:
|
||||||
|
this._clearRotationState();
|
||||||
|
this._clearTimeout();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this._status = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -452,7 +481,6 @@ class BoostMotor {
|
||||||
MathUtil.clamp(this.power + BoostMotorMaxPowerAdd, 0, 100),
|
MathUtil.clamp(this.power + BoostMotorMaxPowerAdd, 0, 100),
|
||||||
BoostMotorProfile.DO_NOT_USE
|
BoostMotorProfile.DO_NOT_USE
|
||||||
]);
|
]);
|
||||||
this._status = BoostPortFeedback.BUSY_OR_FULL;
|
|
||||||
|
|
||||||
this._parent.send(BoostBLE.characteristic, cmd);
|
this._parent.send(BoostBLE.characteristic, cmd);
|
||||||
|
|
||||||
|
@ -497,7 +525,6 @@ class BoostMotor {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
this._pendingPositionOrigin = this.position;
|
|
||||||
this._pendingPositionDestination = this.position + (degrees * this.direction * direction);
|
this._pendingPositionDestination = this.position + (degrees * this.direction * direction);
|
||||||
this._parent.send(BoostBLE.characteristic, cmd);
|
this._parent.send(BoostBLE.characteristic, cmd);
|
||||||
}
|
}
|
||||||
|
@ -556,6 +583,19 @@ class BoostMotor {
|
||||||
this._pendingTimeoutStartTime = Date.now();
|
this._pendingTimeoutStartTime = Date.now();
|
||||||
this._pendingTimeoutDelay = delay;
|
this._pendingTimeoutDelay = delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the motor states related to rotation-based commands, if any.
|
||||||
|
* Safe to call even when there is no pending timeout.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_clearRotationState () {
|
||||||
|
if (this._pendingPromiseFunction) {
|
||||||
|
this._pendingPromiseFunction();
|
||||||
|
this._pendingPromiseFunction = null;
|
||||||
|
}
|
||||||
|
this._pendingPositionDestination = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -953,13 +993,13 @@ class Boost {
|
||||||
const feedback = data[4];
|
const feedback = data[4];
|
||||||
const motor = this.motor(portID);
|
const motor = this.motor(portID);
|
||||||
if (motor) {
|
if (motor) {
|
||||||
motor._status = feedback;
|
|
||||||
// Makes sure that commands resolve both when they actually complete and when they fail
|
// Makes sure that commands resolve both when they actually complete and when they fail
|
||||||
const isBusy = feedback & BoostPortFeedback.IN_PROGRESS;
|
const isBusy = feedback & BoostPortFeedback.IN_PROGRESS;
|
||||||
const commandCompleted = feedback & (BoostPortFeedback.COMPLETED ^ BoostPortFeedback.DISCARDED);
|
const commandCompleted = feedback & (BoostPortFeedback.COMPLETED ^ BoostPortFeedback.DISCARDED);
|
||||||
if (!isBusy && commandCompleted && motor.pendingPromiseFunction) {
|
if (!isBusy && commandCompleted) {
|
||||||
motor.pendingPromiseFunction();
|
if (motor.status === BoostMotorState.ON_FOR_ROTATION) {
|
||||||
motor.pendingPromiseFunction = null;
|
motor.status = BoostMotorState.OFF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1619,6 +1659,7 @@ class Scratch3BoostBlocks {
|
||||||
this._forEachMotor(args.MOTOR_ID, motorIndex => {
|
this._forEachMotor(args.MOTOR_ID, motorIndex => {
|
||||||
const motor = this._peripheral.motor(motorIndex);
|
const motor = this._peripheral.motor(motorIndex);
|
||||||
if (motor) {
|
if (motor) {
|
||||||
|
motor.status = BoostMotorState.ON_FOR_TIME;
|
||||||
motor.turnOnFor(durationMS);
|
motor.turnOnFor(durationMS);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1654,12 +1695,8 @@ class Scratch3BoostBlocks {
|
||||||
const promises = motors.map(portID => {
|
const promises = motors.map(portID => {
|
||||||
const motor = this._peripheral.motor(portID);
|
const motor = this._peripheral.motor(portID);
|
||||||
if (motor) {
|
if (motor) {
|
||||||
if (motor.pendingPromiseFunction) {
|
|
||||||
// If there's already a promise for this motor it must be resolved to avoid hanging blocks.
|
|
||||||
motor.pendingPromiseFunction();
|
|
||||||
motor.pendingPromiseFunction = null;
|
|
||||||
}
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
|
motor.status = BoostMotorState.ON_FOR_ROTATION;
|
||||||
motor.pendingPromiseFunction = resolve;
|
motor.pendingPromiseFunction = resolve;
|
||||||
motor.turnOnForDegrees(degrees, sign);
|
motor.turnOnForDegrees(degrees, sign);
|
||||||
});
|
});
|
||||||
|
@ -1684,6 +1721,7 @@ class Scratch3BoostBlocks {
|
||||||
this._forEachMotor(args.MOTOR_ID, motorIndex => {
|
this._forEachMotor(args.MOTOR_ID, motorIndex => {
|
||||||
const motor = this._peripheral.motor(motorIndex);
|
const motor = this._peripheral.motor(motorIndex);
|
||||||
if (motor) {
|
if (motor) {
|
||||||
|
motor.status = BoostMotorState.ON_FOREVER;
|
||||||
motor.turnOn();
|
motor.turnOn();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1706,6 +1744,7 @@ class Scratch3BoostBlocks {
|
||||||
this._forEachMotor(args.MOTOR_ID, motorIndex => {
|
this._forEachMotor(args.MOTOR_ID, motorIndex => {
|
||||||
const motor = this._peripheral.motor(motorIndex);
|
const motor = this._peripheral.motor(motorIndex);
|
||||||
if (motor) {
|
if (motor) {
|
||||||
|
motor.status = BoostMotorState.OFF;
|
||||||
motor.turnOff();
|
motor.turnOff();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1729,14 +1768,17 @@ class Scratch3BoostBlocks {
|
||||||
const motor = this._peripheral.motor(motorIndex);
|
const motor = this._peripheral.motor(motorIndex);
|
||||||
if (motor) {
|
if (motor) {
|
||||||
motor.power = MathUtil.clamp(Cast.toNumber(args.POWER), 0, 100);
|
motor.power = MathUtil.clamp(Cast.toNumber(args.POWER), 0, 100);
|
||||||
if (motor.isOn) {
|
switch (motor.status) {
|
||||||
if (motor.pendingTimeoutDelay) {
|
case BoostMotorState.ON_FOREVER:
|
||||||
|
motor.turnOn();
|
||||||
|
break;
|
||||||
|
case BoostMotorState.ON_FOR_TIME:
|
||||||
motor.turnOnFor(motor.pendingTimeoutStartTime + motor.pendingTimeoutDelay - Date.now());
|
motor.turnOnFor(motor.pendingTimeoutStartTime + motor.pendingTimeoutDelay - Date.now());
|
||||||
} else if (motor.pendingPromiseFunction) {
|
break;
|
||||||
|
case BoostMotorState.ON_FOR_ROTATION: {
|
||||||
const p = Math.abs(motor.pendingPositionDestination - motor.position);
|
const p = Math.abs(motor.pendingPositionDestination - motor.position);
|
||||||
motor.turnOnForDegrees(p, Math.sign(p));
|
motor.turnOnForDegrees(p, Math.sign(p));
|
||||||
} else {
|
break;
|
||||||
motor.turnOn();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1770,14 +1812,20 @@ class Scratch3BoostBlocks {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// keep the motor on if it's running, and update the pending timeout if needed
|
// keep the motor on if it's running, and update the pending timeout if needed
|
||||||
if (motor.isOn) {
|
if (motor) {
|
||||||
if (motor.pendingTimeoutDelay) {
|
motor.power = MathUtil.clamp(Cast.toNumber(args.POWER), 0, 100);
|
||||||
|
switch (motor.status) {
|
||||||
|
case BoostMotorState.ON_FOREVER:
|
||||||
|
motor.turnOn();
|
||||||
|
break;
|
||||||
|
case BoostMotorState.ON_FOR_TIME:
|
||||||
motor.turnOnFor(motor.pendingTimeoutStartTime + motor.pendingTimeoutDelay - Date.now());
|
motor.turnOnFor(motor.pendingTimeoutStartTime + motor.pendingTimeoutDelay - Date.now());
|
||||||
} else if (motor.pendingPromiseFunction) {
|
break;
|
||||||
|
case BoostMotorState.ON_FOR_ROTATION: {
|
||||||
const p = Math.abs(motor.pendingPositionDestination - motor.position);
|
const p = Math.abs(motor.pendingPositionDestination - motor.position);
|
||||||
motor.turnOnForDegrees(p, Math.sign(p));
|
motor.turnOnForDegrees(p, Math.sign(p));
|
||||||
} else {
|
break;
|
||||||
motor.turnOn();
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue