Skip to content
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

hubinfo tool #51

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bin/hubinfo
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
require("../dist/node/hubinfo").main(process.argv.slice(2));
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,8 @@
"typescript": "^3.7.2",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10"
},
"bin": {
"lpf2-hubinfo": "./bin/hubinfo"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency with this library name, could we call it poweredup-hubinfo? I'm not sure LPF2 is the right name to use given that its Lego internal only, and most people know the technology as Powered UP.

}
}
1 change: 0 additions & 1 deletion src/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export class Hub extends EventEmitter {
});
}


/**
* @readonly
* @property {string} name Name of the hub
Expand Down
85 changes: 85 additions & 0 deletions src/hubinfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import * as Consts from "./consts";
import { Hub } from "./hub";
import { LPF2Hub } from "./lpf2hub";
import { PoweredUP } from "./poweredup-node";
import { toBin, toHex } from "./utils";

function sanitizeString(s: string) {
return s.replace(/\0/g, " ").replace(/ +$/, "");
}

export function main(argv: string[]) {
let portInfo = false;
let quiet = false;
for (const arg of argv) {
switch (arg) {
case "-p":
case "--portinfo":
portInfo = true;
break;
case "-q":
case "--quiet":
quiet = true;
break;
default:
console.log("Usage: lpf2-hubinfo [-p|--portinfo] [-q|--quiet]");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The process never quits, likely as Noble is still running in the background. Can you add something like process.exit(0);?

return;
}
}
if (portInfo) {
LPF2Hub.requestPortModeInfo = true;
}
const pup = new PoweredUP();
pup.on("discover", async (hub: Hub) => {
const hubName = sanitizeString(hub.name);
if (!quiet) {
console.log(`Discovered ${hub.uuid} (${hubName})`);
}
await hub.connect();
hub.on("portInfo", ({ port, type, hardwareVersion, softwareVersion }) => {
const typeName = Consts.DeviceTypeNames[type] || "unknown";
console.log(`${hub.uuid} Port ${toHex(port)}, type ${toHex(type, 4)} (${typeName})`);
console.log(`${hub.uuid} Port ${toHex(port)}, hardware v${hardwareVersion}, software v${softwareVersion}`);
});
hub.on("portModes", ({ port, count, input, output }) => {
console.log(`${hub.uuid} Port ${toHex(port)}, total modes ${count}, input modes ${toBin(input, count)}, output modes ${toBin(output, count)}`);
});
hub.on("portModeCombinations", ({ port, modeCombinationMasks }) => {
console.log(`${hub.uuid} Port ${toHex(port)}, mode combinations [${modeCombinationMasks.map((c: number) => toBin(c, 0)).join(", ")}]`);
});
hub.on("portModeInfo", (info) => {
const { port, mode, type } = info;
const prefix = `${hub.uuid} Port ${toHex(port)}, mode ${mode}`;
switch (type) {
case 0x00: // Mode Name
console.log(`${prefix}, name ${sanitizeString(info.name)}`);
break;
case 0x01: // RAW Range
console.log(`${prefix}, RAW min ${info.min}, max ${info.max}`);
break;
case 0x02: // PCT Range
console.log(`${prefix}, PCT min ${info.min}, max ${info.max}`);
break;
case 0x03: // SI Range
console.log(`${prefix}, SI min ${info.min}, max ${info.max}`);
break;
case 0x04: // SI Symbol
console.log(`${prefix}, SI symbol ${sanitizeString(info.name)}`);
break;
case 0x80: // Value Format
console.log(`${prefix}, Value ${info.numValues} x ${info.dataType}, Decimal format ${info.decimalFormat}`);
break;
}
});
await hub.sleep(2000);
const typeName = Consts.HubTypeNames[hub.type] || `unknown (${toHex(hub.type)})`;
console.log(`${hub.uuid} firmware v${hub.firmwareVersion} hardware v${hub.hardwareVersion} ${typeName} (${hubName})`);
if (hub instanceof LPF2Hub && !portInfo) {
await hub.shutdown();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add another process.exit(0); here so that the process terminates after shutting down the hub?

}
});
pup.scan();
if (!quiet) {
console.log("Waiting for hubs...");
}
}
40 changes: 17 additions & 23 deletions src/lpf2hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import { Hub } from "./hub";
import { Port } from "./port";

import * as Consts from "./consts";
import { toBin, toHex } from "./utils";
import { toHex } from "./utils";

import Debug = require("debug");
const debug = Debug("lpf2hub");
const modeInfoDebug = Debug("lpf2hubmodeinfo");


/**
* @class LPF2Hub
* @extends Hub
*/
export class LPF2Hub extends Hub {
public static requestPortModeInfo = false;

private static decodeVersion(v: number) {
const t = v.toString(16).padStart(8, "0");
return [t[0], t[1], t.substring(2, 4), t.substring(4)].join(".");
Expand Down Expand Up @@ -294,12 +295,10 @@ export class LPF2Hub extends Hub {
let port = this._getPortForPortNumber(data[3]);
const type = data[4] ? data.readUInt16LE(5) : 0;

if (data[4] === 0x01 && modeInfoDebug.enabled) {
const typeName = Consts.DeviceTypeNames[data[5]] || "unknown";
modeInfoDebug(`Port ${toHex(data[3])}, type ${toHex(type, 4)} (${typeName})`);
const hwVersion = LPF2Hub.decodeVersion(data.readInt32LE(7));
const swVersion = LPF2Hub.decodeVersion(data.readInt32LE(11));
modeInfoDebug(`Port ${toHex(data[3])}, hardware version ${hwVersion}, software version ${swVersion}`);
if (data[4] === 0x01 && LPF2Hub.requestPortModeInfo) {
const hardwareVersion = LPF2Hub.decodeVersion(data.readInt32LE(7));
const softwareVersion = LPF2Hub.decodeVersion(data.readInt32LE(11));
this.emit("portInfo", { port: data[3], type, hardwareVersion, softwareVersion });
this._sendPortInformationRequest(data[3]);
}

Expand Down Expand Up @@ -343,13 +342,13 @@ export class LPF2Hub extends Hub {
for (let i = 5; i < data.length; i += 2) {
modeCombinationMasks.push(data.readUInt16LE(i));
}
modeInfoDebug(`Port ${toHex(port)}, mode combinations [${modeCombinationMasks.map((c) => toBin(c, 0)).join(", ")}]`);
this.emit("portModeCombinations", { port, modeCombinationMasks });
return;
}
const count = data[6];
const input = toBin(data.readUInt16LE(7), count);
const output = toBin(data.readUInt16LE(9), count);
modeInfoDebug(`Port ${toHex(port)}, total modes ${count}, input modes ${input}, output modes ${output}`);
const input = data.readUInt16LE(7);
const output = data.readUInt16LE(9);
this.emit("portModes", { port, count, input, output });

for (let i = 0; i < count; i++) {
this._sendModeInformationRequest(port, i, 0x00); // Mode Name
Expand All @@ -368,31 +367,26 @@ export class LPF2Hub extends Hub {


private _parseModeInformationResponse (data: Buffer) {
const port = toHex(data[3]);
const port = data[3];
const mode = data[4];
const type = data[5];
switch (type) {
case 0x00: // Mode Name
modeInfoDebug(`Port ${port}, mode ${mode}, name ${data.slice(6, data.length).toString()}`);
case 0x04: // SI Symbol
this.emit("portModeInfo", { port, mode, type, name: data.slice(6, data.length).toString() });
break;
case 0x01: // RAW Range
modeInfoDebug(`Port ${port}, mode ${mode}, RAW min ${data.readFloatLE(6)}, max ${data.readFloatLE(10)}`);
break;
case 0x02: // PCT Range
modeInfoDebug(`Port ${port}, mode ${mode}, PCT min ${data.readFloatLE(6)}, max ${data.readFloatLE(10)}`);
break;
case 0x03: // SI Range
modeInfoDebug(`Port ${port}, mode ${mode}, SI min ${data.readFloatLE(6)}, max ${data.readFloatLE(10)}`);
break;
case 0x04: // SI Symbol
modeInfoDebug(`Port ${port}, mode ${mode}, SI symbol ${data.slice(6, data.length).toString()}`);
this.emit("portModeInfo", { port, mode, type, min: data.readFloatLE(6), max: data.readFloatLE(10) });
break;
case 0x80: // Value Format
const numValues = data[6];
const dataType = ["8bit", "16bit", "32bit", "float"][data[7]];
const totalFigures = data[8];
const decimals = data[9];
modeInfoDebug(`Port ${port}, mode ${mode}, Value ${numValues} x ${dataType}, Decimal format ${totalFigures}.${decimals}`);
this.emit("portModeInfo", { port, mode, type, numValues, dataType, decimalFormat: `${totalFigures}.${decimals}`});
break;
}
}

Expand Down