adds initial user defined panels

This commit is contained in:
2025-12-23 16:41:21 -05:00
parent a110aa6376
commit ebbf864af9
33 changed files with 2188 additions and 370 deletions

View File

@@ -1,85 +1,15 @@
use crate::http::error::HttpServerResultError;
use crate::telemetry::management_service::TelemetryManagementService;
use actix_web::{get, web, Responder};
use chrono::{DateTime, TimeDelta, Utc};
use log::trace;
use serde::Deserialize;
use std::sync::Arc;
use std::time::Duration;
use tokio::time::timeout;
mod panels;
mod tlm;
#[get("/tlm/info/{name:[\\w\\d/_-]+}")]
async fn get_tlm_definition(
data: web::Data<Arc<TelemetryManagementService>>,
name: web::Path<String>,
) -> Result<impl Responder, HttpServerResultError> {
let string = name.to_string();
trace!("get_tlm_definition {}", string);
let Some(data) = data.get_by_name(&string) else {
return Err(HttpServerResultError::TlmNameNotFound { tlm: string });
};
Ok(web::Json(data.definition.clone()))
}
#[get("/tlm/info")]
async fn get_all_tlm_definitions(
data: web::Data<Arc<TelemetryManagementService>>,
) -> Result<impl Responder, HttpServerResultError> {
trace!("get_all_tlm_definitions");
Ok(web::Json(data.get_all_definitions()))
}
#[derive(Deserialize)]
struct HistoryQuery {
from: String,
to: String,
resolution: i64,
}
#[get("/tlm/history/{uuid:[0-9a-f]+}")]
async fn get_tlm_history(
data_arc: web::Data<Arc<TelemetryManagementService>>,
uuid: web::Path<String>,
info: web::Query<HistoryQuery>,
) -> Result<impl Responder, HttpServerResultError> {
let uuid = uuid.to_string();
trace!(
"get_tlm_history {} from {} to {} resolution {}",
uuid,
info.from,
info.to,
info.resolution
);
let Ok(from) = info.from.parse::<DateTime<Utc>>() else {
return Err(HttpServerResultError::InvalidDateTime {
date_time: info.from.clone(),
});
};
let Ok(to) = info.to.parse::<DateTime<Utc>>() else {
return Err(HttpServerResultError::InvalidDateTime {
date_time: info.to.clone(),
});
};
let maximum_resolution = TimeDelta::milliseconds(info.resolution);
let history_service = data_arc.history_service();
let data = data_arc.pin();
match data.get_by_uuid(&uuid) {
None => Err(HttpServerResultError::TlmUuidNotFound { uuid }),
Some(tlm) => timeout(
Duration::from_secs(10),
tlm.get(from, to, maximum_resolution, &history_service),
)
.await
.map(|result| Ok(web::Json(result)))
.unwrap_or_else(|_| Err(HttpServerResultError::Timeout)),
}
}
use actix_web::web;
pub fn setup_api(cfg: &mut web::ServiceConfig) {
cfg.service(get_all_tlm_definitions)
.service(get_tlm_definition)
.service(get_tlm_history);
cfg.service(tlm::get_all_tlm_definitions)
.service(tlm::get_tlm_definition)
.service(tlm::get_tlm_history)
.service(panels::new)
.service(panels::get_all)
.service(panels::get_one)
.service(panels::set)
.service(panels::delete);
}

View File

@@ -0,0 +1,67 @@
use crate::http::error::HttpServerResultError;
use crate::panels::panel::PanelUpdate;
use crate::panels::PanelService;
use actix_web::{delete, get, post, put, web, Responder};
use serde::Deserialize;
use std::sync::Arc;
#[derive(Deserialize)]
struct CreateParam {
name: String,
data: String,
}
#[derive(Deserialize)]
struct IdParam {
id: String,
}
#[post("/panel")]
pub(super) async fn new(
panels: web::Data<Arc<PanelService>>,
data: web::Json<CreateParam>,
) -> Result<impl Responder, HttpServerResultError> {
let uuid = panels.create(&data.name, &data.data).await?;
Ok(web::Json(uuid.value))
}
#[get("/panel")]
pub(super) async fn get_all(
panels: web::Data<Arc<PanelService>>,
) -> Result<impl Responder, HttpServerResultError> {
let result = panels.read_all().await?;
Ok(web::Json(result))
}
#[get("/panel/{id}")]
pub(super) async fn get_one(
panels: web::Data<Arc<PanelService>>,
path: web::Path<IdParam>,
) -> Result<impl Responder, HttpServerResultError> {
let result = panels.read(path.id.clone().into()).await?;
match result {
Some(result) => Ok(web::Json(result)),
None => Err(HttpServerResultError::PanelUuidNotFound {
uuid: path.id.clone(),
}),
}
}
#[put("/panel/{id}")]
pub(super) async fn set(
panels: web::Data<Arc<PanelService>>,
path: web::Path<IdParam>,
data: web::Json<PanelUpdate>,
) -> Result<impl Responder, HttpServerResultError> {
panels.update(path.id.clone().into(), data.0).await?;
Ok(web::Json(()))
}
#[delete("/panel/{id}")]
pub(super) async fn delete(
panels: web::Data<Arc<PanelService>>,
path: web::Path<IdParam>,
) -> Result<impl Responder, HttpServerResultError> {
panels.delete(path.id.clone().into()).await?;
Ok(web::Json(()))
}

View File

