Skip to content

Commit 771dc5e

Browse files
committed
Add more details to the /setup page
1 parent 4ab2922 commit 771dc5e

File tree

5 files changed

+173
-57
lines changed

5 files changed

+173
-57
lines changed

Cargo.lock

Lines changed: 92 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ num_enum = "0.5.9"
2424
once_cell = { version = "1.17.1", optional = true }
2525
rand = { version = "0.8.5", optional = true }
2626
reqwest = { version = "0.11.14", optional = true, default-features = false, features = ["json"] }
27+
sailfish = { version = "0.6.1", optional = true }
2728
serde = { version = "1.0.145", features = ["derive", "rc"] }
2829
serde-ndim = { version = "1.1.0", optional = true, features = ["ndarray"] }
2930
serde_json = { version = "1.0.86" }
@@ -76,7 +77,7 @@ switch = []
7677
telescope = []
7778

7879
client = ["dep:reqwest", "dep:bytes", "dep:mime", "dep:rand", "dep:once_cell"]
79-
server = ["dep:axum", "tokio/macros"]
80+
server = ["dep:axum", "dep:sailfish", "tokio/macros"]
8081

8182
test = ["client", "server", "camera", "tokio/macros", "tokio/rt-multi-thread"]
8283

sailfish.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
template_dirs = ["src/server"]

src/server/mod.rs

