-
Notifications
You must be signed in to change notification settings - Fork 66
Dynamic modes #97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Dynamic modes #97
Changes from 6 commits
ea6b23b
ec37e6d
d4ea7cb
299963b
b4f1880
57c7baa
45b0c20
4edd5eb
8c451f2
8feffd6
4c429e2
c830cb9
df8ebd2
405b5f2
351bc90
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,11 @@ | ||
| import { EventEmitter } from "events"; | ||
|
|
||
| import { IDeviceInterface } from "../interfaces"; | ||
| import { IDeviceInterface, IMode } from "../interfaces"; | ||
|
|
||
| import * as Consts from "../consts"; | ||
|
|
||
| import { normalize } from "../utils"; | ||
|
|
||
| /** | ||
| * @class Device | ||
| * @extends EventEmitter | ||
|
|
@@ -13,10 +15,12 @@ export class Device extends EventEmitter { | |
| public autoSubscribe: boolean = true; | ||
| public values: {[event: string]: any} = {}; | ||
|
|
||
| protected _modes: IMode[] = []; | ||
| protected _mode: number | undefined; | ||
| protected _busy: boolean = false; | ||
| protected _finished: (() => void) | undefined; | ||
|
|
||
| private _ready: boolean = false; | ||
| private _hub: IDeviceInterface; | ||
| private _portId: number; | ||
| private _connected: boolean = true; | ||
|
|
@@ -37,7 +41,7 @@ export class Device extends EventEmitter { | |
| this._isVirtualPort = this.hub.isPortVirtual(portId); | ||
|
|
||
| const eventAttachListener = (event: string) => { | ||
| if (event === "detach") { | ||
| if (event === "detach" || !this._ready) { | ||
| return; | ||
| } | ||
| if (this.autoSubscribe) { | ||
|
|
@@ -64,6 +68,11 @@ export class Device extends EventEmitter { | |
| this.hub.on("newListener", eventAttachListener); | ||
| this.on("newListener", eventAttachListener); | ||
| this.hub.on("detach", deviceDetachListener); | ||
|
|
||
| if (!this.autoparse) { | ||
| this._ready = true; | ||
| this.emit('ready'); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -126,6 +135,22 @@ export class Device extends EventEmitter { | |
| return this._isVirtualPort; | ||
| } | ||
|
|
||
| /** | ||
| * @readonly | ||
| * @property {string[]} events List of availlable events (input modes). | ||
| */ | ||
| public get events () { | ||
| return this._modes.filter(mode => mode.input).map(({ name }) => name); | ||
| } | ||
|
|
||
| /** | ||
| * @readonly | ||
| * @property {string[]} writeModes List of availlable write (output modes). | ||
| */ | ||
| public get writeModes () { | ||
|
aileo marked this conversation as resolved.
Outdated
|
||
| return this._modes.filter(mode => mode.output).map(({ name }) => name); | ||
| } | ||
|
|
||
| public writeDirect (mode: number, data: Buffer) { | ||
| if (this.isWeDo2SmartHub) { | ||
| return this.send(Buffer.concat([Buffer.from([this.portId, 0x01, 0x02]), data]), Consts.BLECharacteristic.WEDO2_MOTOR_VALUE_WRITE); | ||
|
|
@@ -134,6 +159,36 @@ export class Device extends EventEmitter { | |
| } | ||
| } | ||
|
|
||
| public autoparseWriteDirect (mode: string, ...data: number[]) { | ||
|
aileo marked this conversation as resolved.
Outdated
|
||
| if (!this.autoparse) return; | ||
| const modeId = this._modeMap[mode]; | ||
| if (modeId === undefined) return; | ||
|
|
||
| const { values } = this._modes[modeId]; | ||
| const valueSize = Consts.ValueTypeSize[values.type]; | ||
|
|
||
| const buf = Buffer.alloc(values.count * valueSize); | ||
| for(let v = 0; v < values.count; v++) { | ||
| const offset = v * valueSize; | ||
| switch(values.type) { | ||
| case Consts.ValueType.Int8: | ||
| buf.writeInt8(data[v] || 0, offset); | ||
| break; | ||
| case Consts.ValueType.Int16: | ||
| buf.writeInt16LE(data[v] || 0, offset); | ||
| break; | ||
| case Consts.ValueType.Int32: | ||
| buf.writeInt32LE(data[v] || 0, offset); | ||
| break; | ||
| case Consts.ValueType.Float: | ||
| buf.writeFloatLE(data[v] || 0, offset); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| return this.writeDirect(modeId, buf); | ||
| } | ||
|
|
||
| public send (data: Buffer, characteristic: string = Consts.BLECharacteristic.LPF2_ALL) { | ||
| this._ensureConnected(); | ||
| return this.hub.send(data, characteristic); | ||
|
|
@@ -153,6 +208,38 @@ export class Device extends EventEmitter { | |
|
|
||
| public receive (message: Buffer) { | ||
| this.notify("receive", { message }); | ||
|
|
||
| const mode = this._mode; | ||
| if (mode === undefined) { | ||
| return; | ||
| } | ||
| const { name, raw, pct, si, values } = this._modes[mode]; | ||
| const valueSize = Consts.ValueTypeSize[values.type]; | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't checked on all devices, but do all device modes only accept one type of value? Even for multiple values? Are there any instances where a device could have ie. 3x int32, 1x uint8? I don't know if the UART protocol even supports that.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would say they mix everything as in the boost color and distance sensor: [
{
"name": "COLOR",
"input": true,
"output": false,
"raw": { "min": 0, "max": 10 },
"pct": { "min": 0, "max": 100 },
"si": { "min": 0, "max": 10, "symbol": "IDX" },
"values": {
"count": 1,
"type": 0
}
},
{
"name": "PROX",
"input": true,
"output": false,
"raw": { "min": 0, "max": 10 },
"pct": { "min": 0, "max": 100 },
"si": { "min": 0, "max": 10, "symbol": "DIS" },
"values": {
"count": 1,
"type": 0
}
},
"[...]",
{
"name": "SPEC 1",
"input": true,
"output": false,
"raw": { "min": 0, "max": 255 },
"pct": { "min": 0, "max": 100 },
"si": { "min": 0, "max": 255, "symbol": "N/A" },
"values": {
"count": 4,
"type": 0
}
},
"[...]"
]For SPEC 1, which includes values from COLOR and PROX modes, it use 0-255 as range instead of 0-10 to match other values. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As an example, the tilt sensor of the TechnicMediumHub has different data types for different modes in use (Int8, Int32, Int16) but within one mode, the data type is always the same. At least, that is what the device/spec is self-declaring. |
||
| const data = []; | ||
|
|
||
| for(let v = 0; v < values.count; v++) { | ||
| const offset = 4 + v * valueSize; | ||
| switch(values.type) { | ||
| case Consts.ValueType.Int8: | ||
| data.push(message.readInt8(offset)); | ||
| break; | ||
| case Consts.ValueType.Int16: | ||
| data.push(message.readInt16LE(offset)); | ||
| break; | ||
| case Consts.ValueType.Int32: | ||
| data.push(message.readInt32LE(offset)); | ||
| break; | ||
| case Consts.ValueType.Float: | ||
| data.push(message.readFloatLE(offset)); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| this.notify(name, { | ||
| raw: data, | ||
| pct: data.map(value => normalize(value, {raw, out: pct})), | ||
| si: data.map(value => normalize(value, {raw, out: si})) | ||
| }); | ||
| } | ||
|
|
||
| public notify (event: string, values: any) { | ||
|
|
@@ -192,4 +279,19 @@ export class Device extends EventEmitter { | |
| } | ||
| } | ||
|
|
||
| public setModes(modes: IMode[]) { | ||
| this._modes = modes; | ||
|
|
||
| this._modeMap = modes.reduce((map: {[name: string]: number}, mode, index) => { | ||
| map[mode.name] = index; | ||
| return map; | ||
| }, {}); | ||
|
|
||
| this._ready = true; | ||
| this.emit('ready'); | ||
| } | ||
|
|
||
| private get autoparse() { | ||
|
aileo marked this conversation as resolved.
Outdated
|
||
| return this.hub.autoParse || this._type === Consts.DeviceType.UNKNOWN; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,10 @@ export class DuploTrainBaseSpeedometer extends Device { | |
| } | ||
|
|
||
| public receive (message: Buffer) { | ||
| if (this.hub.autoParse) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are quite repetitive, and error prone when new devices are added (ie. could forget to do this). Could the check be done at hub level, calling either receive or autoParse?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am thinking about another way to do this:
No more
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I totally forgot about the subscription logic in the comment above. It will need the correct mode map.
aileo marked this conversation as resolved.
Outdated
|
||
| return super.receive(message); | ||
| } | ||
|
|
||
| const mode = this._mode; | ||
|
|
||
| switch (mode) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.