Skip to content

Commit cf42af0

Browse files
committed
web: add /upload-hex-model handler for saving ev model to file
1 parent a2665dc commit cf42af0

File tree

2 files changed

+68
-3
lines changed

2 files changed

+68
-3
lines changed

src/ev.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use serde::Deserialize;
1515
use warp::Filter;
1616

1717
pub static FORD_EV_MODEL: &[u8] = include_bytes!("protos/ford_ev_model.bin");
18+
pub const EV_MODEL_FILE: &str = "/etc/aa-proxy-rs/ev_model.bin";
1819

1920
// module name for logging engine
2021
const NAME: &str = "<i><bright-black> ev: </>";

src/web.rs

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
use crate::config::AppConfig;
22
use crate::config::SharedConfig;
3+
use crate::ev::EV_MODEL_FILE;
34
use axum::{
45
body::Body,
5-
extract::{Query, State},
6-
http::{header, Response, StatusCode},
6+
extract::{Query, RawBody, State},
7+
http::{header, HeaderMap, Response, StatusCode},
78
response::{Html, IntoResponse},
8-
routing::get,
9+
routing::{get, post},
910
Json, Router,
1011
};
1112
use chrono::Local;
13+
use hyper::body::to_bytes;
1214
use std::collections::HashMap;
1315
use std::path::PathBuf;
1416
use std::sync::Arc;
1517
use tokio::fs;
18+
use tokio::io::AsyncWriteExt;
1619

1720
const TEMPLATE: &str = include_str!("../static/index.html");
1821
const PICO_CSS: &str = include_str!("../static/pico.min.css");
@@ -29,6 +32,7 @@ pub fn app(state: Arc<AppState>) -> Router {
2932
.route("/config", get(get_config).post(set_config))
3033
.route("/download", get(download_handler))
3134
.route("/restart", get(restart_handler))
35+
.route("/upload-hex-model", post(upload_hex_model_handler))
3236
.with_state(state)
3337
}
3438

@@ -83,6 +87,66 @@ async fn download_handler(
8387
}
8488
}
8589

90+
async fn upload_hex_model_handler(
91+
State(state): State<Arc<AppState>>,
92+
headers: HeaderMap,
93+
RawBody(body): RawBody,
94+
) -> impl IntoResponse {
95+
// read body as bytes
96+
let body_bytes = match to_bytes(body).await {
97+
Ok(bytes) => bytes,
98+
Err(err) => {
99+
return (
100+
StatusCode::BAD_REQUEST,
101+
format!("Unable to read body: {}", err),
102+
)
103+
}
104+
};
105+
106+
// convert to UTF-8 string
107+
let hex_str = match std::str::from_utf8(&body_bytes) {
108+
Ok(s) => s.trim(), // remove whitespaces
109+
Err(err) => {
110+
return (
111+
StatusCode::BAD_REQUEST,
112+
format!("Unable to parse body to UTF-8: {}", err),
113+
)
114+
}
115+
};
116+
117+
// decode into Vec<u8>
118+
let binary_data = match hex::decode(hex_str) {
119+
Ok(data) => data,
120+
Err(err) => {
121+
return (
122+
StatusCode::BAD_REQUEST,
123+
format!("Invalid hex data: {}", err),
124+
)
125+
}
126+
};
127+
128+
// save to model file
129+
let path: PathBuf = PathBuf::from(EV_MODEL_FILE);
130+
match fs::File::create(&path).await {
131+
Ok(mut file) => {
132+
if let Err(err) = file.write_all(&binary_data).await {
133+
return (
134+
StatusCode::INTERNAL_SERVER_ERROR,
135+
format!("Error saving model file: {}", err),
136+
);
137+
}
138+
(
139+
StatusCode::OK,
140+
format!("File saved correctly as {:?}", path),
141+
)
142+
}
143+
Err(err) => (
144+
StatusCode::INTERNAL_SERVER_ERROR,
145+
format!("File create error: {}", err),
146+
),
147+
}
148+
}
149+
86150
async fn get_config(State(state): State<Arc<AppState>>) -> impl IntoResponse {
87151
let cfg = state.config.read().await.clone();
88152
Json(cfg)

0 commit comments

Comments
 (0)