-
Notifications
You must be signed in to change notification settings - Fork 63
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
Feature/devices: parsing sensor #65
Changes from 3 commits
388a291
633a2e9
ae51b7f
67f0836
d3eb19b
9d05d64
345b34c
3f105a7
a76a82e
f694cdc
3da255b
64425a9
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 |
---|---|---|
|
@@ -2,90 +2,111 @@ import { Device } from "./device"; | |
|
||
import { IDeviceInterface } from "../interfaces"; | ||
|
||
import * as Consts from "../consts"; | ||
import { DeviceType, HubType, IDeviceMode, ValueType } from "../consts"; | ||
|
||
export class ColorDistanceSensor extends Device { | ||
|
||
private static modes: { [name: string]: IDeviceMode } = { | ||
COLOR: { | ||
/** | ||
* Emits when a color sensor is activated. | ||
* @event ColorDistanceSensor#color | ||
* @param {string} port | ||
* @param {Color} color | ||
*/ | ||
input: true, | ||
event: "color", | ||
values: { type: ValueType.UInt8, count: 1, min: 0, max: 255 }, | ||
num: { | ||
[HubType.BOOST_MOVE_HUB]: 0x00, | ||
[HubType.CONTROL_PLUS_HUB]: 0x00, | ||
[HubType.POWERED_UP_HUB]: 0x00, | ||
[HubType.WEDO2_SMART_HUB]: 0x00 | ||
} | ||
}, | ||
PROX: { | ||
input: true, | ||
event: "distance", | ||
values: { type: ValueType.UInt8, count: 1, min: 0, max: 10 }, | ||
num: { | ||
[HubType.BOOST_MOVE_HUB]: 0x01, | ||
[HubType.CONTROL_PLUS_HUB]: 0x01, | ||
[HubType.POWERED_UP_HUB]: 0x01 | ||
} | ||
}, | ||
COUNT: { | ||
input: true, | ||
event: "count", | ||
values: { type: ValueType.UInt8, count: 1, min: 0 }, | ||
num: { | ||
[HubType.BOOST_MOVE_HUB]: 0x02, | ||
[HubType.CONTROL_PLUS_HUB]: 0x02, | ||
[HubType.POWERED_UP_HUB]: 0x02 | ||
} | ||
}, | ||
REFLT: { | ||
input: true, | ||
event: "reflectivity", | ||
values: { type: ValueType.UInt8, count: 1, min: 0, max: 100 }, | ||
num: { | ||
[HubType.BOOST_MOVE_HUB]: 0x03, | ||
[HubType.CONTROL_PLUS_HUB]: 0x03, | ||
[HubType.POWERED_UP_HUB]: 0x03 | ||
} | ||
}, | ||
AMBI: { | ||
input: true, | ||
event: "luminosity", | ||
values: { type: ValueType.UInt8, count: 1, min: 0, max: 100 }, | ||
num: { | ||
[HubType.BOOST_MOVE_HUB]: 0x04, | ||
[HubType.CONTROL_PLUS_HUB]: 0x04, | ||
[HubType.POWERED_UP_HUB]: 0x04 | ||
} | ||
}, | ||
SOME_OUTPUT_MODE: { | ||
input: false, | ||
num: {} | ||
}, | ||
RGB_I: { | ||
input: true, | ||
event: "rgb", | ||
values: { type: ValueType.UInt8, count: 3, min: 0, max: 255 }, | ||
num: { | ||
[HubType.BOOST_MOVE_HUB]: 0x06, | ||
[HubType.CONTROL_PLUS_HUB]: 0x06, | ||
[HubType.POWERED_UP_HUB]: 0x06 | ||
} | ||
}, | ||
OTHER_OUTPUT_MODE: { | ||
input: false, | ||
num: {} | ||
}, | ||
SPEC_1: { | ||
input: true, | ||
event: "colorAndDistance", | ||
values: { type: ValueType.UInt8, count: 4, min: 0, max: 255 }, | ||
num: { | ||
[HubType.BOOST_MOVE_HUB]: 0x08, | ||
[HubType.CONTROL_PLUS_HUB]: 0x08, | ||
[HubType.POWERED_UP_HUB]: 0x08 | ||
} | ||
} | ||
}; | ||
|
||
constructor (hub: IDeviceInterface, portId: number) { | ||
super(hub, portId, ColorDistanceSensor.ModeMap, Consts.DeviceType.COLOR_DISTANCE_SENSOR); | ||
super(hub, portId, ColorDistanceSensor.modes, DeviceType.COLOR_DISTANCE_SENSOR); | ||
} | ||
|
||
public receive (message: Buffer) { | ||
const mode = this._mode; | ||
|
||
switch (mode) { | ||
case ColorDistanceSensor.Mode.COLOR: | ||
if (message[this.isWeDo2SmartHub ? 2 : 4] <= 10) { | ||
const color = message[this.isWeDo2SmartHub ? 2 : 4]; | ||
|
||
/** | ||
* Emits when a color sensor is activated. | ||
* @event ColorDistanceSensor#color | ||
* @param {Color} color | ||
*/ | ||
this.emit("color", color); | ||
} | ||
break; | ||
|
||
case ColorDistanceSensor.Mode.DISTANCE: | ||
if (this.isWeDo2SmartHub) { | ||
break; | ||
} | ||
if (message[4] <= 10) { | ||
const distance = Math.floor(message[4] * 25.4) - 20; | ||
|
||
/** | ||
* Emits when a distance sensor is activated. | ||
* @event ColorDistanceSensor#distance | ||
* @param {number} distance Distance, in millimeters. | ||
*/ | ||
this.emit("distance", distance); | ||
} | ||
break; | ||
|
||
case ColorDistanceSensor.Mode.COLOR_AND_DISTANCE: | ||
if (this.isWeDo2SmartHub) { | ||
break; | ||
} | ||
|
||
let distance = message[5]; | ||
const partial = message[7]; | ||
|
||
if (partial > 0) { | ||
distance += 1.0 / partial; | ||
} | ||
|
||
distance = Math.floor(distance * 25.4) - 20; | ||
|
||
/** | ||
* A combined color and distance event, emits when the sensor is activated. | ||
* @event ColorDistanceSensor#colorAndDistance | ||
* @param {Color} color | ||
* @param {number} distance Distance, in millimeters. | ||
*/ | ||
if (message[4] <= 10) { | ||
const color = message[4]; | ||
this.emit("colorAndDistance", color, distance); | ||
} | ||
break; | ||
const data = super.receive(message) || []; | ||
|
||
if (this._mode === "SPEC_1") { | ||
this.emit("color", data[0]); | ||
this.emit("distance", data[1]); | ||
this.emit("reflectivity", data[3]); | ||
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. The purpose of this is to defer the generic parsing to the device class then get back the data to handle only edge cases. However, this is not the same as the current state of the lib in term of events and data but I think that if we decide to break some things, it is now. |
||
} | ||
} | ||
|
||
} | ||
|
||
export namespace ColorDistanceSensor { | ||
|
||
export enum Mode { | ||
COLOR = 0x00, | ||
DISTANCE = 0x01, | ||
COLOR_AND_DISTANCE = 0x08 | ||
return data; | ||
} | ||
|
||
export const ModeMap: {[event: string]: number} = { | ||
"color": ColorDistanceSensor.Mode.COLOR, | ||
"distance": ColorDistanceSensor.Mode.DISTANCE, | ||
"colorAndDistance": ColorDistanceSensor.Mode.COLOR_AND_DISTANCE | ||
} | ||
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,26 +8,38 @@ export class Device extends EventEmitter { | |
|
||
public autoSubscribe: boolean = true; | ||
|
||
protected _mode: number | undefined; | ||
protected _mode: string | undefined; | ||
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 use mode names instead of number because number is not reliable over different hubs. |
||
protected _busy: boolean = false; | ||
protected _finished: (() => void) | undefined; | ||
|
||
private _hub: IDeviceInterface; | ||
private _portId: number; | ||
private _connected: boolean = true; | ||
private _type: Consts.DeviceType; | ||
private _modeMap: {[event: string]: number} = {}; | ||
private _modes: {[name: string]: Consts.IDeviceMode} = {}; | ||
private _eventMap: {[event: string]: string}; | ||
|
||
private _isWeDo2SmartHub: boolean; | ||
|
||
constructor (hub: IDeviceInterface, portId: number, modeMap: {[event: string]: number} = {}, type: Consts.DeviceType = Consts.DeviceType.UNKNOWN) { | ||
constructor (hub: IDeviceInterface, portId: number, modes: {[name: string]: Consts.IDeviceMode} = {}, type: Consts.DeviceType = Consts.DeviceType.UNKNOWN) { | ||
super(); | ||
this._hub = hub; | ||
this._portId = portId; | ||
this._type = type; | ||
this._modeMap = modeMap; | ||
this._modes = modes; | ||
this._isWeDo2SmartHub = (this.hub.type === Consts.HubType.WEDO2_SMART_HUB); | ||
|
||
this._eventMap = Object.keys(modes).reduce( | ||
(map: {[event: string]: string}, name) => { | ||
const mode = modes[name]; | ||
if (mode.num[hub.type] !== undefined && mode.event) { | ||
map[mode.event] = name; | ||
} | ||
return map; | ||
}, | ||
{} | ||
); | ||
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. Create an event to mode name map to subscribe from event |
||
|
||
const detachListener = (device: Device) => { | ||
if (device.portId === this.portId) { | ||
this._connected = false; | ||
|
@@ -42,9 +54,12 @@ export class Device extends EventEmitter { | |
return; | ||
} | ||
if (this.autoSubscribe) { | ||
if (this._modeMap[event] !== undefined) { | ||
this.subscribe(this._modeMap[event]); | ||
if (!this._eventMap[event]) { | ||
// TODO : error handling -> no mode for event | ||
return; | ||
} | ||
|
||
this.subscribe(this._eventMap[event]); | ||
} | ||
}); | ||
} | ||
|
@@ -78,16 +93,73 @@ export class Device extends EventEmitter { | |
this.hub.send(data, characteristic, callback); | ||
} | ||
|
||
public subscribe (mode: number) { | ||
public subscribe (modeName: string) { | ||
this._ensureConnected(); | ||
if (mode !== this._mode) { | ||
this._mode = mode; | ||
this.hub.subscribe(this.portId, this.type, mode); | ||
if (modeName !== this._mode) { | ||
this._mode = modeName; | ||
|
||
const modeNum = this._modes[modeName].num[this.hub.type]; | ||
if (modeNum === undefined) { | ||
// TODO : error handling -> unsupported mode | ||
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. Allow to handle hub related unsupported mode error |
||
return; | ||
} | ||
this.hub.subscribe(this.portId, this.type, modeNum); | ||
} | ||
} | ||
|
||
public receive (message: Buffer) { | ||
this.emit("receive", message); | ||
if (this._mode === undefined) { | ||
// TODO : error handling -> no mode defined | ||
return; | ||
} | ||
|
||
const mode = this._modes[this._mode]; | ||
|
||
if (!mode.values) { | ||
// TODO : error handling -> no parsing informations | ||
return; | ||
} | ||
|
||
const data = []; | ||
|
||
for (let index = 0; index <= message.length; index += Consts.ValueBits[mode.values.type]) { | ||
switch (mode.values.type) { | ||
case Consts.ValueType.UInt8: { | ||
data.push(message.readUInt8(index)); | ||
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. Should the raw value be changed to take into account min/max value at this point? |
||
break; | ||
} | ||
case Consts.ValueType.Int8: { | ||
data.push(message.readInt8(index)); | ||
break; | ||
} | ||
case Consts.ValueType.UInt16: { | ||
data.push(message.readUInt16LE(index)); | ||
break; | ||
} | ||
case Consts.ValueType.Int16: { | ||
data.push(message.readInt16LE(index)); | ||
break; | ||
} | ||
case Consts.ValueType.UInt32: { | ||
data.push(message.readUInt32LE(index)); | ||
break; | ||
} | ||
case Consts.ValueType.Int32: { | ||
data.push(message.readInt32LE(index)); | ||
break; | ||
} | ||
case Consts.ValueType.Float: { | ||
data.push(message.readFloatLE(index)); | ||
break; | ||
} | ||
} | ||
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. Because "We All Love Switches (TM)" :) 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. "This is the way." |
||
} | ||
|
||
if (mode.event) { | ||
this.emit(mode.event, ...data); | ||
} | ||
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. emit related event if any. This breaks the distance one as it does not apply calculation on the value and also the colorAndDistance which will receive color, distance (0-10), led color and reflectivity instead of only color and distance. |
||
|
||
return data; | ||
} | ||
|
||
public finish () { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -410,7 +410,8 @@ export class LPF2Hub extends Hub { | |
const device = this._getDeviceByPortId(portId); | ||
|
||
if (device) { | ||
device.receive(message); | ||
// Forward message to device (without size, hub and port) | ||
device.receive(message.slice(3)); | ||
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. Device does not need to get the full message, data is sufficient. 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. Also, it seems the offset is not the same for WEDO2, it would remove specifique parsing from devices. |
||
} | ||
|
||
// if (data[3] === this._voltagePort) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes devices objects very declarative.
It handle compatibility matrix, value parsing description and event relation to mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could add here some transformation function that get data array in parameters and return transformed array. It could do some calculation for distance or simply remove some values (color and distance).