Skip to content

Commit

Permalink
feat(readoutd): stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
hawkw committed Jun 23, 2024
1 parent e020a95 commit 2f71b39
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 22 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 31 additions & 3 deletions eclss-readoutd/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl WindowArgs {
let mut interval = tokio::time::interval(self.refresh.into());
loop {
let metrics = client.fetch().await?;

tracing::trace!(?metrics);
display.clear(BinaryColor::Off)?;
render_embedded_graphics(&mut display, style, &metrics)?;
window.update(&display);
Expand Down Expand Up @@ -108,9 +108,10 @@ where
const HUMIDITY: &str = "HUMIDITY:";
const TVOC: &str = "TVOC:";
const CO2: &str = "CO2:";
const STATION: &str = "STATION:";

const WIDTH: usize = {
let labels = [TEMP, HUMIDITY, TVOC, CO2];
let labels = [STATION, TEMP, HUMIDITY, TVOC, CO2];
let mut max = 0;
let mut i = 0;
while i < labels.len() {
Expand All @@ -123,6 +124,33 @@ where
max
};

let text_style = TextStyleBuilder::new()
.alignment(Alignment::Center)
.baseline(embedded_graphics::text::Baseline::Top)
.line_height(LineHeight::Percent(110))
.build();
let center = target.bounding_box().center();

let pt = Text::with_text_style(
"ECLSS READOUT\n",
Point::new(center.x, OFFSET),
char_style,
text_style,
)
.draw(target)
.map_err(|e| anyhow::anyhow!("error drawing title: {e:?}"))?;

let mut pt = Point::new(OFFSET, pt.y);
if let Some(location) = metrics.location.as_ref() {
pt = Text::with_text_style(
&format!("{STATION:<WIDTH$}: {location}\n"),
pt,
char_style,
text_style,
)
.draw(target)
.map_err(|e| anyhow::anyhow!("error drawing location: {e:?}"))?;
}
let text_style = TextStyleBuilder::new()
.alignment(Alignment::Left)
.baseline(embedded_graphics::text::Baseline::Top)
Expand All @@ -135,7 +163,7 @@ where
})
.unwrap_or_else(|| format!("{TEMP:<WIDTH$} ??? °C / ??? °F\n"));

let pt = Text::with_text_style(&temp, Point::new(OFFSET, OFFSET), char_style, text_style)
let pt = Text::with_text_style(&temp, pt, char_style, text_style)
.draw(target)
.map_err(|e| anyhow::anyhow!("error drawing temperature: {e:?}"))?;

Expand Down
34 changes: 26 additions & 8 deletions eclss-readoutd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use anyhow::Context;
use clap::Parser;
use eclss_app::TraceArgs;
use embedded_graphics::prelude::*;
use std::sync::Arc;
use std::time::Duration;

mod display;
#[cfg(feature = "terminal")]
Expand All @@ -10,7 +12,15 @@ mod terminal;
#[derive(Debug, Parser)]
struct Args {
/// The hostname of the `eclssd` instance to display data from.
host: reqwest::Url,
host: Arc<str>,

/// The target port on the eclssd instance.
#[clap(long, short, default_value_t = 4200)]
port: u16,

/// Refresh interval
#[clap(long, short, default_value = "2s", global = true)]
refresh: humantime::Duration,

#[clap(subcommand)]
display: DisplayCommand,
Expand All @@ -31,26 +41,27 @@ enum DisplayCommand {
}

#[derive(Debug, Parser)]
struct TerminalArgs {
/// Refresh interval
#[clap(long, short, default_value = "2s")]
refresh: humantime::Duration,
}
struct TerminalArgs {}

impl Args {
fn client(&self) -> anyhow::Result<Client> {
let client = reqwest::Client::new();
let metrics_url = self.host.join("/metrics.json")?;
let metrics_url =
reqwest::Url::parse(&format!("http://{}:{}/metrics.json", self.host, self.port))?;
Ok(Client {
client,
hostname: self.host.clone(),
metrics_url,
refresh: self.refresh.into(),
})
}
}

struct Client {
client: reqwest::Client,
pub(crate) hostname: Arc<str>,
pub(crate) metrics_url: reqwest::Url,
pub(crate) refresh: Duration,
}

impl Client {
Expand All @@ -65,6 +76,10 @@ impl Client {
tracing::debug!("received response: {:?}", rsp.status());
rsp.json().await.context("reading request body failed")
}

fn refresh_interval(&self) -> tokio::time::Interval {
tokio::time::interval(self.refresh)
}
}

#[cfg(not(feature = "terminal"))]
Expand All @@ -81,7 +96,10 @@ async fn main() -> anyhow::Result<()> {
let client = args.client()?;
match args.display {
DisplayCommand::Terminal(cmd) => cmd.run(client).await,
DisplayCommand::Window(cmd) => cmd.run(client).await,
DisplayCommand::Window(cmd) => {
tracing::info!("running in windowed mode: {cmd:?}");
cmd.run(client).await
}
DisplayCommand::Ssd1680(cmd) => cmd.run(client).await,
}
}
Expand Down
10 changes: 8 additions & 2 deletions eclss-readoutd/src/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl TerminalArgs {
terminal.clear()?;

let mut input = Box::pin(EventStream::new());
let mut interval = tokio::time::interval(self.refresh.into());
let mut interval = client.refresh_interval();
let fetch = client.fetch().await;
let mut app = App {
args: self,
Expand Down Expand Up @@ -90,7 +90,13 @@ struct App {

impl Widget for &App {
fn render(self, area: Rect, buf: &mut Buffer) {
let title = Title::from(" ECLSS READOUT ".bold());
let title = match self.fetch {
Ok(Metrics {
location: Some(ref location),
..
}) => Title::from(format!(" ECLSS READOUT - {location} ").bold()),
_ => Title::from(" ECLSS READOUT ".bold()),
};
let instructions = Title::from(Line::from(vec![" Quit ".into(), "<q/Q> ".blue().bold()]));
let block = Block::default()
.title(title.alignment(Alignment::Center))
Expand Down
14 changes: 9 additions & 5 deletions eclssd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use embedded_hal_async::{delay::DelayNs, i2c::I2c};
use linux_embedded_hal::I2cdev;
use std::future::Future;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;

#[cfg(feature = "mdns")]
Expand Down Expand Up @@ -36,7 +37,7 @@ struct Args {
trace: TraceArgs,

#[clap(long = "location", env = "ECLSS_LOCATION")]
location: Option<String>,
location: Option<Arc<str>>,

/// enable mDNS advertisement
#[clap(
Expand Down Expand Up @@ -125,10 +126,13 @@ async fn main() -> anyhow::Result<()> {

let listener = tokio::net::TcpListener::bind(args.listen_addr).await?;
tracing::info!(listen_addr = ?args.listen_addr, "listening...");
let server = tokio::spawn(async move {
eclss_axum::axum::serve(listener, eclss_axum::app(eclss))
.await
.unwrap();
let server = tokio::spawn({
let location = args.location.clone();
async move {
eclss_axum::axum::serve(listener, eclss_axum::app(eclss, location))
.await
.unwrap();
}
});

if args.mdns {
Expand Down
1 change: 1 addition & 0 deletions lib/eclss-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct Metrics {
pub nox_iaq_index: heapless::Vec<Measurement, MAX_SENSORS>,
pub pressure_hpa: heapless::Vec<Measurement, MAX_SENSORS>,
pub sensor_errors: heapless::Vec<Measurement, MAX_SENSORS>,
pub location: Option<heapless::String<64>>,
}

#[derive(Clone, PartialEq, Serialize, Deserialize)]
Expand Down
1 change: 1 addition & 0 deletions lib/eclss-axum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ edition = "2021"
[dependencies]
axum = { workspace = true }
eclss = { workspace = true, features = ["serde"] }
serde = { workspace = true, features = ["derive", "rc"] }
26 changes: 22 additions & 4 deletions lib/eclss-axum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ use axum::{
Router,
};
use eclss::{sensor::Registry, Eclss, SensorMetrics};
use std::sync::Arc;

#[derive(Clone)]
struct AppState<const SENSORS: usize> {
metrics: &'static SensorMetrics,
sensors: &'static Registry<SENSORS>,
location: Option<Arc<str>>,
}

pub fn app<I, const SENSORS: usize>(eclss: &'static Eclss<I, { SENSORS }>) -> Router {
pub fn app<I, const SENSORS: usize>(
eclss: &'static Eclss<I, { SENSORS }>,
location: Option<Arc<str>>,
) -> Router {
Router::new()
.route("/metrics", get(get_metrics))
.route("/metrics.json", get(get_metrics_json))
Expand All @@ -23,6 +28,7 @@ pub fn app<I, const SENSORS: usize>(eclss: &'static Eclss<I, { SENSORS }>) -> Ro
.with_state(AppState {
metrics: eclss.metrics(),
sensors: eclss.sensors(),
location,
})
.fallback(not_found)
}
Expand All @@ -35,10 +41,22 @@ async fn get_metrics<const SENSORS: usize>(
resp
}

#[derive(serde::Serialize)]
struct MetricsResponse {
#[serde(flatten)]
metrics: &'static SensorMetrics,
location: Option<Arc<str>>,
}

async fn get_metrics_json<const SENSORS: usize>(
State(AppState { metrics, .. }): State<AppState<{ SENSORS }>>,
) -> Json<&'static SensorMetrics> {
Json(metrics)
State(AppState {
metrics, location, ..
}): State<AppState<{ SENSORS }>>,
) -> Json<MetricsResponse> {
Json(MetricsResponse {
metrics,
location: location.clone(),
})
}

async fn get_sensors<const SENSORS: usize>(
Expand Down

0 comments on commit 2f71b39

Please sign in to comment.