Runtime#
The Runtime module provides control over JavaScript script execution, environment management, and runtime behavior configuration.
Functions#
runtime.exit([code])#
Exits the current JavaScript script with an optional exit code.
Parameters:
| Parameter | Type | Description |
|---|---|---|
code |
number, optional |
Exit code (default: 0) |
Returns: Does not return (terminates script)
Example:
runtime.exec(scriptPath)#
Executes another JavaScript file from the current script.
Parameters:
| Parameter | Type | Description |
|---|---|---|
scriptPath |
string |
Path to the JavaScript file to execute |
Returns: boolean - True if script executed successfully, false otherwise
Example:
Script Management System#
Script Launcher#
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 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | |
Script Creation and Editing#
function createNewScript() {
const name = keyboard.keyboard("Script name:", "new_script.js");
if (!name) return;
let filename = name;
if (!filename.endsWith(".js")) {
filename += ".js";
}
const template = dialog.choice([
"Blank Script",
"Hello World",
"GPIO Control",
"Display Demo",
"File Operations",
"Custom Template"
]);
let content = "";
switch (template) {
case 0: // Blank Script
content = `// ${filename}
// Created: ${Date.now()}
console.log("Script started");
// Your code here
console.log("Script completed");
`;
break;
case 1: // Hello World
content = `// Hello World Example
console.log("Hello, World!");
display.fill(0);
display.print(0, 0, "Hello World!", 2);
dialog.info("Hello from JavaScript!");
`;
break;
case 2: // GPIO Control
content = "// GPIO Control Example\nvar LED_PIN = 2;\n\n// Configure LED pin\ngpio.mode(LED_PIN, 'output');\n\n// Blink LED\nfor (var i = 0; i < 10; i++) {\n gpio.write(LED_PIN, true);\n delay(500);\n gpio.write(LED_PIN, false);\n delay(500);\n}\n\nconsole.log('GPIO demo completed');\n";
break;
case 3: // Display Demo
content = "// Display Demo\ndisplay.fill(0);\n\n// Title\ndisplay.print(0, 0, 'Display Demo', 2);\n\n// Show counter\nfor (var i = 1; i <= 10; i++) {\n display.drawFillRect(0, 25, 128, 15, 0); // Clear counter area\n display.print(0, 25, 'Count: ' + i, 1);\n delay(500);\n}\n\ndisplay.print(0, 40, 'Demo Complete!', 1);\ndelay(2000);\n";
break;
case 4: // File Operations
content = `// File Operations Example
const testFile = "/sd/test_output.txt";
const testData = "Hello from script!\\nTimestamp: " + Date.now();
// Write file
if (storage.write(testFile, testData)) {
console.log("File written successfully");
// Read file back
const readData = storage.read(testFile);
if (readData) {
console.log("File content:", readData);
dialog.info("File operations successful!");
} else {
dialog.error("Failed to read file");
}
} else {
dialog.error("Failed to write file");
}
`;
break;
case 5: // Custom Template
content = keyboard.textArea("Enter script content:", "// Custom script\\nconsole.log('Hello');");
break;
}
const scriptPath = `/sd/BruceJS/${filename}`;
if (storage.write(scriptPath, content)) {
dialog.success(`Script created: ${scriptPath}`);
const runNow = dialog.choice(["Run Now", "Edit First", "Just Save"]);
switch (runNow) {
case 0: // Run Now
runScriptWithLogging(scriptPath);
break;
case 1: // Edit First
editScript(scriptPath);
break;
// case 2: Just save - do nothing
}
} else {
dialog.error("Failed to create script");
}
}
function launchScriptEditor() {
const scriptFile = dialog.pickFile("/sd/BruceJS", ".js");
if (scriptFile) {
editScript(scriptFile);
}
}
function editScript(scriptPath) {
try {
const content = storage.read(scriptPath);
if (content === null) {
dialog.error("Failed to read script file");
return;
}
const newContent = keyboard.textArea("Edit script:", content);
if (newContent !== null) {
if (storage.write(scriptPath, newContent)) {
dialog.success("Script saved successfully");
const action = dialog.choice(["Run Script", "Save Only", "Validate Syntax"]);
switch (action) {
case 0: // Run Script
runScriptWithLogging(scriptPath);
break;
case 2: // Validate Syntax
validateScriptSyntax(newContent);
break;
}
} else {
dialog.error("Failed to save script");
}
}
} catch (error) {
dialog.error(`Failed to edit script: ${error.message}`);
}
}
function validateScriptSyntax(code) {
try {
// Basic syntax validation (would need actual parser in real implementation)
// This is a simplified check
const lines = code.split('\\n');
let braceCount = 0;
let parenCount = 0;
let issues = [];
for (var i = 0; i < lines.length; i++) {
var line = lines[i].trim();
if (line.length === 0 || line.indexOf('//') === 0) continue;
// Count braces and parentheses
for (var j = 0; j < line.length; j++) {
var char = line[j];
if (char === '{') braceCount++;
if (char === '}') braceCount--;
if (char === '(') parenCount++;
if (char === ')') parenCount--;
}
// Check for common issues
if (line.includes('console.log') && !line.includes('(')) {
issues.push(`Line ${i + 1}: Missing parentheses in console.log`);
}
}
if (braceCount !== 0) {
issues.push(`Mismatched braces: ${braceCount > 0 ? 'extra opening' : 'extra closing'}`);
}
if (parenCount !== 0) {
issues.push(`Mismatched parentheses: ${parenCount > 0 ? 'extra opening' : 'extra closing'}`);
}
if (issues.length === 0) {
dialog.success("No syntax issues found");
} else {
dialog.error(`Syntax issues found:\\n${issues.join('\\n')}`);
}
} catch (error) {
dialog.error(`Validation error: ${error.message}`);
}
}
Runtime Environment Management#
function showRuntimeInfo() {
const info = gatherRuntimeInfo();
dialog.viewText(info, "Runtime Information");
}
function gatherRuntimeInfo() {
let info = "JavaScript Runtime Information:\\n\\n";
// Memory information (simulated)
info += "Memory Usage:\\n";
info += " Used: ~150KB\\n";
info += " Available: ~350KB\\n";
info += " Total: ~500KB\\n\\n";
// Execution context
info += "Execution Context:\\n";
info += " Engine: MicroQuickJS\\n";
info += " Strict Mode: Yes\\n";
info += " Script Directory: /sd/BruceJS/\\n\\n";
// Available modules
info += "Available Modules:\\n";
const modules = [
"audio", "badusb", "device", "dialog", "display",
"globals", "gpio", "i2c", "ir", "keyboard",
"math", "mic", "notification", "rfid", "runtime",
"serial", "storage", "subghz", "wifi"
];
for (var i = 0; i < modules.length; i++) {
info += " ✓ " + modules[i] + "\n";
}
info += "\\nEnvironment Variables:\\n";
info += " BRUCE_VERSION: " + device.getVersion() + "\n";
info += " HARDWARE: " + device.getModel() + "\n";
info += " STORAGE: SD Card\n";
return info;
}
function runtimeDiagnostics() {
const tests = [
{ name: "Module Loading", test: testModuleLoading },
{ name: "Memory Access", test: testMemoryAccess },
{ name: "File System", test: testFileSystem },
{ name: "Script Execution", test: testScriptExecution },
{ name: "Error Handling", test: testErrorHandling }
];
let results = "Runtime Diagnostics:\\n\\n";
for (var i = 0; i < tests.length; i++) {
var test = tests[i];
try {
var passed = test.test();
results += (passed ? '✓' : '✗') + " " + test.name + ": " + (passed ? 'PASS' : 'FAIL') + "\n";
} catch (error) {
results += "✗ " + test.name + ": ERROR - " + error.message + "\n";
}
}
return results;
}
function testModuleLoading() {
// Test that core modules are available
return typeof console !== 'undefined' &&
typeof display !== 'undefined' &&
typeof dialog !== 'undefined';
}
function testMemoryAccess() {
// Test basic memory operations
try {
const arr = new Array(1000);
for (let i = 0; i < arr.length; i++) {
arr[i] = i;
}
return arr[999] === 999;
} catch (e) {
return false;
}
}
function testFileSystem() {
// Test file system access
try {
const testData = "runtime_test_" + Date.now();
const testFile = "/sd/runtime_test.tmp";
if (!storage.write(testFile, testData)) {
return false;
}
const readData = storage.read(testFile);
storage.remove(testFile); // Cleanup
return readData === testData;
} catch (e) {
return false;
}
}
function testScriptExecution() {
// Test basic script execution capabilities
try {
const testScript = `/sd/BruceJS/runtime_test_${Date.now()}.js`;
const scriptContent = 'console.log("Runtime test successful");';
if (!storage.write(testScript, scriptContent)) {
return false;
}
const result = runtime.exec(testScript);
storage.remove(testScript); // Cleanup
return result;
} catch (e) {
return false;
}
}
function testErrorHandling() {
// Test error handling mechanisms
try {
try {
throw new Error("Test error");
} catch (e) {
return e.message === "Test error";
}
} catch (e) {
return false;
}
}
Process Control System#
class ScriptManager {
constructor() {
this.runningScripts = [];
this.scriptHistory = [];
this.maxHistory = 50;
}
executeScript(scriptPath, args = []) {
const execution = {
id: Date.now(),
path: scriptPath,
args: args,
startTime: Date.now(),
status: "running",
exitCode: null,
error: null
};
this.runningScripts.push(execution);
try {
console.log(`[ScriptManager] Starting: ${scriptPath}`);
// Set up execution environment
this.setupScriptEnvironment(execution);
// Execute the script
const success = runtime.exec(scriptPath);
execution.status = success ? "completed" : "failed";
execution.exitCode = success ? 0 : 1;
execution.endTime = Date.now();
} catch (error) {
execution.status = "error";
execution.error = error.message;
execution.exitCode = -1;
execution.endTime = Date.now();
console.log(`[ScriptManager] Error in ${scriptPath}: ${error.message}`);
}
// Move to history
this.runningScripts = this.runningScripts.filter(s => s.id !== execution.id);
this.addToHistory(execution);
return execution;
}
setupScriptEnvironment(execution) {
// Set global variables for script
global.SCRIPT_PATH = execution.path;
global.SCRIPT_ID = execution.id;
global.SCRIPT_ARGS = execution.args;
global.SCRIPT_START_TIME = execution.startTime;
}
addToHistory(execution) {
this.scriptHistory.unshift(execution);
if (this.scriptHistory.length > this.maxHistory) {
this.scriptHistory = this.scriptHistory.slice(0, this.maxHistory);
}
this.saveHistory();
}
saveHistory() {
try {
const historyData = JSON.stringify(this.scriptHistory);
storage.write("scripts/execution_history.json", historyData);
} catch (e) {
console.log("Failed to save script history");
}
}
loadHistory() {
try {
const historyData = storage.read("scripts/execution_history.json");
if (historyData) {
this.scriptHistory = JSON.parse(historyData);
}
} catch (e) {
this.scriptHistory = [];
}
}
getHistory() {
return this.scriptHistory.slice(); // Return copy
}
clearHistory() {
this.scriptHistory = [];
this.saveHistory();
}
getRunningScripts() {
return this.runningScripts.slice(); // Return copy
}
killScript(scriptId) {
const script = this.runningScripts.find(s => s.id === scriptId);
if (script) {
script.status = "killed";
script.exitCode = -9;
script.endTime = Date.now();
// Remove from running list
this.runningScripts = this.runningScripts.filter(s => s.id !== scriptId);
// Add to history
this.addToHistory(script);
return true;
}
return false;
}
formatExecutionInfo(execution) {
const duration = execution.endTime ?
execution.endTime - execution.startTime :
Date.now() - execution.startTime;
let info = `Script: ${execution.path}\\n`;
info += `ID: ${execution.id}\\n`;
info += `Status: ${execution.status.toUpperCase()}\\n`;
info += `Duration: ${duration}ms\\n`;
info += `Started: ${new Date(execution.startTime).toLocaleString()}\\n`;
if (execution.endTime) {
info += `Ended: ${new Date(execution.endTime).toLocaleString()}\\n`;
}
if (execution.exitCode !== null) {
info += `Exit Code: ${execution.exitCode}\\n`;
}
if (execution.error) {
info += `Error: ${execution.error}\\n`;
}
if (execution.args && execution.args.length > 0) {
info += `Arguments: ${execution.args.join(', ')}\\n`;
}
return info;
}
}
// Global script manager instance
const scriptManager = new ScriptManager();
scriptManager.loadHistory();
function processManager() {
while (true) {
const choice = dialog.choice([
"Running Scripts",
"Execution History",
"Run Script",
"System Info",
"Clear History",
"Exit"
]);
switch (choice) {
case 0: // Running Scripts
showRunningScripts();
break;
case 1: // Execution History
showExecutionHistory();
break;
case 2: // Run Script
const scriptFile = dialog.pickFile("/sd/BruceJS", ".js");
if (scriptFile) {
const execution = scriptManager.executeScript(scriptFile);
const info = scriptManager.formatExecutionInfo(execution);
dialog.viewText(info, "Execution Result");
}
break;
case 3: // System Info
const info = gatherRuntimeInfo() + "\\n\\n" + runtimeDiagnostics();
dialog.viewText(info, "System Information");
break;
case 4: // Clear History
scriptManager.clearHistory();
dialog.info("Execution history cleared");
break;
case 5: // Exit
return;
}
}
}
function showRunningScripts() {
const running = scriptManager.getRunningScripts();
if (running.length === 0) {
dialog.info("No scripts currently running");
return;
}
var choices = [];
for (var i = 0; i < running.length; i++) {
var script = running[i];
var duration = Date.now() - script.startTime;
var pathParts = script.path.split('/');
var name = pathParts[pathParts.length - 1];
choices.push(name + " (" + duration + "ms)");
}
choices.push("< Back");
const choice = dialog.choice(choices);
if (choice >= 0 && choice < running.length) {
const script = running[choice];
const info = scriptManager.formatExecutionInfo(script);
dialog.viewText(info, "Script Details");
}
}
function showExecutionHistory() {
const history = scriptManager.getHistory();
if (history.length === 0) {
dialog.info("No execution history");
return;
}
var choices = [];
for (var i = 0; i < Math.min(history.length, 20); i++) {
var execution = history[i];
var pathParts = execution.path.split('/');
var name = pathParts[pathParts.length - 1];
var status = execution.status === "completed" ? "✓" :
execution.status === "failed" ? "✗" : "⚠";
var duration = execution.endTime ?
execution.endTime - execution.startTime : "?";
choices.push(status + " " + name + " (" + duration + "ms)");
}
choices.push("< Back");
const choice = dialog.choice(choices);
if (choice >= 0 && choice < history.length) {
const execution = history[choice];
const info = scriptManager.formatExecutionInfo(execution);
dialog.viewText(info, "Execution Details");
}
}
Error Handling and Debugging#
function createDebugScript(scriptPath) {
var debugWrapper = "\n// Debug wrapper for: " + scriptPath + "\nconsole.log('[Debug] Script starting: " + scriptPath + "');\nconsole.log('[Debug] Timestamp:', Date.now());\n\ntry {\n // Execute original script\n var result = runtime.exec('" + scriptPath + "');\n console.log('[Debug] Script result:', result);\n \n if (result) {\n console.log('[Debug] Script completed successfully');\n } else {\n console.log('[Debug] Script failed');\n }\n \n} catch (error) {\n console.log('[Debug] Script error:', error.message);\n console.log('[Debug] Stack trace:', error.stack || 'Not available');\n \n // Save error log\n var errorLog = {\n script: '" + scriptPath + "',\n error: error.message,\n timestamp: Date.now(),\n stack: error.stack || null\n };\n \n var logFile = '/sd/debug_errors.json';\n var errors = [];\n \n try {\n var existingData = storage.read(logFile);\n if (existingData) {\n errors = JSON.parse(existingData);\n }\n } catch (e) {\n // Ignore parse errors\n }\n \n errors.push(errorLog);\n storage.write(logFile, JSON.stringify(errors, null, 2));\n \n dialog.error('Script error logged to debug_errors.json');\n}\n\nconsole.log('[Debug] Debug session completed');\n";
const debugPath = "/sd/debug_wrapper.js";
storage.write(debugPath, debugWrapper);
return debugPath;
}