@@ -0,0 +1,79 @@
use crate::http::error::HttpServerResultError;
use crate::telemetry::management_service::TelemetryManagementService;
use actix_web::{get, web, Responder};
use chrono::{DateTime, TimeDelta, Utc};
use log::trace;
use serde::Deserialize;
use std::sync::Arc;
use std::time::Duration;
use tokio::time::timeout;
#[get("/tlm/info/{name:[\\w\\d/_-]+}")]
pub(super) async fn get_tlm_definition(
data: web::Data<Arc<TelemetryManagementService>>,
name: web::Path<String>,
) -> Result<impl Responder, HttpServerResultError> {
let string = name.to_string();
trace!("get_tlm_definition {}", string);
let Some(data) = data.get_by_name(&string) else {
return Err(HttpServerResultError::TlmNameNotFound { tlm: string });
};
Ok(web::Json(data.definition.clone()))
}
#[get("/tlm/info")]
pub(super) async fn get_all_tlm_definitions(
data: web::Data<Arc<TelemetryManagementService>>,
) -> Result<impl Responder, HttpServerResultError> {
trace!("get_all_tlm_definitions");
Ok(web::Json(data.get_all_definitions()))
}
#[derive(Deserialize)]
struct HistoryQuery {
from: String,
to: String,
resolution: i64,
}
#[get("/tlm/history/{uuid:[0-9a-f]+}")]
pub(super) async fn get_tlm_history(
data_arc: web::Data<Arc<TelemetryManagementService>>,
uuid: web::Path<String>,
info: web::Query<HistoryQuery>,
) -> Result<impl Responder, HttpServerResultError> {
let uuid = uuid.to_string();
trace!(
"get_tlm_history {} from {} to {} resolution {}",
uuid,
info.from,
info.to,
info.resolution
);
let Ok(from) = info.from.parse::<DateTime<Utc>>() else {
return Err(HttpServerResultError::InvalidDateTime {
date_time: info.from.clone(),
});
};
let Ok(to) = info.to.parse::<DateTime<Utc>>() else {
return Err(HttpServerResultError::InvalidDateTime {
date_time: info.to.clone(),
});
};
let maximum_resolution = TimeDelta::milliseconds(info.resolution);
let history_service = data_arc.history_service();
let data = data_arc.pin();
match data.get_by_uuid(&uuid) {
None => Err(HttpServerResultError::TlmUuidNotFound { uuid }),
Some(tlm) => timeout(
Duration::from_secs(10),
tlm.get(from, to, maximum_resolution, &history_service),
)
.await
.map(|result| Ok(web::Json(result)))
.unwrap_or_else(|_| Err(HttpServerResultError::Timeout)),
}
}

View File

@@ -2,11 +2,9 @@ use actix_web::error::ResponseError;
use actix_web::http::header::ContentType;
use actix_web::http::StatusCode;
use actix_web::HttpResponse;
use anyhow::Error;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum WebsocketResponseError {}
#[derive(Error, Debug)]
pub enum HttpServerResultError {
#[error("Telemetry Name Not Found: {tlm}")]
@@ -17,6 +15,10 @@ pub enum HttpServerResultError {
InvalidDateTime { date_time: String },
#[error("Timed out")]
Timeout,
#[error("Internal Error")]
InternalError(anyhow::Error),
#[error("Panel Uuid Not Found: {uuid}")]
PanelUuidNotFound { uuid: String },
}
impl ResponseError for HttpServerResultError {
@@ -25,7 +27,9 @@ impl ResponseError for HttpServerResultError {
HttpServerResultError::TlmNameNotFound { .. } => StatusCode::NOT_FOUND,
HttpServerResultError::TlmUuidNotFound { .. } => StatusCode::NOT_FOUND,
HttpServerResultError::InvalidDateTime { .. } => StatusCode::BAD_REQUEST,
HttpServerResultError::Timeout { .. } => StatusCode::GATEWAY_TIMEOUT,
HttpServerResultError::Timeout => StatusCode::GATEWAY_TIMEOUT,
HttpServerResultError::InternalError { .. } => StatusCode::INTERNAL_SERVER_ERROR,
HttpServerResultError::PanelUuidNotFound { .. } => StatusCode::NOT_FOUND,
}
}
fn error_response(&self) -> HttpResponse {
@@ -34,3 +38,9 @@ impl ResponseError for HttpServerResultError {
.body(self.to_string())
}
}
impl From<anyhow::Error> for HttpServerResultError {
fn from(value: Error) -> Self {
Self::InternalError(value)
}
}

View File

@@ -4,7 +4,9 @@ mod websocket;
use crate::http::api::setup_api;
use crate::http::websocket::setup_websocket;
use crate::panels::PanelService;
use crate::telemetry::management_service::TelemetryManagementService;
use actix_web::middleware::Logger;
use actix_web::{web, App, HttpServer};
use log::info;
use std::sync::Arc;
@@ -13,17 +15,21 @@ use tokio_util::sync::CancellationToken;
pub async fn setup(
cancellation_token: CancellationToken,
telemetry_definitions: Arc<TelemetryManagementService>,
panel_service: PanelService,
) -> anyhow::Result<()> {
let data = web::Data::new(telemetry_definitions);
let cancel_token = web::Data::new(cancellation_token);
let panel_service = web::Data::new(Arc::new(panel_service));
info!("Starting HTTP Server");
HttpServer::new(move || {
App::new()
.app_data(data.clone())
.app_data(cancel_token.clone())
.app_data(panel_service.clone())
.service(web::scope("/ws").configure(setup_websocket))
.service(web::scope("/api").configure(setup_api))
.wrap(Logger::default())
})
.bind("localhost:8080")?
.run()