Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | 12x 12x 86x 86x 86x 61x 61x 61x 61x 61x 61x 61x 61x 61x 2x 61x 61x 61x 61x 61x 71x 62x 61x 61x 59x 61x 59x 2x 61x 61x | /**
* Sketch File Builder
*
* Handles the construction of Arduino sketch files by wrapping user code
* with the Arduino mock implementation and generating appropriate main() wrappers.
*/
import { join } from "path";
import { mkdir, writeFile } from "fs/promises";
import { ARDUINO_MOCK_CODE } from "../mocks/arduino-mock";
import { Logger } from "@shared/logger";
export interface SketchBuildResult {
sketchDir: string;
sketchFile: string;
exeFile: string;
}
export class SketchFileBuilder {
private logger = new Logger("SketchFileBuilder");
private createdSketchDirs = new Set<string>();
constructor(private tempDir: string) {}
/**
* Builds a complete sketch file with Arduino mock and user code
*
* @param code - User's Arduino code
* @param sketchId - Unique identifier for this sketch
* @returns Paths to sketch directory and files
*/
async build(code: string, sketchId: string): Promise<SketchBuildResult> {
const sketchDir = join(this.tempDir, sketchId);
const sketchFile = join(sketchDir, "sketch.cpp");
const exeFile = join(sketchDir, "sketch");
try {
await mkdir(sketchDir, { recursive: true });
this.createdSketchDirs.add(sketchDir);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
this.logger.error(`Failed to create sketch directory: ${msg}`);
throw err;
}
const hasSetup = /void\s+setup\s*\([^)]*\)/.test(code);
const hasLoop = /void\s+loop\s*\([^)]*\)/.test(code);
if (!hasSetup && !hasLoop) {
this.logger.warn(
"Weder setup() noch loop() gefunden - Code wird nur als Bibliothek kompiliert",
);
}
const footer = this.buildFooter(hasSetup, hasLoop);
const cleanedCode = code.replace(/#include\s*[<"]Arduino\.h[>"]/g, "");
const combined = `${ARDUINO_MOCK_CODE}\n\n// --- User code follows ---\n${cleanedCode}\n\n// --- Footer ---\n${footer}`;
await writeFile(sketchFile, combined);
return { sketchDir, sketchFile, exeFile };
}
getCreatedSketchDirs(): string[] {
return Array.from(this.createdSketchDirs);
}
clearCreatedSketchDir(dir: string): void {
this.createdSketchDirs.delete(dir);
}
/**
* Generates the main() wrapper based on presence of setup() and loop()
*/
private buildFooter(hasSetup: boolean, hasLoop: boolean): string {
let footer = `
#include <thread>
#include <atomic>
#include <cstring>
#include <chrono>
int main() {
// Initialize IO registry for pin state tracking
initIORegistry();
// Start background thread for serial input
std::thread readerThread(serialInputReader);
readerThread.detach();
`;
if (hasSetup) {
footer += `
// Call user's setup() function
setup();
Serial.flush();
`;
}
if (hasLoop) {
footer += `
// Run user's loop() function continuously
bool __registry_sent = false;
while (1) {
Serial.flush();
loop();
// Send registry after first loop iteration
if (!__registry_sent) {
Serial.flush();
outputIORegistry();
__registry_sent = true;
}
// Sleep 1ms to prevent 100% CPU usage (Arduino runs at ~16MHz, so 1ms is reasonable throttle)
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
`;
} else {
footer += `
// No loop() function, just output registry once
outputIORegistry();
`;
}
footer += `
Serial.flush();
// Cleanup: stop serial input reader
keepReading.store(false);
return 0;
}
`;
return footer;
}
}
|