mirror of
https://github.com/ParkerTenBroeck/hdl_sim.git
synced 2026-06-07 05:28:45 -04:00
updated segment interface
This commit is contained in:
parent
4773ab5e9e
commit
5746846896
9 changed files with 183 additions and 195 deletions
|
|
@ -1,5 +1,8 @@
|
|||
use std::{
|
||||
collections::HashMap, ffi::OsStr, ops::Deref, path::{Path, PathBuf}
|
||||
collections::HashMap,
|
||||
ffi::OsStr,
|
||||
ops::Deref,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use tokio::process::{Child, Command};
|
||||
|
||||
|
|
@ -46,9 +49,7 @@ impl AsRef<Path> for TempDir {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn copy_and_build(
|
||||
files: HashMap<String, String>,
|
||||
) -> HResult<TempDir> {
|
||||
pub async fn copy_and_build(files: HashMap<String, String>) -> HResult<TempDir> {
|
||||
use std::hash::*;
|
||||
let mut hasher = std::hash::DefaultHasher::default();
|
||||
for (key, value) in &files {
|
||||
|
|
@ -73,19 +74,18 @@ pub async fn copy_and_build(
|
|||
Ok(work_dir)
|
||||
}
|
||||
|
||||
|
||||
pub async fn build(build: &Path, src: &Path) -> HResult<()>{
|
||||
pub async fn build(build: &Path, src: &Path) -> HResult<()> {
|
||||
std::fs::create_dir_all(build)?;
|
||||
let embedded_lib_path = build.join("libvhdl_conn.a");
|
||||
let embedded_tb_path = build.join("tb.vhdl");
|
||||
std::fs::write(&embedded_lib_path, EMBEDDED_VHDL_UI_LIB)?;
|
||||
std::fs::write(&embedded_tb_path, EMBEDDED_TB_VHDL)?;
|
||||
|
||||
let mut cmd = Command::new("ghdl");
|
||||
let mut cmd = Command::new("ghdl");
|
||||
cmd.kill_on_drop(true);
|
||||
cmd.args(["-i", "-g", "--std=08"]);
|
||||
|
||||
for file in src.read_dir().unwrap().flatten(){
|
||||
for file in src.read_dir().unwrap().flatten() {
|
||||
if Path::new(&file.file_name()).extension() == Some(OsStr::new("vhdl")) {
|
||||
cmd.arg(file.path().canonicalize()?);
|
||||
}
|
||||
|
|
@ -99,7 +99,6 @@ pub async fn build(build: &Path, src: &Path) -> HResult<()>{
|
|||
cmd.current_dir(build);
|
||||
ensure_ok(cmd.spawn()?).await?;
|
||||
|
||||
|
||||
let mut cmd = Command::new("ghdl");
|
||||
cmd.kill_on_drop(true);
|
||||
cmd.args(["-m", "--std=08"]);
|
||||
|
|
@ -113,6 +112,6 @@ pub async fn build(build: &Path, src: &Path) -> HResult<()>{
|
|||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped());
|
||||
ensure_ok(cmd.spawn()?).await?;
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,10 @@ async fn serve_styles() -> impl IntoResponse {
|
|||
|
||||
async fn serve_app_js() -> impl IntoResponse {
|
||||
(
|
||||
[(header::CONTENT_TYPE, "application/javascript; charset=utf-8")],
|
||||
[(
|
||||
header::CONTENT_TYPE,
|
||||
"application/javascript; charset=utf-8",
|
||||
)],
|
||||
UI_APP_JS,
|
||||
)
|
||||
}
|
||||
|
|
@ -91,7 +94,8 @@ fn parse_config_from_args() -> Result<Config, String> {
|
|||
}
|
||||
"--help" | "-h" => {
|
||||
return Err(
|
||||
"usage: relay [--ip <ip>] [--port <port>] [--update-ms <ms>] [--workspace]".into(),
|
||||
"usage: relay [--ip <ip>] [--port <port>] [--update-ms <ms>] [--workspace]"
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
|
|
@ -116,30 +120,24 @@ async fn main() {
|
|||
};
|
||||
|
||||
let update_interval = Duration::from_millis(cfg.update_ms);
|
||||
let mut app = Router::new()
|
||||
.route(
|
||||
"/",
|
||||
get(move || {
|
||||
async move { serve_index().await }
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/index.html",
|
||||
get(move || {
|
||||
async move { serve_index().await }
|
||||
}),
|
||||
)
|
||||
.route("/styles.css", get(serve_styles))
|
||||
.route("/app.js", get(serve_app_js))
|
||||
.route(
|
||||
"/ws/uploaded",
|
||||
get(move |ws: WebSocketUpgrade| {
|
||||
let update_interval = update_interval;
|
||||
async move {
|
||||
ws.on_upgrade(move |socket| uploaded::ws_handler(socket, update_interval))
|
||||
}
|
||||
}),
|
||||
);
|
||||
let mut app =
|
||||
Router::new()
|
||||
.route("/", get(move || async move { serve_index().await }))
|
||||
.route(
|
||||
"/index.html",
|
||||
get(move || async move { serve_index().await }),
|
||||
)
|
||||
.route("/styles.css", get(serve_styles))
|
||||
.route("/app.js", get(serve_app_js))
|
||||
.route(
|
||||
"/ws/uploaded",
|
||||
get(move |ws: WebSocketUpgrade| {
|
||||
let update_interval = update_interval;
|
||||
async move {
|
||||
ws.on_upgrade(move |socket| uploaded::ws_handler(socket, update_interval))
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
if cfg.workspace_ws {
|
||||
app = app.route(
|
||||
|
|
@ -183,10 +181,7 @@ pub enum ServerMsg<'a> {
|
|||
Start,
|
||||
Stop,
|
||||
Led(u32),
|
||||
Seg0(u32),
|
||||
Seg1(u32),
|
||||
Seg2(u32),
|
||||
Seg3(u32),
|
||||
Seg { value: u32, index: u32 },
|
||||
}
|
||||
|
||||
pub type HResult<T> = Result<T, Box<dyn std::error::Error + Sync + Send>>;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
use axum::{
|
||||
extract::ws::{Message, WebSocket},
|
||||
};
|
||||
use futures_util::{
|
||||
SinkExt, StreamExt,
|
||||
};
|
||||
use axum::extract::ws::{Message, WebSocket};
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use std::{collections::HashMap, time::Duration};
|
||||
use tokio::{
|
||||
io::{AsyncBufReadExt, BufReader}, time::Instant,
|
||||
io::{AsyncBufReadExt, BufReader},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use crate::{ClientMsg, HResult, ServerMsg, build, run};
|
||||
|
|
@ -16,27 +13,30 @@ pub async fn ws_handler(socket: WebSocket, refresh_time: Duration) {
|
|||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
let artifact_dir = match build::copy_and_build(files).await{
|
||||
let artifact_dir = match build::copy_and_build(files).await {
|
||||
Ok(dir) => dir,
|
||||
Err(err) => {
|
||||
_ = sender.send(Message::Text(format!("Failed to build: {err}").into())).await;
|
||||
_ = sender
|
||||
.send(Message::Text(format!("Failed to build: {err}").into()))
|
||||
.await;
|
||||
return;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
let mut process = match run::run(&artifact_dir).await{
|
||||
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;
|
||||
_ = 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();
|
||||
|
|
@ -44,7 +44,7 @@ pub async fn ws_handler(socket: WebSocket, refresh_time: Duration) {
|
|||
let artifact_prefix = artifact_dir.to_str().unwrap_or("\0\0NOPE");
|
||||
|
||||
let mut print_deadline = Instant::now();
|
||||
|
||||
|
||||
let result: HResult<()> = async {
|
||||
loop{
|
||||
tokio::select! {
|
||||
|
|
@ -89,14 +89,12 @@ pub async fn ws_handler(socket: WebSocket, refresh_time: Duration) {
|
|||
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 if let Some(repr) = line.strip_prefix("seg=")
|
||||
&& let Some((val, idx)) = repr.split_once(";") {
|
||||
ServerMsg::Seg{
|
||||
value: val.parse().unwrap_or(0),
|
||||
index: idx.parse().unwrap_or(0)
|
||||
}
|
||||
}else{
|
||||
ServerMsg::Log {
|
||||
stream: "stderr",
|
||||
|
|
@ -121,10 +119,10 @@ pub async fn ws_handler(socket: WebSocket, refresh_time: Duration) {
|
|||
Ok(())
|
||||
}.await;
|
||||
|
||||
match result{
|
||||
Ok(_) => {},
|
||||
match result {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
_ = sender.send(Message::Text(format!("{err}").into())).await;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use axum::{
|
||||
Error, extract::ws::{Message, WebSocket}
|
||||
Error,
|
||||
extract::ws::{Message, WebSocket},
|
||||
};
|
||||
use futures_util::{
|
||||
SinkExt, StreamExt,
|
||||
|
|
@ -8,12 +9,12 @@ use futures_util::{
|
|||
use std::{path::PathBuf, time::Duration};
|
||||
use tokio::{
|
||||
io::{AsyncBufReadExt, BufReader, Lines},
|
||||
process::{Child, ChildStderr, ChildStdin, ChildStdout}, time::Instant,
|
||||
process::{Child, ChildStderr, ChildStdin, ChildStdout},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use crate::{ClientMsg, ServerMsg, build, run};
|
||||
|
||||
|
||||
struct Process {
|
||||
process: Child,
|
||||
|
||||
|
|
@ -22,7 +23,6 @@ struct Process {
|
|||
stdin: ChildStdin,
|
||||
}
|
||||
|
||||
|
||||
struct Handler {
|
||||
sender: SplitSink<WebSocket, Message>,
|
||||
receiver: SplitStream<WebSocket>,
|
||||
|
|
@ -36,7 +36,6 @@ struct Handler {
|
|||
}
|
||||
|
||||
impl Handler {
|
||||
|
||||
fn workspace(socket: WebSocket, build: PathBuf, src: PathBuf, refresh_time: Duration) -> Self {
|
||||
let (sender, receiver) = socket.split();
|
||||
Self {
|
||||
|
|
@ -55,7 +54,12 @@ impl Handler {
|
|||
stream: "stdout",
|
||||
line: msg.as_ref(),
|
||||
};
|
||||
_ = self.sender.send(Message::Text(serde_json::to_string(&msg).unwrap_or_default().into())).await;
|
||||
_ = self
|
||||
.sender
|
||||
.send(Message::Text(
|
||||
serde_json::to_string(&msg).unwrap_or_default().into(),
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn eprint(&mut self, msg: impl AsRef<str>) {
|
||||
|
|
@ -64,36 +68,51 @@ impl Handler {
|
|||
stream: "stderr",
|
||||
line: msg.as_ref(),
|
||||
};
|
||||
_ = self.sender.send(Message::Text(serde_json::to_string(&msg).unwrap_or_default().into())).await;
|
||||
_ = self
|
||||
.sender
|
||||
.send(Message::Text(
|
||||
serde_json::to_string(&msg).unwrap_or_default().into(),
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn stop_process(&mut self) {
|
||||
self.process = None;
|
||||
_ = self.sender.send(Message::Text(serde_json::to_string(&ServerMsg::Stop).unwrap_or_default().into())).await;
|
||||
_ = self
|
||||
.sender
|
||||
.send(Message::Text(
|
||||
serde_json::to_string(&ServerMsg::Stop)
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn handle_websocket_msg(&mut self, msg: ClientMsg) {
|
||||
match msg{
|
||||
match msg {
|
||||
ClientMsg::Start => self.run_program().await,
|
||||
ClientMsg::Stop => self.stop_process().await,
|
||||
ClientMsg::Input { switch, buttons } => {
|
||||
if let Some(process) = &mut self.process{
|
||||
if let Some(process) = &mut self.process {
|
||||
use tokio::io::AsyncWriteExt;
|
||||
_ = process.stdin.write_all(format!("btn={}\n", buttons).as_bytes()).await;
|
||||
_ = process.stdin.write_all(format!("sw={}\n", switch).as_bytes()).await;
|
||||
_ = process
|
||||
.stdin
|
||||
.write_all(format!("btn={}\n", buttons).as_bytes())
|
||||
.await;
|
||||
_ = process
|
||||
.stdin
|
||||
.write_all(format!("sw={}\n", switch).as_bytes())
|
||||
.await;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_websocket_receive(
|
||||
&mut self,
|
||||
msg: Option<Result<Message, Error>>,
|
||||
) -> bool {
|
||||
async fn handle_websocket_receive(&mut self, msg: Option<Result<Message, Error>>) -> bool {
|
||||
match msg {
|
||||
Some(Ok(Message::Close(_))) => true,
|
||||
Some(Ok(Message::Text(msg))) => {
|
||||
let msg = match serde_json::from_str(msg.as_str()){
|
||||
let msg = match serde_json::from_str(msg.as_str()) {
|
||||
Ok(msg) => msg,
|
||||
Err(err) => {
|
||||
self.eprint(format!("Client message error {err}")).await;
|
||||
|
|
@ -107,7 +126,7 @@ impl Handler {
|
|||
Some(Err(err)) => {
|
||||
self.eprint(format!("Client websocket error {err}")).await;
|
||||
true
|
||||
},
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
|
|
@ -115,7 +134,7 @@ impl Handler {
|
|||
async fn run(&mut self) {
|
||||
loop {
|
||||
if let Some(process) = &mut self.process {
|
||||
if let Ok(Some(_)) = process.process.try_wait(){
|
||||
if let Ok(Some(_)) = process.process.try_wait() {
|
||||
self.stop_process().await;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -143,14 +162,12 @@ impl Handler {
|
|||
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 if let Some(repr) = line.strip_prefix("seg=")
|
||||
&& let Some((val, idx)) = repr.split_once(";") {
|
||||
ServerMsg::Seg{
|
||||
value: val.parse().unwrap_or(0),
|
||||
index: idx.parse().unwrap_or(0)
|
||||
}
|
||||
}else{
|
||||
self.eprint(line).await;
|
||||
continue;
|
||||
|
|
@ -170,19 +187,18 @@ impl Handler {
|
|||
_ = process.stdin.write_all("\n".as_bytes()).await;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
let res = self.receiver.next().await;
|
||||
if self.handle_websocket_receive(res).await{
|
||||
if self.handle_websocket_receive(res).await {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_program(&mut self) {
|
||||
|
||||
async fn run_program(&mut self) {
|
||||
match build::build(&self.build_dir, &self.src_dir).await {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
_ = self.eprint(format!("Failed to build: {err}")).await;
|
||||
return;
|
||||
|
|
@ -200,13 +216,25 @@ impl Handler {
|
|||
let stderr = BufReader::new(process.stderr).lines();
|
||||
let stdin = process.stdin;
|
||||
|
||||
_ = self.sender.send(Message::Text(serde_json::to_string(&ServerMsg::Start).unwrap_or_default().into())).await;
|
||||
self.process = Some(
|
||||
Process { process: process.child, stderr, stdout, stdin }
|
||||
)
|
||||
_ = self
|
||||
.sender
|
||||
.send(Message::Text(
|
||||
serde_json::to_string(&ServerMsg::Start)
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
))
|
||||
.await;
|
||||
self.process = Some(Process {
|
||||
process: process.child,
|
||||
stderr,
|
||||
stdout,
|
||||
stdin,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn ws_handler(socket: WebSocket, refresh_time: Duration) {
|
||||
Handler::workspace(socket, "./target".into(), "./src".into(), refresh_time).run().await;
|
||||
Handler::workspace(socket, "./target".into(), "./src".into(), refresh_time)
|
||||
.run()
|
||||
.await;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue