Skip to content
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
12 changes: 6 additions & 6 deletions archive/debug-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { SandboxRunner } from "../server/services/sandbox-runner.ts";
(async () => {
const runner = new SandboxRunner();
console.log("initial state running=", runner.isRunning, "paused=", runner.isPaused);
runner.runSketch(
`
runner.runSketch({
code: `
void setup() {
Serial.begin(9600);
Serial.println("BOOTED");
Expand All @@ -18,10 +18,10 @@ import { SandboxRunner } from "../server/services/sandbox-runner.ts";
delay(100);
}
`,
(line) => { console.log("[RUNNER OUT]", line); },
(err) => { console.error("[RUNNER ERR]", err); },
(code) => { console.log("[RUNNER EXIT]", code); },
);
onOutput: (line) => { console.log("[RUNNER OUT]", line); },
onError: (err) => { console.error("[RUNNER ERR]", err); },
onExit: (code) => { console.log("[RUNNER EXIT]", code); },
});
setTimeout(() => {
console.log("[RUNNER] setting pin 2 to HIGH");
runner.setPinValue(2, 1);
Expand Down
12 changes: 6 additions & 6 deletions scripts/debug-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { SandboxRunner } from "../server/services/sandbox-runner.ts";
(async () => {
const runner = new SandboxRunner();
console.log("initial state running=", runner.isRunning, "paused=", runner.isPaused);
runner.runSketch(
`
runner.runSketch({
code: `
void setup() {
Serial.begin(9600);
Serial.println("BOOTED");
Expand All @@ -18,10 +18,10 @@ import { SandboxRunner } from "../server/services/sandbox-runner.ts";
delay(100);
}
`,
(line) => { console.log("[RUNNER OUT]", line); },
(err) => { console.error("[RUNNER ERR]", err); },
(code) => { console.log("[RUNNER EXIT]", code); },
);
onOutput: (line) => { console.log("[RUNNER OUT]", line); },
onError: (err) => { console.error("[RUNNER ERR]", err); },
onExit: (code) => { console.log("[RUNNER EXIT]", code); },
});
setTimeout(() => {
console.log("[RUNNER] setting pin 2 to HIGH");
runner.setPinValue(2, 1);
Expand Down
28 changes: 14 additions & 14 deletions server/routes/simulation.ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,20 +203,20 @@ export function registerSimulationWebSocket(httpServer: Server, deps: Simulation
logger.warn(`Could not stringify run payload for evidence: ${err instanceof Error ? err.message : String(err)}`);
}

// Call the legacy positional signature to preserve exact runtime behavior
clientState.runner.runSketch(
lastCompiledCode,
opts.onOutput,
opts.onError,
opts.onExit,
opts.onCompileError,
opts.onCompileSuccess,
opts.onPinState,
opts.timeoutSec,
opts.onIORegistry,
opts.onTelemetry,
opts.onPinStateBatch,
);
clientState.runner.runSketch({
code: lastCompiledCode,
onOutput: opts.onOutput,
onError: opts.onError,
onExit: opts.onExit,
onCompileError: opts.onCompileError,
onCompileSuccess: opts.onCompileSuccess,
onPinState: opts.onPinState,
timeoutSec: opts.timeoutSec,
onIORegistry: opts.onIORegistry,
onTelemetry: opts.onTelemetry,
onPinStateBatch: opts.onPinStateBatch,
context: opts.context,
});
}
break;

Expand Down
12 changes: 6 additions & 6 deletions server/services/local-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,19 +471,19 @@ export class LocalCompiler {
await import("fs/promises").then((fs) => fs.writeFile(src, ARDUINO_MOCK_CODE));

const obj = join(tmp, "sim-core.o");
await new Promise<void>(async (res, rej) => {
const { spawn } = await import("child_process");
await new Promise<void>((res, rej) => {
const { spawn } = require("child_process");
const proc = spawn("g++", ["-std=gnu++17", "-pthread", "-c", src, "-o", obj]);
try { const gs: any = (globalThis as any).spawnInstances; if (Array.isArray(gs)) gs.push(proc); } catch {}
proc.on("close", (code) => (code === 0 ? res() : rej(new Error("g++ native core compile failed"))));
proc.on("close", (code: number | null) => (code === 0 ? res() : rej(new Error("g++ native core compile failed"))));
proc.on("error", rej);
});

await new Promise<void>(async (res, rej) => {
const { spawn } = await import("child_process");
await new Promise<void>((res, rej) => {
const { spawn } = require("child_process");
const proc = spawn("ar", ["rcs", LocalCompiler.SIM_CACHE_PATH, obj]);
try { const gs: any = (globalThis as any).spawnInstances; if (Array.isArray(gs)) gs.push(proc); } catch {}
proc.on("close", (code) => (code === 0 ? res() : rej(new Error("ar archiving failed"))));
proc.on("close", (code: number | null) => (code === 0 ? res() : rej(new Error("ar archiving failed"))));
proc.on("error", rej);
});

Expand Down
50 changes: 5 additions & 45 deletions server/services/sandbox-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,48 +401,8 @@ export class SandboxRunner {

// Note: Duplicate flushMessageQueue removed - using single implementation above

async runSketch(...args: any[]) {
// Supports both new object-based signature and old positional args for backward compatibility.
// Normalize to RunSketchOptions object.
let opts: RunSketchOptions;
if (args.length === 1 && typeof args[0] === "object" && args[0] !== null && "code" in args[0]) {
opts = args[0] as RunSketchOptions;
} else {
const [
code,
onOutput,
onError,
onExit,
onCompileError,
onCompileSuccess,
onPinState,
timeoutSec,
onIORegistry,
onTelemetry,
onPinStateBatch,
] = args as any[];

opts = {
code,
onOutput,
onError,
onExit,
onCompileError,
onCompileSuccess,
onPinState,
timeoutSec,
onIORegistry,
onTelemetry,
onPinStateBatch,
} as RunSketchOptions;
}

// Evidence logging required by Task B1
try {
console.info("[B1-Evidence] Payload:", JSON.stringify(opts, null, 2));
} catch (err) {
this.logger.warn(`Could not stringify runSketch options for evidence: ${err instanceof Error ? err.message : String(err)}`);
}
async runSketch(options: RunSketchOptions) {
const opts = options;

// Extract stable variables for the rest of the method
const {
Expand All @@ -457,7 +417,7 @@ export class SandboxRunner {
onIORegistry,
onTelemetry,
onPinStateBatch,
} = opts as RunSketchOptions;
} = opts;

// Lazy initialization: ensure Docker is checked and temp directory exists
this.ensureDockerChecked();
Expand Down Expand Up @@ -1055,13 +1015,13 @@ export class SandboxRunner {
// Only stop batchers if we were actually RUNNING (not during mock test setup)
// In mock tests, close fires during setup before state reaches RUNNING
if (wasRunning) {
this.flushBatchers();

if (this.serialOutputBatcher) {
this.serialOutputBatcher.stop(); // Flushes pending data
this.serialOutputBatcher.destroy(); // Cleans up timer
this.serialOutputBatcher = null;
}
if (this.pinStateBatcher) {
this.pinStateBatcher.stop(); // Flushes pending states
this.pinStateBatcher.destroy(); // Cleans up timer
this.pinStateBatcher = null;
}
Expand Down
18 changes: 9 additions & 9 deletions tests/sandbox-stress.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ function runSketchHelper(
callbacks: RunSketchCallbacks,
timeoutSec?: number
) {
return runner.runSketch(
return runner.runSketch({
code,
callbacks.onOutput || (() => {}),
callbacks.onError || (() => {}),
callbacks.onExit || (() => {}),
callbacks.onCompileError,
callbacks.onCompileSuccess,
callbacks.onPinState,
onOutput: callbacks.onOutput || (() => {}),
onError: callbacks.onError || (() => {}),
onExit: callbacks.onExit || (() => {}),
onCompileError: callbacks.onCompileError,
onCompileSuccess: callbacks.onCompileSuccess,
onPinState: callbacks.onPinState,
timeoutSec,
callbacks.onIORegistry
);
onIORegistry: callbacks.onIORegistry,
});
}

// Store original setTimeout for non-test operations
Expand Down
43 changes: 18 additions & 25 deletions tests/server/pause-resume-digitalread.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,13 @@ maybeDescribe("Pause/Resume - digitalRead after Resume", () => {
};

// start simulation after listeners are ready
runner.runSketch(
runner.runSketch({
code,
onOutput,
onError,
() => {}, // onExit
undefined, // onCompileError
undefined, // onCompileSuccess
undefined,
10, // timeout
);
onExit: () => {},
timeoutSec: 10,
});

} catch (err) {
clearTimeout(timeout);
Expand Down Expand Up @@ -136,9 +133,9 @@ maybeDescribe("Pause/Resume - digitalRead after Resume", () => {
});
}, 15000);

runner.runSketch(
runner.runSketch({
code,
(line) => {
onOutput: (line) => {
output.push(line);
const fullOutput = output.join("");

Expand Down Expand Up @@ -183,19 +180,17 @@ maybeDescribe("Pause/Resume - digitalRead after Resume", () => {
});
}
},
(err) => {
onError: (err) => {
stderrLines.push(`[STDERR] ${err}`);
},
() => {
onExit: () => {
stderrLines.push(`[TEST] Process exited`);
},
undefined, // onCompileError
undefined, // onCompileSuccess
(pin, type, value) => {
onPinState: (pin, type, value) => {
stderrLines.push(`[PIN_STATE] pin=${pin}, type=${type}, value=${value}`);
},
30, // timeout
);
timeoutSec: 30,
});
});

// Print debug info BEFORE assertions
Expand Down Expand Up @@ -243,9 +238,9 @@ maybeDescribe("Pause/Resume - digitalRead after Resume", () => {
reject(new Error("Timeout - did not see expected pin values after resume"));
}, 30000);

runner.runSketch(
runner.runSketch({
code,
(line) => {
onOutput: (line) => {
output.push(line);
const fullOutput = output.join("");

Expand Down Expand Up @@ -285,22 +280,20 @@ maybeDescribe("Pause/Resume - digitalRead after Resume", () => {
resolve();
}
},
(err) => {
onError: (err) => {
if (err.includes("[[PIN_")) return;
if (err.includes("[[STDIN_RECV")) {
console.log("📍 C++ stdin:", err);
return;
}
console.error("Stderr:", err);
},
() => {},
undefined,
undefined,
(pin, type, value) => {
onExit: () => {},
onPinState: (pin, type, value) => {
console.log(`📍 Pin: ${pin}=${value} (${type})`);
},
30,
);
timeoutSec: 30,
});
});

const fullOutput = output.join("");
Expand Down
Loading
Loading