Lines changed: 28 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ use axum::response::IntoResponse;
2828
use axum::routing::MethodFilter;
2929
use axum::{BoxError, Router};
3030
use net_literals::addr;
31+
use sailfish::TemplateOnce;
3132
use std::collections::BTreeMap;
32-
use std::fmt::Write;
3333
use std::future::Future;
3434
use std::net::SocketAddr;
3535
use std::pin::Pin;
@@ -243,74 +243,51 @@ impl Server {
243243
})
244244
})
245245
})
246-
.route(
247-
"/management/v1/description",
246+
.route("/management/v1/description", {
247+
let server_info = Arc::clone(&server_info);
248+
248249
axum::routing::get(move |server_handler: ServerHandler| {
249250
server_handler.exec(|_params| async move {
250251
ValueResponse::from(Arc::clone(&server_info))
251252
})
252-
}),
253-
)
253+
})
254+
})
254255
.route("/setup", {
255256
let this = Arc::clone(&devices);
257+
let server_info = Arc::clone(&server_info);
256258

257259
axum::routing::get(|| async move {
258-
let mut grouped_devices = BTreeMap::new();
260+
#[derive(TemplateOnce)]
261+
#[template(path = "setup_template.html")]
262+
struct TemplateContext {
263+
server_info: Arc<ServerInfo>,
264+
grouped_devices: BTreeMap<DeviceType, Vec<(usize, String)>>,
265+
}
266+
267+
let mut ctx = TemplateContext {
268+
server_info: Arc::clone(&server_info),
269+
grouped_devices: BTreeMap::new(),
270+
};
271+
259272
for (device, number) in this.iter_all() {
260273
let device = device.to_configured_device(number);
261274

262-
grouped_devices
275+
ctx.grouped_devices
263276
.entry(device.ty)
264277
.or_insert_with(Vec::new)
265278
.push((number, device.name));
266279
}
267280

268-
let mut devices_html = String::new();
269-
270-
for (device_type, devices) in grouped_devices {
271-
writeln!(devices_html, "<figure>").unwrap();
272-
writeln!(devices_html, "<figcaption>{}</figcaption>", match device_type {
273-
#[cfg(feature = "camera")]
274-
DeviceType::Camera => "Cameras",
275-
#[cfg(feature = "dome")]
276-
DeviceType::Dome => "Domes",
277-
#[cfg(feature = "filterwheel")]
278-
DeviceType::FilterWheel => "Filter wheels",
279-
#[cfg(feature = "focuser")]
280-
DeviceType::Focuser => "Focusers",
281-
#[cfg(feature = "observingconditions")]
282-
DeviceType::ObservingConditions => "Weather stations",
283-
#[cfg(feature = "rotator")]
284-
DeviceType::Rotator => "Rotators",
285-
#[cfg(feature = "safetymonitor")]
286-
DeviceType::SafetyMonitor => "Safety monitors",
287-
#[cfg(feature = "switch")]
288-
DeviceType::Switch => "Switches",
289-
#[cfg(feature = "telescope")]
290-
DeviceType::Telescope => "Telescopes",
291-
#[cfg(feature = "covercalibrator")]
292-
DeviceType::CoverCalibrator => "Cover calibrators",
293-
}).unwrap();
294-
writeln!(devices_html, "<ul>").unwrap();
295-
for (device_index, device_name) in devices {
296-
writeln!(
297-
devices_html,
298-
r#"<li><a href="/api/v1/{group_path}/{device_index}/setup">{device_name}</a></li>"#,
299-
group_path = DevicePath(device_type),
300-
).unwrap();
281+
match ctx.render_once() {
282+
Ok(html) => Ok(axum::response::Html(html)),
283+
Err(err) => {
284+
tracing::error!(%err, "Failed to render setup page");
285+
Err((
286+
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
287+
err.to_string(),
288+
))
301289
}
302-
writeln!(devices_html, "</ul>").unwrap();
303-
writeln!(devices_html, "</figure>").unwrap();
304290
}
305-
306-
axum::response::Html(include_str!("setup_template.html").replace(
307-
"<!-- devices_html -->",
308-
if devices_html.is_empty() {
309-
"<p>No devices are registered on this server.</p>"
310-
} else {
311-
&devices_html
312-
},
313-
))
314291
})
315292
})
316293
.route(

src/server/setup_template.html

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<html lang="en">
44
<head>
55
<meta charset="UTF-8" />
6-
<title>Alpaca devices</title>
6+
<title><%= server_info.server_name %></title>
77
<style>
88
body {
99
font-family: Arial, sans-serif;
@@ -25,7 +25,7 @@
2525
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 -2px 2px rgba(0, 0, 0, 0.3);
2626
}
2727
figcaption,
28-
a {
28+
li>a {
2929
margin: 0;
3030
padding: 0.625em;
3131
}
@@ -47,20 +47,65 @@
4747
li {
4848
border-bottom: 1px solid #ccc;
4949
}
50-
a {
50+
li>a {
5151
color: #333;
5252
text-decoration: none;
5353
display: block;
5454
transition: background-color 0.3s ease;
5555
}
56-
a:hover {
56+
li>a:hover {
5757
background-color: #f5f5f5;
5858
}
5959
</style>
6060
</head>
6161
<body>
6262
<h1>Alpaca devices</h1>
6363

64-
<!-- devices_html -->
64+
<% if grouped_devices.is_empty() { %>
65+
<p>No devices are registered on this server.</p>
66+
<% } %>
67+
68+
<% for (group_ty, group) in grouped_devices { %>
69+
<figure>
70+
<figcaption>
71+
<%= match group_ty {
72+
#[cfg(feature = "camera")]
73+
DeviceType::Camera => "Cameras",
74+
#[cfg(feature = "dome")]
75+
DeviceType::Dome => "Domes",
76+
#[cfg(feature = "filterwheel")]
77+
DeviceType::FilterWheel => "Filter wheels",
78+
#[cfg(feature = "focuser")]
79+
DeviceType::Focuser => "Focusers",
80+
#[cfg(feature = "observingconditions")]
81+
DeviceType::ObservingConditions => "Weather stations",
82+
#[cfg(feature = "rotator")]
83+
DeviceType::Rotator => "Rotators",
84+
#[cfg(feature = "safetymonitor")]
85+
DeviceType::SafetyMonitor => "Safety monitors",
86+
#[cfg(feature = "switch")]
87+
DeviceType::Switch => "Switches",
88+
#[cfg(feature = "telescope")]
89+
DeviceType::Telescope => "Telescopes",
90+
#[cfg(feature = "covercalibrator")]
91+
DeviceType::CoverCalibrator => "Cover calibrators",
92+
} %>
93+
</figcaption>
94+
<ul>
95+
<% for (number, name) in group { %>
96+
<li><a href="/api/v1/<%= DevicePath(group_ty) | disp %>/<%= number %>/setup"><%= name %></a></li>
97+
<% } %>
98+
</ul>
99+
</figure>
100+
<% } %>
101+
102+
<footer>
103+
<p>
104+
This is an <a href="https://ascom-standards.org/AlpacaDeveloper/Index.htm">ASCOM Alpaca</a> server
105+
<%= server_info.server_name %> v<%= server_info.manufacturer_version %>
106+
by <%= server_info.manufacturer %>.
107+
</p>
108+
<p>Built with <a href="https://github.com/RReverser/ascom-alpaca-rs">ascom-alpaca-rs</a>.</p>
109+
</footer>
65110
</body>
66111
</html>

0 commit comments

Comments
 (0)