Skip to content

Commit

Permalink
Various Fixes
Browse files Browse the repository at this point in the history
- Pass in `isFsr` to test data parsing
- Throttle stage panel inputs in UI for better reactivity
- General cleanup
  • Loading branch information
fchorney committed Apr 7, 2024
1 parent 6de6bdc commit 1f014cd
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 100 deletions.
21 changes: 8 additions & 13 deletions sdk/commands/sensor_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class SMXPanelTestData {
dip_switch_value = -1;
bad_jumper: Array<boolean> = Array(SENSOR_COUNT).fill(false);

constructor(data: Decoded<typeof detail_data_t>, mode: SensorTestMode) {
constructor(data: Decoded<typeof detail_data_t>, mode: SensorTestMode, isFsr: boolean) {
/**
* Check the header. this is always `false true false` or `0 1 0` to identify it as a response,
* and not as random steps from the player.
Expand Down Expand Up @@ -134,29 +134,24 @@ export class SMXPanelTestData {
* These are signed as they can be negative, but I imagine them going
* negative is just kind of noise from the hardware.
*/
this.sensor_level = data.sensors.map((value) => this.clamp_sensor_value(value, mode));
this.sensor_level = data.sensors.map((value) => this.clamp_sensor_value(value, mode, isFsr));
}

private clamp_sensor_value(value: number, mode: SensorTestMode) {
private clamp_sensor_value(value: number, mode: SensorTestMode, isFsr: boolean) {
if (mode === SensorTestMode.Noise) {
/**
* In Noise mode, we receive standard deviation values squared.
* Display the square root, since the panels don't do this for us.
* This makes the number different than the configured value
* (square it to convert back), but without this we display a bunch
* of four and five digit numbers that are too hard to read.
*
* TODO: Do we want to round this value or just display decimal values?
*/
return Math.sqrt(value);
}

// TODO: We need a way to pass in if the stage we are getting this data for
// is using FSRs or not. Defined as `true` for now.
const isFSR = true;

// TODO: This may be necessary for defining sensor value vertial bars in the UI
// const max_value = isFSR ? 250 : 500;
// This will probably go in the UI and not here
// const max_value = isFsr ? 250 : 500;

let clamped_value = value;
/**
Expand All @@ -169,7 +164,7 @@ export class SMXPanelTestData {
}

// FSR values are bitshifted right by 2 (effectively a div by 4).
if (isFSR) {
if (isFsr) {
clamped_value >>= 2;
}

Expand All @@ -180,7 +175,7 @@ export class SMXPanelTestData {
export class SMXSensorTestData {
panels: Array<SMXPanelTestData> = [];

constructor(data: Array<number>, mode: SensorTestMode) {
constructor(data: Array<number>, mode: SensorTestMode, isFsr: boolean) {
/**
* The first 3 bytes are the preamble.
*
Expand Down Expand Up @@ -236,7 +231,7 @@ export class SMXSensorTestData {
}

// Generate an SMXPanelTestData object for each panel
this.panels.push(new SMXPanelTestData(detail_data_t.decode(out_bytes, true), data_mode));
this.panels.push(new SMXPanelTestData(detail_data_t.decode(out_bytes, true), data_mode, isFsr));
}
}
}
74 changes: 14 additions & 60 deletions sdk/packet.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,5 @@
import { pad_packet } from "./utils.ts";

/**
* Gets the next report from the device matching a given reportId,
* wrapped in a promise for convenience. Times out after 500ms looking
* for a response.
*/
export function nextReportCommand(dev: HIDDevice): Promise<DataView> {
return new Promise((resolve, reject) => {
// set a 500ms timeout
// TODO: Should this be longer?
const timeoutHandle = window.setTimeout(() => {
// stop listening
dev.removeEventListener("inputreport", handleReport);
reject("timeout");
}, 500);

function handleReport(event: HIDInputReportEvent) {
// TODO other more specific filtering here?
if (event.reportId !== HID_REPORT_INPUT) {
return; // not the report we're looking for
}
// stop listening
dev.removeEventListener("inputreport", handleReport);
// stop timeout
clearTimeout(timeoutHandle);
// return data to caller
resolve(event.data);
}

dev.addEventListener("inputreport", handleReport);
});
}

/*
StepManiaX Stages expect packets that are exactly 64-bytes in length.
Expand All @@ -43,7 +11,7 @@ Thus, we're going to set the packet length to 63, since the Report ID will
be added to the data going out, making it 64 bits.
*/
export const MAX_PACKET_SIZE = 63;
const PACKET_PREAMBLE_SIZE = 2;
export const PACKET_PREAMBLE_SIZE = 2;

// USB Communication Packet Flags
export const PACKET_FLAG_START_OF_COMMAND = 0x04;
Expand All @@ -56,9 +24,19 @@ export const HID_REPORT_INPUT_STATE = 0x03;
export const HID_REPORT_OUTPUT = 0x05;
export const HID_REPORT_INPUT = 0x06;

// Acknowledge Code
// TODO: Decide what to do with this
const ACK_COMMAND = 0x7;
export async function send_data(dev: HIDDevice, data: Array<number>, debug = false) {
// Split data into packets
const packets = make_packets(data);

if (debug) {
console.log("Sending Packets: ", packets);
}

// Send each packet
for (const packet of packets) {
await dev.sendReport(HID_REPORT_OUTPUT, packet);
}
}

export function make_packets(data: Array<number>): Array<Uint8Array> {
const packets = [];
Expand Down Expand Up @@ -100,27 +78,3 @@ export function make_packets(data: Array<number>): Array<Uint8Array> {

return packets;
}

export async function makeSpecialDevicePacket(dev: HIDDevice, debug = false) {
const packet = pad_packet([PACKET_FLAG_DEVICE_INFO]);

if (debug) {
console.log("Sending Packets: ", packet);
}

await dev.sendReport(HID_REPORT_OUTPUT, packet);
}

export async function send_data(dev: HIDDevice, data: Array<number>, debug = false) {
// Split data into packets
const packets = make_packets(data);

if (debug) {
console.log("Sending Packets: ", packets);
}

// Send each packet
for (const packet of packets) {
await dev.sendReport(HID_REPORT_OUTPUT, packet);
}
}
32 changes: 14 additions & 18 deletions sdk/smx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ class SMXEvents {
.filter((e) => e.type === "host_cmd_finished")
.map((e) => e.type === "host_cmd_finished");

// this.otherReports$.onValue((value) => console.log("Packet: ", value));

finishedCommand$.log("Cmd Finished");
// finishedCommand$.log("Cmd Finished");

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

Expand All @@ -76,7 +74,7 @@ export class SMXStage {
test: SMXSensorTestData | null = null;
inputs: Array<boolean> | null = null;
private test_mode: SensorTestMode = SensorTestMode.CalibratedValues; // TODO: Maybe we just let this be public
private debug = true;
private debug = false;

private configResponse$: Bacon.EventStream<SMXConfig>;

Expand All @@ -86,8 +84,8 @@ export class SMXStage {

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

// Set the device info handler
Expand All @@ -112,13 +110,7 @@ export class SMXStage {
}

async init(): Promise<SMXConfig> {
/**
* This is a special RequestDeviceInfo packet. This is the same as sending an
* 'i' command, but we can send it safely at any time, even if another
* application is talking to the device. Thus we can do this during enumeration.
*/
//await requestSpecialDeviceInfo(this.dev); // Modify `send_data` to accept this somehow?

// Request the device information
this.updateDeviceInfo();

// Request some initial test data
Expand Down Expand Up @@ -151,22 +143,26 @@ export class SMXStage {
const encoded_config = this.config.encode();
if (encoded_config) {
const buf = new Uint8Array(encoded_config.buffer);
console.log("Config Encodes Correctly: ", data.slice(2, -1).toString() === buf.toString());
this.debug && console.log("Config Encodes Correctly: ", data.slice(2, -1).toString() === buf.toString());
}
console.log("Got Config: ", this.config);
this.debug && console.log("Got Config: ", this.config);
return this.config;
}

private handleTestData(data: Uint8Array) {
this.test = new SMXSensorTestData(Array.from(data), this.test_mode);
this.test = new SMXSensorTestData(
Array.from(data),
this.test_mode,
this.config?.config?.flags?.PlatformFlags_FSR || true,
);

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

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

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

private handleInputs(data: Decoded<typeof StageInputs>) {
Expand Down
7 changes: 4 additions & 3 deletions sdk/state-machines/collate-packets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
PACKET_FLAG_END_OF_COMMAND,
PACKET_FLAG_HOST_CMD_FINISHED,
PACKET_FLAG_START_OF_COMMAND,
PACKET_PREAMBLE_SIZE,
} from "../packet";

interface PacketHandlingState {
Expand All @@ -18,6 +19,7 @@ export type Packet = { type: "host_cmd_finished" } | DataPacket;
* Gets called when a packet is received, returns a tuple of new state and an array of
*/
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");
return [state, []];
Expand All @@ -29,7 +31,7 @@ export const collatePackets: StateF<DataView, PacketHandlingState, Packet> = (st
// console.log("Raw Packet Data: ", data);

// Return if packet is empty
if (data.length <= 3) {
if (data.length <= PACKET_PREAMBLE_SIZE) {
console.log("Empty Packet");
return [state, []];
}
Expand All @@ -44,8 +46,7 @@ export const collatePackets: StateF<DataView, PacketHandlingState, Packet> = (st
console.log("Found Packet Flag Device Info");
}

// TODO: Make some consts for these 2's everywhere
if (2 + byte_len > data.length) {
if (PACKET_PREAMBLE_SIZE + byte_len > data.length) {
// TODO: Can this even happen???
console.log("Communication Error: Oversized Packet (ignored)");
return [state, []];
Expand Down
22 changes: 16 additions & 6 deletions ui/stage/stage-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,24 @@ import { FsrPanel } from "./fsr-panel";
import { type SMXStage, SensorTestMode, type SMXPanelTestData, type SMXSensorTestData } from "../../sdk/";
import { displayTestData$ } from "../state";

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

function useInputState(stage: SMXStage | undefined) {
const [panelStates, setPanelStates] = useState<Array<boolean> | undefined>();
const inputs = stage?.inputs || undefined;
const [panelStates, setPanelStates] = useState<Array<boolean> | null>();

useEffect(() => {
if (!stage) return;
return setPanelStates(inputs); //TODO: Figure out why this feels laggy?
}, [stage, inputs]);

const d = stage;
async function update() {
setPanelStates(d.inputs);
}

const handle = setInterval(update, UI_UPDATE_RATE);
return () => clearInterval(handle);
}, [stage]);

return panelStates;
}

Expand All @@ -30,7 +40,7 @@ function useTestData(stage: SMXStage | undefined) {
setTestData(d.test);
}

const handle = setInterval(update, 50);
const handle = setInterval(update, UI_UPDATE_RATE);
return () => clearInterval(handle);
}, [stage, readTestData]);

Expand All @@ -46,7 +56,7 @@ export function StageTest({
const testData = useTestData(stage);
const inputState = useInputState(stage);

if (!testData) {
if (!testData || !inputState) {
return null;
}

Expand Down

0 comments on commit 1f014cd

Please sign in to comment.