web: implement synchronous IO with web and service workers
This commit is contained in:
parent
d485971a1c
commit
f4c24976c6
|
|
@ -0,0 +1,12 @@
|
|||
from js import workerReadSerial, workerWriteSerial
|
||||
|
||||
def capture(foo):
|
||||
print(f"(Python): capture() has been called with args: {foo}")
|
||||
workerWriteSerial("W00000001\r\n")
|
||||
workerWriteSerial("W00010001\r\n")
|
||||
workerWriteSerial("R0000\r\n")
|
||||
foo = workerReadSerial()
|
||||
print(f"(Python): capture() has completed and returned value: ", {foo})
|
||||
return foo
|
||||
|
||||
print("(Python) Load Complete!")
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
let port;
|
||||
|
||||
async function getPort() {
|
||||
if (port) return port;
|
||||
try {
|
||||
[port] = await navigator.serial.getPorts();
|
||||
await port.open({ baudRate: 115200 });
|
||||
console.log('Connected to serial device:', port);
|
||||
return port;
|
||||
} catch (error) {
|
||||
console.error('Error connecting to serial device:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function write(data) {
|
||||
const port = await getPort();
|
||||
const writer = port.writable.getWriter();
|
||||
await writer.write(new TextEncoder().encode(data));
|
||||
await writer.releaseLock();
|
||||
}
|
||||
|
||||
async function read() {
|
||||
const port = await getPort();
|
||||
const reader = port.readable.getReader();
|
||||
const {value, done} = await reader.read();
|
||||
reader.releaseLock();
|
||||
|
||||
if (!done) {
|
||||
return new TextDecoder().decode(value);
|
||||
}
|
||||
}
|
||||
|
||||
self.addEventListener('message', async function(event) {
|
||||
await write(event.data);
|
||||
});
|
||||
|
||||
async function readWrapper() {
|
||||
if (port) {
|
||||
self.postMessage(await read()); // this is a hack but i'm curious
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(readWrapper, 500);
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
addEventListener('install', () => {
|
||||
self.skipWaiting();
|
||||
});
|
||||
addEventListener('activate', () => {
|
||||
self.clients.claim();
|
||||
});
|
||||
|
||||
let resolver;
|
||||
console.log("(Service Worker): Loaded!");
|
||||
|
||||
addEventListener('message', event => {
|
||||
resolver(new Response(event.data,{status:200}));
|
||||
console.log("(Service Worker): fufilling fetch, sending: ", event.data);
|
||||
});
|
||||
|
||||
addEventListener('fetch', e => {
|
||||
const u = new URL(e.request.url);
|
||||
console.log("(Service Worker): Got fetch to: ", u);
|
||||
if (u.pathname === '/read_serial/' || u.pathname === '/write_serial/') {
|
||||
e.respondWith(new Promise(r => resolver = r));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
let data;
|
||||
let fileUploaded = false;
|
||||
|
||||
// Map the fake upload button to the real (hidden) upload button
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const uploadButton = document.getElementById("uploadButton");
|
||||
const fileInput = document.getElementById("fileInput");
|
||||
const filePathDisplay = document.getElementById("filePath");
|
||||
|
||||
uploadButton.addEventListener("click", function() {
|
||||
fileInput.click();
|
||||
});
|
||||
|
||||
fileInput.addEventListener("change", function() {
|
||||
const filePath = fileInput.value; // Get the file path from the file input
|
||||
filePathDisplay.textContent = "Loaded " + filePath; // Display the file path
|
||||
});
|
||||
});
|
||||
|
||||
// Update fileUploaded flag when a file gets uploaded
|
||||
document.getElementById('fileInput').addEventListener('change', function(event) {
|
||||
const file = event.target.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
data = e.target.result;
|
||||
fileUploaded = true;
|
||||
};
|
||||
reader.readAsText(file);
|
||||
});
|
||||
|
||||
// Continuously poll to see if the file has been uploaded
|
||||
function runAfterUpload() {
|
||||
if (!fileUploaded) {
|
||||
setTimeout(runAfterUpload, 100); // Check again after 100ms
|
||||
return;
|
||||
}
|
||||
|
||||
// Additional JavaScript code to run after file upload
|
||||
// WebTerminal(data);
|
||||
}
|
||||
|
||||
runAfterUpload();
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
let pyodide;
|
||||
|
||||
importScripts("https://cdn.jsdelivr.net/pyodide/v0.25.1/full/pyodide.js");
|
||||
|
||||
(async function() {
|
||||
pyodide = await loadPyodide({ indexURL : "https://cdn.jsdelivr.net/pyodide/v0.25.1/full/" });
|
||||
const response = await fetch('../assets/main.py');
|
||||
pyodide.runPython(await response.text());
|
||||
console.log("(Web Worker) Python loaded!");
|
||||
})()
|
||||
|
||||
addEventListener('message', function(e) {
|
||||
console.log("(Web Worker): Message received from main thread: ", e.data);
|
||||
const output = pyodide.globals.get("capture")(e.data);
|
||||
});
|
||||
|
||||
function blockingRequestToURL(url) {
|
||||
const request = new XMLHttpRequest();
|
||||
|
||||
// `false` makes the request synchronous
|
||||
request.open('GET', url, false);
|
||||
request.send(null);
|
||||
console.log('status', request.status);
|
||||
return request.responseText;
|
||||
}
|
||||
|
||||
function workerReadSerial() {
|
||||
console.log("(Web Worker): workerReadSerial called");
|
||||
postMessage({awaitingRead: true});
|
||||
data = blockingRequestToURL('/read_serial/');
|
||||
postMessage({awaitingRead: false});
|
||||
return data;
|
||||
}
|
||||
|
||||
function workerWriteSerial(data) {
|
||||
console.log("(Web Worker): workerWriteSerial called with: ", data);
|
||||
postMessage({awaitingWrite: true, writeData: data});
|
||||
blockingRequestToURL('/write_serial/');
|
||||
postMessage({awaitingWrite: false, writeData: undefined});
|
||||
}
|
||||
|
|
@ -1,53 +1,75 @@
|
|||
document.getElementById('connectButton').addEventListener('click', selectPort);
|
||||
document.getElementById('runButton').addEventListener('click', webTerminal);
|
||||
// serial
|
||||
let port;
|
||||
|
||||
async function selectPort(){
|
||||
await navigator.serial.requestPort();
|
||||
async function connect() {
|
||||
try {
|
||||
const serialPort = await navigator.serial.requestPort();
|
||||
await serialPort.open({ baudRate: 115200 });
|
||||
port = serialPort;
|
||||
console.log('Connected to serial device:', port);
|
||||
} catch (error) {
|
||||
console.error('Error connecting to serial device:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Start Web Worker for Serial API
|
||||
globalThis.receiveBuffer = [];
|
||||
|
||||
const serialWorker = new Worker("../javascripts/serial.js");
|
||||
function sendToSerialWorker(data){
|
||||
console.log("Main -> Worker: ", data);
|
||||
serialWorker.postMessage(data);
|
||||
async function write(data) {
|
||||
console.log("(Main) write() called with: ", data);
|
||||
const writer = port.writable.getWriter();
|
||||
await writer.write(new TextEncoder().encode(data));
|
||||
await writer.releaseLock();
|
||||
}
|
||||
|
||||
serialWorker.onmessage = (e) => {
|
||||
console.log("Worker -> Main: ", e.data);
|
||||
receiveBuffer.push(e.data);
|
||||
async function read() {
|
||||
console.log("(Main) read() called");
|
||||
const reader = port.readable.getReader();
|
||||
const {value, done} = await reader.read();
|
||||
reader.releaseLock();
|
||||
|
||||
if (!done) {
|
||||
return new TextDecoder().decode(value);
|
||||
}
|
||||
}
|
||||
|
||||
// workers
|
||||
let serviceWorker;
|
||||
|
||||
navigator.serviceWorker.register('../javascripts/service-worker.js').then(function(registration) {
|
||||
serviceWorker = registration.active;
|
||||
if (!serviceWorker) {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
// main loop
|
||||
let awaitingRead = false;
|
||||
let awaitingWrite = false;
|
||||
let writeData;
|
||||
|
||||
const worker = new Worker('../javascripts/web-worker.js');
|
||||
worker.onmessage = function(e) {
|
||||
console.log("(Main) received message from web worker: ", e.data);
|
||||
if ("awaitingRead" in e.data) {awaitingRead = e.data.awaitingRead};
|
||||
if ("awaitingWrite" in e.data) {awaitingWrite = e.data.awaitingWrite};
|
||||
if ("writeData" in e.data) {writeData = e.data.writeData};
|
||||
};
|
||||
|
||||
// Main function for the Web Terminal
|
||||
async function webTerminal(){
|
||||
let pyodide = await loadPyodide();
|
||||
|
||||
// Load Manta.yaml into pyodide's file system
|
||||
// pyodide.FS.writeFile("/manta.yaml", data, { encoding: "utf8" });
|
||||
|
||||
// Load micropip, setuptools, manta
|
||||
await pyodide.loadPackage("micropip");
|
||||
await pyodide.loadPackage("setuptools");
|
||||
const micropip = pyodide.pyimport("micropip");
|
||||
await micropip.install('../assets/manta-1.0.0-py3-none-any.whl');
|
||||
|
||||
pyodide.runPythonAsync(`
|
||||
import asyncio
|
||||
from js import sendToSerialWorker
|
||||
from js import receiveBuffer
|
||||
from manta import Manta
|
||||
|
||||
#m = Manta("/manta.yaml")
|
||||
|
||||
print("Sending read request")
|
||||
sendToSerialWorker("R0000\\r\\n")
|
||||
|
||||
for _ in range(10):
|
||||
print(len(receiveBuffer))
|
||||
print(receiveBuffer)
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
print("Python Complete")
|
||||
`);
|
||||
async function capture() {
|
||||
console.log("(Main): Sending message to Web Worker: ", "");
|
||||
worker.postMessage("");
|
||||
}
|
||||
|
||||
async function checkForAwaitingInput() {
|
||||
if (awaitingRead) {
|
||||
let out = await read();
|
||||
console.log("(Main): Sending message to Service Worker: ", out);
|
||||
serviceWorker.postMessage(out);
|
||||
}
|
||||
|
||||
if (awaitingWrite) {
|
||||
let out = await write(writeData);
|
||||
console.log("(Main): Sending message to Service Worker: ", out);
|
||||
serviceWorker.postMessage(out);
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(checkForAwaitingInput, 100);
|
||||
|
|
|
|||
|
|
@ -1,20 +1,7 @@
|
|||
## Open Serial Port
|
||||
<button class="md-button" id="connectButton">Connect</button>
|
||||
|
||||
## Load Manta Configuration File
|
||||
<button class="md-button" id="uploadButton">Upload File</button>
|
||||
<input type="file" id="fileInput" accept=".yaml,.yml" style="display: none">
|
||||
<div id="filePath"></div>
|
||||
|
||||
## Run Main Thread
|
||||
<button class="md-button" id="runButton">Run!</button>
|
||||
|
||||
|
||||
<!-- Load Javascript -->
|
||||
<script src="../javascripts/webTerminal.js"></script>
|
||||
<script src="../javascripts/serial.js"></script>
|
||||
<!-- <script src="../javascripts/upload.js"></script> -->
|
||||
<script src="https://cdn.jsdelivr.net/pyodide/v0.25.1/full/pyodide.js"></script>
|
||||
|
||||
<button onclick='connect()' class="md-button">Connect</button>
|
||||
<button onclick='console.log("File Upload Callback")' class="md-button">Upload Configuration File</button>
|
||||
<button onclick='capture()' class="md-button">Capture</button>
|
||||
<input type="file" id="fileInput" accept=".yaml,.yml,.json" style="display: none">
|
||||
<script src="../javascripts/webTerminal.js" type="text/javascript"></script>
|
||||
|
||||
<!-- <iframe src="https://app.surfer-project.org/?load_url=https://app.surfer-project.org/picorv32.vcd&startup_commands=show_quick_start;module_add%20testbench.top;toggle_menu" ,="" style="width: 100%; height: 500px"></iframe> -->
|
||||
Loading…
Reference in New Issue