mirror of
https://github.com/ParkerTenBroeck/hdl_sim.git
synced 2026-06-07 05:28:45 -04:00
adding local support
This commit is contained in:
parent
191101591e
commit
92102e3629
5 changed files with 515 additions and 183 deletions
|
|
@ -3,24 +3,30 @@ use axum::{
|
|||
extract::ws::{Message, WebSocket, WebSocketUpgrade},
|
||||
routing::get,
|
||||
};
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use futures_util::{
|
||||
stream::{SplitSink, SplitStream},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, net::SocketAddr};
|
||||
use std::{collections::HashMap, net::SocketAddr, path::PathBuf, time::Duration};
|
||||
use tokio::{
|
||||
io::{AsyncBufReadExt, BufReader},
|
||||
io::{BufReader, Lines},
|
||||
process::{Child, ChildStderr, ChildStdin, ChildStdout},
|
||||
};
|
||||
use tower_http::services::ServeDir;
|
||||
|
||||
use crate::build::TempDir;
|
||||
|
||||
pub mod build;
|
||||
pub mod run;
|
||||
|
||||
pub mod local;
|
||||
pub mod remote;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let app = Router::new()
|
||||
.route(
|
||||
"/ws",
|
||||
get(|ws: WebSocketUpgrade| async move { ws.on_upgrade(ws_handler) }),
|
||||
get(|ws: WebSocketUpgrade| async move { ws.on_upgrade(remote::ws_handler) }),
|
||||
)
|
||||
.fallback_service(ServeDir::new("ui"));
|
||||
|
||||
|
|
@ -32,132 +38,52 @@ async fn main() {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
struct ClientInput{
|
||||
/// bitfield of 32 switches
|
||||
switch: u32,
|
||||
/// bitfield of 32 buttons
|
||||
buttons: u32
|
||||
pub enum ClientMsg {
|
||||
Compile(Option<HashMap<String, String>>),
|
||||
Start,
|
||||
Stop,
|
||||
Input {
|
||||
/// bitfield of 32 switches
|
||||
switch: u32,
|
||||
/// bitfield of 32 buttons
|
||||
buttons: u32,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
enum ServerMsg<'a> {
|
||||
pub enum ServerMsg<'a> {
|
||||
Log { stream: &'a str, line: &'a str },
|
||||
Start,
|
||||
Stop,
|
||||
Led(u32),
|
||||
Seg0(u32),
|
||||
Seg1(u32),
|
||||
Seg2(u32),
|
||||
Seg3(u32)
|
||||
Seg3(u32),
|
||||
}
|
||||
|
||||
async fn ws_handler(socket: WebSocket) {
|
||||
let (mut sender, mut receiver) = socket.split();
|
||||
struct Process {
|
||||
process: Child,
|
||||
|
||||
let files = if let Some(Ok(Message::Text(msg))) = receiver.next().await
|
||||
&& let Ok(files) = serde_json::from_str::<'_, HashMap<String, String>>(&msg)
|
||||
{
|
||||
files
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
stderr: Lines<BufReader<ChildStderr>>,
|
||||
stdout: Lines<BufReader<ChildStdout>>,
|
||||
stdin: ChildStdin,
|
||||
}
|
||||
|
||||
struct Handler {
|
||||
sender: SplitSink<WebSocket, Message>,
|
||||
receiver: SplitStream<WebSocket>,
|
||||
|
||||
let artifact_dir = match build::build(files).await{
|
||||
Ok(dir) => dir,
|
||||
Err(err) => {
|
||||
_ = sender.send(Message::Text(format!("Failed to build: {err}").into())).await;
|
||||
return;
|
||||
},
|
||||
};
|
||||
build_dir: TempDir,
|
||||
src_dir: PathBuf,
|
||||
|
||||
let mut process = match run::run(&artifact_dir).await{
|
||||
Ok(process) => process,
|
||||
Err(err) => {
|
||||
_ = sender.send(Message::Text(format!("Failed to run: {err}").into())).await;
|
||||
return;
|
||||
},
|
||||
};
|
||||
let mut sout = BufReader::new(process.stdout).lines();
|
||||
let mut serr = BufReader::new(process.stderr).lines();
|
||||
program: Option<PathBuf>,
|
||||
process: Option<Process>,
|
||||
|
||||
let artifact_prefix = artifact_dir.to_str().unwrap_or("\0\0NOPE");
|
||||
|
||||
let result: Result<(), Box<dyn std::error::Error + Sync + Send>> = async {
|
||||
loop{
|
||||
tokio::select! {
|
||||
receive = receiver.next() => {
|
||||
match receive{
|
||||
Some(Ok(Message::Close(_))) => break,
|
||||
Some(Ok(Message::Text(msg))) => {
|
||||
let input = serde_json::from_str::<'_, ClientInput>(&msg)?;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
process.stdin.write_all(format!("btn={}\n", input.buttons).as_bytes()).await?;
|
||||
process.stdin.write_all(format!("sw={}\n", input.switch).as_bytes()).await?;
|
||||
},
|
||||
Some(Ok(_)) => {},
|
||||
Some(Err(err)) => Err(err)?,
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
out = sout.next_line() => {
|
||||
match out{
|
||||
Ok(Some(line)) => {
|
||||
|
||||
let msg = ServerMsg::Log {
|
||||
stream: "stdout",
|
||||
line: line.strip_prefix(artifact_prefix).unwrap_or(&line),
|
||||
};
|
||||
sender.send(Message::Text(serde_json::to_string(&msg)?.into())).await?;
|
||||
},
|
||||
Ok(None) => break,
|
||||
Err(err) => {
|
||||
Err(format!("Failed to read proccess sout: {err}"))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
err = serr.next_line() => {
|
||||
match err{
|
||||
Ok(Some(line)) => {
|
||||
let msg = if let Some(repr) = line.strip_prefix("led="){
|
||||
ServerMsg::Led(repr.parse().unwrap_or(0))
|
||||
}else if let Some(repr) = line.strip_prefix("seg0="){
|
||||
ServerMsg::Seg0(repr.parse().unwrap_or(0))
|
||||
}else if let Some(repr) = line.strip_prefix("seg1="){
|
||||
ServerMsg::Seg1(repr.parse().unwrap_or(0))
|
||||
}else if let Some(repr) = line.strip_prefix("seg2="){
|
||||
ServerMsg::Seg2(repr.parse().unwrap_or(0))
|
||||
}else if let Some(repr) = line.strip_prefix("seg3="){
|
||||
ServerMsg::Seg3(repr.parse().unwrap_or(0))
|
||||
}else{
|
||||
ServerMsg::Log {
|
||||
stream: "stderr",
|
||||
line: line.strip_prefix(artifact_prefix).unwrap_or(&line),
|
||||
}
|
||||
};
|
||||
sender.send(Message::Text(serde_json::to_string(&msg)?.into())).await?;
|
||||
},
|
||||
Ok(None) => break,
|
||||
Err(err) => {
|
||||
Err(format!("Failed to read proccess serr: {err}"))?
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = tokio::time::sleep(std::time::Duration::from_millis(30)) => {
|
||||
use tokio::io::AsyncWriteExt;
|
||||
process.stdin.write_all("\n".as_bytes()).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}.await;
|
||||
refresh_time: Duration,
|
||||
}
|
||||
|
||||
match result{
|
||||
Ok(_) => {},
|
||||
Err(err) => {
|
||||
_ = sender.send(Message::Text(format!("{err}").into())).await;
|
||||
},
|
||||
}
|
||||
}
|
||||
pub type HResult<T> = Result<T, Box<dyn std::error::Error + Sync + Send>>;
|
||||
Loading…
Add table
Add a link
Reference in a new issue