diff --git a/src/consts.ts b/src/consts.ts index e46e5e5..2412567 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -706,12 +706,12 @@ export enum MarioColor { /** * @typedef CommandFeedback - * @param {number} TRANSMISSION_PENDING 0x00 waiting for previous comands to complete transmission or execution + * @param {number} TRANSMISSION_PENDING 0x00 waiting for previous commands to complete transmission or execution * @param {number} TRANSMISSION_BUSY 0x10 waiting for device to acknowledge reception - * @param {number} TRANSMISSION_DISCARDED 0x44 other command for immediate execution has been recieved or device disconnected + * @param {number} TRANSMISSION_DISCARDED 0x44 interrupt command has been recieved or device disconnected * @param {number} EXECUTION_PENDING 0x20 device is waiting for previous command to complete * @param {number} EXECUTION_BUSY 0x21 device is executing the command - * @param {number} EXECUTION_DISCARDED 0x24 device discarded the command e.g. due to other command for immediate execution + * @param {number} EXECUTION_DISCARDED 0x24 device discarded the command e.g. due to interrupt * @param {number} EXECUTION_COMPLETED 0x22 device reported successful completion of command * @param {number} FEEDBACK_MISSING 0x66 device disconnected or failed to report feedback * @param {number} FEEDBACK_DISABLED 0x26 feedback not implemented for this command diff --git a/src/devices/basicmotor.ts b/src/devices/basicmotor.ts index 2b09461..3041fd4 100644 --- a/src/devices/basicmotor.ts +++ b/src/devices/basicmotor.ts @@ -1,9 +1,6 @@ import { Device } from "./device"; - import { IDeviceInterface } from "../interfaces"; - import * as Consts from "../consts"; - import { calculateRamp, mapSpeed } from "../utils"; /** @@ -39,15 +36,12 @@ export class BasicMotor extends Device { * @returns {Promise} Resolved upon completion of command. */ public rampPower (fromPower: number, toPower: number, time: number) { - return new Promise((resolve) => { - calculateRamp(this, fromPower, toPower, time) - .on("changePower", (power) => { - this.setPower(power, false); - }) - .on("finished", () => { - return resolve(Consts.CommandFeedback.FEEDBACK_DISABLED); - }) + const powerValues = calculateRamp(fromPower, toPower, time); + powerValues.forEach(value => { + this.setPower(value); + this.addPortOutputSleep(Math.round(time/powerValues.length)); }); + return this.setPower(toPower); } diff --git a/src/devices/device.ts b/src/devices/device.ts index 2d3d2f9..c4ee186 100644 --- a/src/devices/device.ts +++ b/src/devices/device.ts @@ -2,6 +2,7 @@ import { EventEmitter } from "events"; import { IDeviceInterface } from "../interfaces"; import { PortOutputCommand } from "../portoutputcommand"; +import { PortOutputSleep } from "../portoutputsleep"; import * as Consts from "../consts"; @@ -19,7 +20,7 @@ export class Device extends EventEmitter { protected _mode: number | undefined; protected _bufferLength: number = 0; - protected _nextPortOutputCommands: PortOutputCommand[] = []; + protected _nextPortOutputCommands: (PortOutputCommand | PortOutputSleep)[] = []; protected _transmittedPortOutputCommands: PortOutputCommand[] = []; private _hub: IDeviceInterface; @@ -30,7 +31,6 @@ export class Device extends EventEmitter { private _isWeDo2SmartHub: boolean; private _isVirtualPort: boolean = false; - private _eventTimer: NodeJS.Timer | null = null; constructor (hub: IDeviceInterface, portId: number, modeMap: {[event: string]: number} = {}, type: Consts.DeviceType = Consts.DeviceType.UNKNOWN) { super(); @@ -132,9 +132,6 @@ export class Device extends EventEmitter { } public writeDirect (mode: number, data: Buffer, interrupt: boolean = false) { - if (interrupt) { - this.cancelEventTimer(); - } if (this.isWeDo2SmartHub) { return this.send(Buffer.concat([Buffer.from([this.portId, 0x01, 0x02]), data]), Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE).then(() => { return Consts.CommandFeedback.FEEDBACK_DISABLED; }); } else { @@ -183,15 +180,29 @@ export class Device extends EventEmitter { this._nextPortOutputCommands = []; return; } - if(this._bufferLength !== this._transmittedPortOutputCommands.length) return; if(!this._nextPortOutputCommands.length) return; - if(this._bufferLength < 2 || this._nextPortOutputCommands[0].interrupt) { + const nextCommand = this._nextPortOutputCommands[0]; + if(nextCommand instanceof PortOutputSleep) { + if(nextCommand.state === Consts.CommandFeedback.EXECUTION_PENDING) { + nextCommand.state = Consts.CommandFeedback.EXECUTION_BUSY; + debug("sleep command ", nextCommand.duration); + setTimeout(() => { + const command = this._nextPortOutputCommands.shift(); + if(command) command.resolve(Consts.CommandFeedback.EXECUTION_COMPLETED); + this.transmitNextPortOutputCommand(); + }, nextCommand.duration); + } + return; + } + if(this._bufferLength !== this._transmittedPortOutputCommands.length) return; + if(this._bufferLength < 2 || nextCommand.interrupt) { const command = this._nextPortOutputCommands.shift(); if(command) { debug("transmit command ", command.startupAndCompletion, command.data); this.send(Buffer.concat([Buffer.from([0x81, this.portId, command.startupAndCompletion]), command.data])); command.state = Consts.CommandFeedback.TRANSMISSION_BUSY; this._transmittedPortOutputCommands.push(command); + this.transmitNextPortOutputCommand(); // if PortOutputSleep this starts timeout // one could start a timer here to ensure finish function is called } } @@ -214,6 +225,12 @@ export class Device extends EventEmitter { return command.promise; } + public addPortOutputSleep(duration: number) { + const command = new PortOutputSleep(duration); + this._nextPortOutputCommands.push(command); + return command.promise; + } + public finish (message: number) { debug("recieved command feedback ", message); if((message & 0x08) === 0x08) this._bufferLength = 0; @@ -309,7 +326,7 @@ export class Device extends EventEmitter { this._missing(); this._busy(); } - // third command can only be interrupt, if busy === 2 it was queued + // third command can only be interrupt, if this._bufferLength === 2 it was queued else { this._missing(); this._missing(); @@ -321,17 +338,6 @@ export class Device extends EventEmitter { this.transmitNextPortOutputCommand(); } - public setEventTimer (timer: NodeJS.Timer) { - this._eventTimer = timer; - } - - public cancelEventTimer () { - if (this._eventTimer) { - clearTimeout(this._eventTimer); - this._eventTimer = null; - } - } - private _ensureConnected () { if (!this.connected) { throw new Error("Device is not connected"); diff --git a/src/devices/light.ts b/src/devices/light.ts index 0b9a00b..b40abf0 100644 --- a/src/devices/light.ts +++ b/src/devices/light.ts @@ -1,7 +1,5 @@ import { Device } from "./device"; - import { IDeviceInterface } from "../interfaces"; - import * as Consts from "../consts"; import { calculateRamp } from "../utils"; @@ -39,16 +37,12 @@ export class Light extends Device { * @returns {Promise} Resolved upon completion of command. */ public rampBrightness (fromBrightness: number, toBrightness: number, time: number) { - return new Promise((resolve) => { - calculateRamp(this, fromBrightness, toBrightness, time) - .on("changePower", (power) => { - this.setBrightness(power, false); - }) - .on("finished", () => { - return resolve(Consts.CommandFeedback.FEEDBACK_DISABLED); - }); + const powerValues = calculateRamp(fromBrightness, toBrightness, time); + powerValues.forEach(value => { + this.setBrightness(value); + this.addPortOutputSleep(Math.round(time/powerValues.length)); }); + return this.setBrightness(toBrightness); } - } diff --git a/src/portoutputsleep.ts b/src/portoutputsleep.ts new file mode 100644 index 0000000..c047671 --- /dev/null +++ b/src/portoutputsleep.ts @@ -0,0 +1,11 @@ +import { CommandFeedback } from "./consts"; +import { PortOutputCommand } from "./portoutputcommand"; + +export class PortOutputSleep extends PortOutputCommand { + public duration: number + constructor(duration: number) { + super(Buffer.alloc(0), false); + this.duration = duration; + this.state = CommandFeedback.EXECUTION_PENDING; + } +} diff --git a/src/utils.ts b/src/utils.ts index 3c6d10e..9a4d0f3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -59,7 +59,7 @@ export const roundAngleToNearest90 = (angle: number) => { return -180; }; -export const calculateRamp = (device: Device, fromPower: number, toPower: number, time: number) => { +export const calculateRamp = (fromPower: number, toPower: number, time: number) => { const emitter = new EventEmitter(); const steps = Math.abs(toPower - fromPower); let delay = time / steps; @@ -71,22 +71,7 @@ export const calculateRamp = (device: Device, fromPower: number, toPower: number if (fromPower > toPower) { increment = -increment; } - let i = 0; - const interval = setInterval(() => { - let power = Math.round(fromPower + (++i * increment)); - if (toPower > fromPower && power > toPower) { - power = toPower; - } else if (fromPower > toPower && power < toPower) { - power = toPower; - } - emitter.emit("changePower", power); - if (power === toPower) { - clearInterval(interval); - emitter.emit("finished"); - } - }, delay); - device.setEventTimer(interval); - return emitter; + return Array(Math.round(time/delay)).fill(0).map((element, index) => fromPower + index*increment); }; export const parseColor = (color: number) => { @@ -94,4 +79,4 @@ export const parseColor = (color: number) => { color = color + 1; } return color; -} \ No newline at end of file +}