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

Display current sensitivity settings #100

Merged
merged 4 commits into from
Nov 12, 2024
Merged
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
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