From 162b5ffefca4b712fcef5639c31c1a7b59a8389c Mon Sep 17 00:00:00 2001 From: Alexander Baranov Date: Wed, 6 May 2026 14:59:55 +0300 Subject: [PATCH] =?UTF-8?q?smart-house-web:=20=D0=B2=20=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit smart-house-web: в работе --- .gitignore | 1 + smart-house-web/Cargo.toml | 19 +++++-- smart-house-web/backend/Cargo.toml | 12 ++++ smart-house-web/backend/src/house.rs | 79 +++++++++++++++++++++++++++ smart-house-web/backend/src/lib.rs | 37 +++++++++++++ smart-house-web/backend/src/main.rs | 6 ++ smart-house-web/backend/src/server.rs | 73 +++++++++++++++++++++++++ smart-house-web/src/main.rs | 3 - 8 files changed, 222 insertions(+), 8 deletions(-) create mode 100644 smart-house-web/backend/Cargo.toml create mode 100644 smart-house-web/backend/src/house.rs create mode 100644 smart-house-web/backend/src/lib.rs create mode 100644 smart-house-web/backend/src/main.rs create mode 100644 smart-house-web/backend/src/server.rs delete mode 100644 smart-house-web/src/main.rs diff --git a/.gitignore b/.gitignore index 6cb0886..21946af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /.idea/ /**/.DS_Store +/tmp/ diff --git a/smart-house-web/Cargo.toml b/smart-house-web/Cargo.toml index 72360fd..dfc365c 100644 --- a/smart-house-web/Cargo.toml +++ b/smart-house-web/Cargo.toml @@ -1,6 +1,15 @@ -[package] -name = "smart-house-web" -version = "0.1.0" -edition = "2024" +[workspace] +resolver = "3" +members = ["backend"] -[dependencies] +[profile.release] +opt-level = "z" +strip = "symbols" +lto = "fat" +panic = "abort" +codegen-units = 1 +overflow-checks = false +debug-assertions = false +incremental = false + +[workspace.dependencies] diff --git a/smart-house-web/backend/Cargo.toml b/smart-house-web/backend/Cargo.toml new file mode 100644 index 0000000..d64875f --- /dev/null +++ b/smart-house-web/backend/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "backend" +version = "0.1.0" +edition = "2024" + +[dependencies] +tracing = "0.1" +tracing-subscriber = "0.3" +tokio = { version = "1.52", features = ["rt", "rt-multi-thread", "signal", "time"] } +axum = "0.8" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/smart-house-web/backend/src/house.rs b/smart-house-web/backend/src/house.rs new file mode 100644 index 0000000..b1007ac --- /dev/null +++ b/smart-house-web/backend/src/house.rs @@ -0,0 +1,79 @@ +use serde::{Deserialize, Serialize}; +use std::fmt::Display; + +#[derive(Debug, Serialize, Deserialize)] +pub struct PowerSocket { + power_rate: f32, + on: bool, +} + +impl PowerSocket { + pub fn new(power_rate: f32, on: bool) -> Self { + Self { power_rate, on } + } + + pub fn is_on(&self) -> bool { + self.on + } + + pub fn set_on(&mut self, on: bool) { + self.on = on + } + + pub fn get_power(&self) -> f32 { + if self.is_on() { self.power_rate } else { 0.0 } + } + + pub fn report(&self) -> impl Display { + let state = if self.is_on() { "ON" } else { "OFF" }; + let power = self.get_power(); + format!("PowerSocket[ {} : {:02.1} ]", state, power) + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Thermometer { + temperature: f32, +} + +impl Thermometer { + pub fn new(temperature: f32) -> Self { + Self { temperature } + } + + pub fn get_temperature(&self) -> f32 { + self.temperature + } + + pub fn report(&self) -> impl Display { + let temperature = self.get_temperature(); + format!("Thermometer[ {:02.1} ]", temperature) + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum Device { + PowerSocket(PowerSocket), + Thermometer(Thermometer), +} + +impl Device { + pub fn report(&self) -> impl Display { + match self { + Device::PowerSocket(v) => format!("{}", v.report()), + Device::Thermometer(v) => format!("{}", v.report()), + } + } +} + +impl From for Device { + fn from(value: PowerSocket) -> Self { + Device::PowerSocket(value) + } +} + +impl From for Device { + fn from(value: Thermometer) -> Self { + Device::Thermometer(value) + } +} diff --git a/smart-house-web/backend/src/lib.rs b/smart-house-web/backend/src/lib.rs new file mode 100644 index 0000000..5686d22 --- /dev/null +++ b/smart-house-web/backend/src/lib.rs @@ -0,0 +1,37 @@ +const CODE_LOGGER_INITIALIZATION_ERROR: i32 = 1; +const CODE_TOKIO_RUNTIME_CREATION_ERROR: i32 = 2; +const CODE_LISTENER_BINDING_ERROR: i32 = 3; +const CODE_STARTIG_SERVER_ERROR: i32 = 4; +const CODE_CTRL_C_SIGNAL_INSTALL_ERROR: i32 = 5; + +pub fn init_logger() { + use std::process::exit; + use tracing::{Level, trace}; + use tracing_subscriber::{ + Layer, filter::Targets, fmt::layer, layer::SubscriberExt, registry, util::SubscriberInitExt, + }; + + let layer = layer() + .compact() + .with_thread_names(true) + .with_file(false) + .with_line_number(false) + .with_filter( + Targets::new() + .with_target("axum::serve", Level::INFO) + .with_default(Level::TRACE), + ) + .boxed(); + if let Err(e) = registry().with(vec![layer]).try_init() { + eprintln!("Logger initialization failed: {:?}", e); + exit(CODE_LOGGER_INITIALIZATION_ERROR); + } else { + trace!("Logger succesfully initialized"); + } +} + +mod server; +pub use server::run_server; + +mod house; +pub use house::{Device, PowerSocket, Thermometer}; diff --git a/smart-house-web/backend/src/main.rs b/smart-house-web/backend/src/main.rs new file mode 100644 index 0000000..da4e57b --- /dev/null +++ b/smart-house-web/backend/src/main.rs @@ -0,0 +1,6 @@ +use backend::{init_logger, run_server}; + +fn main() { + init_logger(); + run_server(); +} diff --git a/smart-house-web/backend/src/server.rs b/smart-house-web/backend/src/server.rs new file mode 100644 index 0000000..257414f --- /dev/null +++ b/smart-house-web/backend/src/server.rs @@ -0,0 +1,73 @@ +use std::{ + process::exit, + sync::atomic::{AtomicUsize, Ordering}, +}; +use tracing::{error, info}; + +pub fn run_server() { + let runtime = match tokio::runtime::Builder::new_multi_thread() + .name("tokio") + .thread_name_fn(|| { + static LAST_ID: AtomicUsize = AtomicUsize::new(0); + let id = LAST_ID.fetch_add(1, Ordering::SeqCst); + format!("tkwr-{id}") + }) + .worker_threads(2) + .thread_stack_size(256 * 1024) + .enable_all() + .build() + { + Ok(runtime) => runtime, + Err(e) => { + error!("Failed to create Tokio runtime: {:?}", e); + exit(crate::CODE_TOKIO_RUNTIME_CREATION_ERROR); + } + }; + runtime.block_on(server_main()); +} + +async fn server_main() { + let app = axum::Router::new().fallback(fallback); + let addr = "127.0.0.1:8080"; + let listener = match tokio::net::TcpListener::bind(addr).await { + Ok(listener) => listener, + Err(e) => { + error!("Failed to bind listener to {}: {:?}", addr, e); + exit(crate::CODE_LISTENER_BINDING_ERROR); + } + }; + info!("Starting server at {}...", addr); + if let Err(e) = axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await + { + error!("Failed to start server: {:?}", e); + exit(crate::CODE_STARTIG_SERVER_ERROR); + }; + info!("Shutdown server"); +} + +async fn fallback() -> axum::response::Response { + use axum::response::IntoResponse; + (axum::http::StatusCode::NOT_FOUND, "404 NOT FOUND").into_response() +} + +async fn shutdown_signal() { + // let timeout = async { + // tokio::time::sleep(std::time::Duration::from_secs(10)).await; + // info!("10 seconds timeout expired"); + // }; + let ctrl_c = async { + tokio::signal::ctrl_c().await.map_err(|e| { + error!("Can't install Ctrl+C signal handler: {:?}", e); + exit(crate::CODE_CTRL_C_SIGNAL_INSTALL_ERROR); + }); + info!("Ctrl+C pressed"); + }; + let pending = std::future::pending::<()>(); + tokio::select! { + // _ = timeout => {}, + _ = ctrl_c => {}, + _ = pending => {}, + } +} diff --git a/smart-house-web/src/main.rs b/smart-house-web/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/smart-house-web/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -}