Skip to content

Commit

Permalink
Merge pull request #100 from noahm/nm/config-display
Browse files Browse the repository at this point in the history
Display current sensitivity settings
  • Loading branch information
noahm authored Nov 12, 2024
2 parents d0ae10a + 43b3afd commit c5f69e7
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 82 deletions.
13 changes: 8 additions & 5 deletions sdk/commands/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ const packed_panel_settings_t = new StructBuffer("packed_panel_settings_t", {
loadCellHighThreshold: uint8_t,

/**
* FSR Thresholds
* 4 Sensors per threshold
* Activation threshold when pressing.
* 4 values, one for each sensor on this panel.
*/
fsrLowThreshold: uint8_t[4],
/**
* Release threshold when lifting.
* 4 values, one for each sensor on this panel.
*/
fsrHighThreshold: uint8_t[4],

/**
Expand Down Expand Up @@ -357,14 +361,13 @@ export class SMXConfig {
*/
constructor(data: Uint8Array, firmwareVersion: number) {
this.firmwareVersion = firmwareVersion;
console.log("Config Firmware Version: ", this.firmwareVersion);
console.log("CONFIG RAW DATA: ", data.toString());
console.debug("CONFIG RAW DATA: ", data.toString());

if (this.firmwareVersion >= 5) {
this.config = smx_config_t.decode(data.slice(2, -1), true);
} else {
this.oldConfigSize = data[1];
console.log("Reading Old Config");
console.debug("Reading Old Config");

const slicedData = data.slice(2, -1);
// handle very old stage's smaller config data by padding
Expand Down
2 changes: 1 addition & 1 deletion sdk/commands/data_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class SMXDeviceInfo {
player = 0;

constructor(data: Uint8Array) {
console.log("DEVICEINFO RAW DATA: ", data.toString());
console.debug("DEVICEINFO RAW DATA: ", data.toString());
this.#decode(data);
}

Expand Down
2 changes: 1 addition & 1 deletion sdk/packet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export async function send_data(dev: HIDDevice, data: Uint8Array, debug = false)
// Send each packet
for (const packet of packets) {
if (debug) {
console.log("OUTGOING RAW PACKET: ", packet.toString());
console.debug("OUTGOING RAW PACKET: ", packet.toString());
}
await dev.sendReport(HID_REPORT_OUTPUT, packet);
}
Expand Down
12 changes: 5 additions & 7 deletions sdk/smx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ class SMXEvents {
.filter((e) => e.type === "host_cmd_finished")
.map((e) => e.type === "host_cmd_finished");

finishedCommand$.log("Cmd Finished");

const okSend$ = finishedCommand$.startWith(true);

// Main USB Output
Expand Down Expand Up @@ -101,7 +99,7 @@ export class SMXStage {

// write outgoing events to the device
this.events.eventsToSend$.onValue(async (value) => {
this.debug && console.log("writing to HID");
this.debug && console.debug("writing to HID");
await send_data(this.dev, value, this.debug);
});

Expand Down Expand Up @@ -241,9 +239,9 @@ export class SMXStage {
const encoded_config = this._config.encode();
if (encoded_config) {
this.debug &&
console.log("Config Encodes Correctly: ", data.slice(2, -1).toString() === encoded_config.toString());
console.debug("Config Encodes Correctly: ", data.slice(2, -1).toString() === encoded_config.toString());
}
this.debug && console.log("Got Config: ", this.config);
this.debug && console.info("Got Config: ", this.config);

return this._config;
}
Expand All @@ -252,15 +250,15 @@ export class SMXStage {
// biome-ignore lint/style/noNonNullAssertion: config should very much be defined here
this.test = new SMXSensorTestData(data, this.test_mode, this.config!.flags.PlatformFlags_FSR);

this.debug && console.log("Got Test: ", this.test);
this.debug && console.debug("Got Test: ", this.test);

return this.test;
}

private handleDeviceInfo(data: Uint8Array): SMXDeviceInfo {
this.info = new SMXDeviceInfo(data);

this.debug && console.log("Got Info: ", this.info);
this.debug && console.debug("Got Info: ", this.info);

return this.info;
}
Expand Down
12 changes: 6 additions & 6 deletions sdk/state-machines/collate-packets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,18 @@ export type Packet = { type: "host_cmd_finished" } | DataPacket | AckPacket;
export const collatePackets: StateF<DataView, PacketHandlingState, Packet> = (state, event) => {
// TODO: This whole function could maybe just use a bit more comments
if (!Bacon.hasValue(event)) {
console.log("No Event Value");
console.debug("No Event Value");
return [state, []];
}

let currentPacket = state.currentPacket;
const data = new Uint8Array(event.value.buffer);

console.log("INCOMING RAW PACKET: ", data.toString());
console.debug("INCOMING RAW PACKET: ", data.toString());

// Return if packet is empty
if (data.length <= PACKET_PREAMBLE_SIZE) {
console.log("Empty Packet");
console.debug("Empty Packet");
return [state, []];
}
const cmd = data[0];
Expand All @@ -46,12 +46,12 @@ export const collatePackets: StateF<DataView, PacketHandlingState, Packet> = (st
// we ignore the packet if we didn't request it, since it might be requested
// for a different program.
// TODO: Handle this? Not sure there's anything to handle here tbh
console.log("Found Packet Flag Device Info");
console.debug("Found Packet Flag Device Info");
}

if (PACKET_PREAMBLE_SIZE + byte_len > data.length) {
// TODO: Can this even happen???
console.log("Communication Error: Oversized Packet (ignored)");
console.warn("Communication Error: Oversized Packet (ignored)");
return [state, []];
}

Expand All @@ -71,7 +71,7 @@ export const collatePackets: StateF<DataView, PacketHandlingState, Packet> = (st
* This shouldn't happen, so warn about it and recover by clearing the junk in the buffer.
* TODO: Again, does this actually happen???!?
*/
console.log(
console.warn(
"Got PACKET_FLAG_OF_START_COMMAND, but we had ${current_packet.length} bytes in the buffer. Dropping it and continuing.",
);
currentPacket = new Uint8Array(0);
Expand Down
39 changes: 39 additions & 0 deletions ui/stage/config.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useAtomValue, type Atom } from "jotai";
import type { SMXStage } from "../../sdk";
import { useConfig } from "./hooks";

export function ConfigValues(props: { stageAtom: Atom<SMXStage | undefined> }) {
const stage = useAtomValue(props.stageAtom);
const config = useConfig(stage);

const ranges = config?.enabledSensors.flatMap((panel, idx) => {
if (!panel.some((sensor) => sensor)) {
return []; // skip panels will all disabled sensors
}

const { fsrHighThreshold: highs, fsrLowThreshold: lows } = config.panelSettings[idx];

return {
idx,
lows,
highs,
};
});

if (!ranges) {
return null;
}

return (
<>
<h3>Sensitivity settings</h3>
<ul>
{ranges.map((range) => (
<li key={range.idx}>
Panel {range.idx + 1}: {range.lows[0]} - {range.highs[0]}
</li>
))}
</ul>
</>
);
}
67 changes: 67 additions & 0 deletions ui/stage/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useAtomValue } from "jotai";
import { useState, useEffect, useRef } from "react";
import { type SMXStage, type SMXSensorTestData, SensorTestMode } from "../../sdk";
import { displayTestData$ } from "../state";

// UI Update Rate in Milliseconds
const UI_UPDATE_RATE = 50;

export function useInputState(stage: SMXStage | undefined) {
const readTestData = useAtomValue(displayTestData$);
const [panelStates, setPanelStates] = useState<Array<boolean> | null>();
useEffect(() => {
return stage?.inputState$.throttle(UI_UPDATE_RATE).onValue(setPanelStates);
}, [stage]);
return panelStates;
}

export function useTestData(stage: SMXStage | undefined) {
const testDataMode = useAtomValue(displayTestData$);
const [testData, setTestData] = useState<SMXSensorTestData | null>(null);

// request updates on an interval
useEffect(() => {
if (!stage || !testDataMode) {
return;
}
let testMode = SensorTestMode.UncalibratedValues;
switch (testDataMode) {
case "calibrated":
testMode = SensorTestMode.CalibratedValues;
break;
case "noise":
testMode = SensorTestMode.Noise;
break;
case "tare":
testMode = SensorTestMode.Tare;
}
const handle = setInterval(() => stage.updateTestData(testMode), UI_UPDATE_RATE);
return () => clearInterval(handle);
}, [stage, testDataMode]);

// ingest responses and display in UI
useEffect(() => {
return stage?.testDataResponse$.onValue(setTestData);
}, [stage]);

return testData;
}

export function useConfig(stage: SMXStage | undefined) {
const stageRef = useRef(stage);
const [configData, setConfig] = useState(stage?.config);

useEffect(() => {
if (stageRef.current !== stage) {
// detected the stage has changed, so keep the config in sync
setConfig(stage?.config);
stageRef.current = stage;
}
}, [stage]);

useEffect(() => {
return stage?.configResponse$.onValue((config) => setConfig(config.config));
}, [stage]);

return configData;
}
59 changes: 2 additions & 57 deletions ui/stage/stage-test.tsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,11 @@
import cn from "classnames";
import { useAtomValue, type Atom } from "jotai";
import type React from "react";
import { useEffect, useState } from "react";
import { FsrPanel } from "./fsr-panel";
import { type SMXStage, SensorTestMode, type SMXSensorTestData } from "../../sdk/";
import { displayTestData$ } from "../state";
import type { SMXStage } from "../../sdk/";
import { timez } from "./util";
import { LoadCellPanel } from "./load-cell-panel";

// UI Update Rate in Milliseconds
const UI_UPDATE_RATE = 50;

function useInputState(stage: SMXStage | undefined) {
const readTestData = useAtomValue(displayTestData$);
const [panelStates, setPanelStates] = useState<Array<boolean> | null>();
useEffect(() => {
return stage?.inputState$.throttle(UI_UPDATE_RATE).onValue(setPanelStates);
}, [stage]);
return panelStates;
}

function useTestData(stage: SMXStage | undefined) {
const testDataMode = useAtomValue(displayTestData$);
const [testData, setTestData] = useState<SMXSensorTestData | null>(null);

// request updates on an interval
useEffect(() => {
if (!stage || !testDataMode) {
return;
}
let testMode = SensorTestMode.UncalibratedValues;
switch (testDataMode) {
case "calibrated":
testMode = SensorTestMode.CalibratedValues;
break;
case "noise":
testMode = SensorTestMode.Noise;
break;
case "tare":
testMode = SensorTestMode.Tare;
}
const handle = setInterval(() => stage.updateTestData(testMode), UI_UPDATE_RATE);
return () => clearInterval(handle);
}, [stage, testDataMode]);

// ingest responses and display in UI
useEffect(() => {
return stage?.testDataResponse$.onValue(setTestData);
}, [stage]);

return testData;
}

function useConfig(stage: SMXStage | undefined) {
const [testData, setConfig] = useState(stage?.config);

useEffect(() => {
return stage?.configResponse$.onValue((config) => setConfig(config.config));
}, [stage]);

return testData;
}
import { useTestData, useInputState, useConfig } from "./hooks";

export function StageTest({
stageAtom,
Expand Down
17 changes: 12 additions & 5 deletions ui/ui.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useAtomValue, useAtom } from "jotai";
import { useAtomValue, useAtom, type Atom } from "jotai";
import type React from "react";
import { useEffect } from "react";
import { useEffect, useId } from "react";

import { DebugCommands } from "./DebugCommands.tsx";
import { open_smx_device, promptSelectDevice } from "./pad-coms.ts";
Expand All @@ -14,14 +14,15 @@ import {
} from "./state.ts";
import { StageTest } from "./stage/stage-test.tsx";
import { TypedSelect } from "./common/typed-select.tsx";
import type { SMXStage } from "../sdk/smx.ts";
import { ConfigValues } from "./stage/config.tsx";

function usePreviouslyPairedDevices() {
useEffect(() => {
// once, on load, get paired devices and attempt connection
if (browserSupported) {
navigator.hid.getDevices().then((devices) =>
devices.map((device) => {
console.log(`Found device: ${device.productName}`);
open_smx_device(device);
}),
);
Expand All @@ -42,6 +43,7 @@ export function UI() {
<p>
<TestDataDisplayToggle />
</p>
<ConfigValues stageAtom={selectedStage$} />
<StatusDisplay />
<footer>
A project of Cathadan and SenPi. This tool is unofficial and not affiliated with Step Revolution. Want to help?{" "}
Expand Down Expand Up @@ -85,14 +87,19 @@ function PickDevice() {

function StatusDisplay() {
const statusText = useAtomValue(statusText$);
return <pre>{statusText}</pre>;
return (
<>
<h3>Event Log</h3>
<pre>{statusText}</pre>
</>
);
}

function TestDataDisplayToggle() {
const [testMode, setTestMode] = useAtom(displayTestData$);

return (
// biome-ignore lint/a11y/noLabelWithoutControl: <explanation>
// biome-ignore lint/a11y/noLabelWithoutControl: the control is in the TypedSelect
<label>
Read Test Values:{" "}
<TypedSelect
Expand Down

0 comments on commit c5f69e7

Please sign in to comment.