diff --git a/sdk/commands/config.ts b/sdk/commands/config.ts index 687c1e5..213a534 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], /** 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..7c2aa39 100644 --- a/sdk/smx.ts +++ b/sdk/smx.ts @@ -101,7 +101,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 +241,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 +252,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 +260,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..400779e --- /dev/null +++ b/ui/stage/hooks.ts @@ -0,0 +1,58 @@ +import { useAtomValue } from "jotai"; +import { useState, useEffect } 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 [configData, setConfig] = useState(stage?.config); + + 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..d7f513f 100644 --- a/ui/ui.tsx +++ b/ui/ui.tsx @@ -1,4 +1,4 @@ -import { useAtomValue, useAtom } from "jotai"; +import { useAtomValue, useAtom, type Atom } from "jotai"; import type React from "react"; import { useEffect } from "react"; @@ -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(() => { @@ -42,6 +44,7 @@ export function UI() {

+