diff --git a/sdk/commands/config.ts b/sdk/commands/config.ts index 687c1e5..b725877 100644 --- a/sdk/commands/config.ts +++ b/sdk/commands/config.ts @@ -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], /** @@ -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 diff --git a/sdk/commands/data_info.ts b/sdk/commands/data_info.ts index de4e3b2..a8359df 100644 --- a/sdk/commands/data_info.ts +++ b/sdk/commands/data_info.ts @@ -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); } diff --git a/sdk/packet.ts b/sdk/packet.ts index 8bc627f..c1c3ab2 100644 --- a/sdk/packet.ts +++ b/sdk/packet.ts @@ -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); } diff --git a/sdk/smx.ts b/sdk/smx.ts index 962029d..155faf3 100644 --- a/sdk/smx.ts +++ b/sdk/smx.ts @@ -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 @@ -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); }); @@ -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; } @@ -252,7 +250,7 @@ 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; } @@ -260,7 +258,7 @@ export class SMXStage { 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; } diff --git a/sdk/state-machines/collate-packets.ts b/sdk/state-machines/collate-packets.ts index 9417652..4820d90 100644 --- a/sdk/state-machines/collate-packets.ts +++ b/sdk/state-machines/collate-packets.ts @@ -24,18 +24,18 @@ export type Packet = { type: "host_cmd_finished" } | DataPacket | AckPacket; export const collatePackets: StateF = (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]; @@ -46,12 +46,12 @@ export const collatePackets: StateF = (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, []]; } @@ -71,7 +71,7 @@ export const collatePackets: StateF = (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); diff --git a/ui/stage/config.tsx b/ui/stage/config.tsx new file mode 100644 index 0000000..1a9e3fc --- /dev/null +++ b/ui/stage/config.tsx @@ -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 }) { + 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 ( + <> +

Sensitivity settings

+
    + {ranges.map((range) => ( +
  • + Panel {range.idx + 1}: {range.lows[0]} - {range.highs[0]} +
  • + ))} +
+ + ); +} diff --git a/ui/stage/hooks.ts b/ui/stage/hooks.ts new file mode 100644 index 0000000..97a055e --- /dev/null +++ b/ui/stage/hooks.ts @@ -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 | 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(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; +} diff --git a/ui/stage/stage-test.tsx b/ui/stage/stage-test.tsx index e976102..b305f9c 100644 --- a/ui/stage/stage-test.tsx +++ b/ui/stage/stage-test.tsx @@ -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 | 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(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, diff --git a/ui/ui.tsx b/ui/ui.tsx index c5538a2..559a9dd 100644 --- a/ui/ui.tsx +++ b/ui/ui.tsx @@ -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"; @@ -14,6 +14,8 @@ 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(() => { @@ -21,7 +23,6 @@ function usePreviouslyPairedDevices() { if (browserSupported) { navigator.hid.getDevices().then((devices) => devices.map((device) => { - console.log(`Found device: ${device.productName}`); open_smx_device(device); }), ); @@ -42,6 +43,7 @@ export function UI() {

+