homework: thermometer mock (not tested)

This commit is contained in:
4 changed files with 62 additions and 10 deletions

View File

@@ -1,4 +1,4 @@
/// Пример подключения нескольких клиентов к розетке. Изменение состояния любым из клиентов отражается на всех. //! Пример подключения нескольких клиентов к розетке. Изменение состояния любым из клиентов отражается на всех.
use smart_house::PowerSocket; use smart_house::PowerSocket;
use std::time::Duration; use std::time::Duration;

View File

@@ -1,4 +1,5 @@
/// Сервер-имитатор умной розетки //! Сервер-имитатор умной розетки
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use std::time::Duration; use std::time::Duration;

View File

@@ -1,4 +1,4 @@
/// Старый пример работы умного дома на заглушках //! Старый пример работы умного дома на заглушках
use smart_house::{Device, House, PowerSocket, PrintStatus, Room, Thermometer, room}; use smart_house::{Device, House, PowerSocket, PrintStatus, Room, Thermometer, room};

View File

@@ -1,25 +1,76 @@
//! Имитатор термометра
use rand::prelude::*;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use rand::prelude::*; use std::time::Duration;
fn parse_args() -> Result<SocketAddr, Box<dyn std::error::Error>> { fn parse_args() -> Result<SocketAddr, Box<dyn std::error::Error>> {
let mut args = std::env::args(); let mut args = std::env::args();
args.next(); args.next();
Ok(args.next().ok_or(std::io::Error::other("no server address parameter specified"))?.parse()?) Ok(args
.next()
.ok_or(std::io::Error::other("no server address parameter must be specified"))?
.parse()?)
} }
fn generate_temperature() -> f32 { fn generate_temperature() -> f32 {
rand::rng().random_range(18.0..23.0) rand::rng().random_range(18.0..23.0)
} }
struct RealThermometer { struct RealThermometer {
temperature: f32 temperature: f32,
}
const REQUEST: u8 = 0b01010101;
async fn mutate_thermometer(thermometer: Arc<RwLock<RealThermometer>>) -> Result<(), std::io::Error> {
let mut interval = tokio::time::interval(Duration::from_secs(1));
loop {
interval.tick().await;
let new_temperature = generate_temperature();
thermometer.try_write().map_err(|_| std::io::Error::other("write lock failed"))?.temperature = new_temperature;
println!("new temperature: {:02.1}", new_temperature)
}
}
async fn handle_request(
request: u8,
thermometer: Arc<RwLock<RealThermometer>>,
socket: Arc<tokio::net::UdpSocket>,
addr: SocketAddr,
) -> Result<(), std::io::Error> {
if request == REQUEST {
let data = thermometer
.try_read()
.map_err(|_| std::io::Error::other("write lock failed"))?
.temperature
.to_le_bytes();
socket.send_to(&data, &addr).await?;
println!("temperature was sent to {}", addr);
} else {
println!("invalid request: {:?}", request);
}
Ok(())
} }
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let real_thermometer = Arc::new(RwLock::new(RealThermometer { temperature: generate_temperature()})); let real_thermometer = Arc::new(RwLock::new(RealThermometer {
temperature: generate_temperature(),
}));
let rt = tokio::runtime::Builder::new_current_thread().enable_all().build()?; let rt = tokio::runtime::Builder::new_current_thread().enable_all().build()?;
rt.block_on(async { rt.block_on(async move {
let thermometer = real_thermometer.clone();
tokio::spawn(async move { mutate_thermometer(thermometer).await });
}); let socket = Arc::new(tokio::net::UdpSocket::bind(parse_args()?).await?);
Ok(()) let mut buf = [0u8; 4];
loop {
let (_, addr) = socket.recv_from(&mut buf).await?;
let request = buf[0];
let thermometer = real_thermometer.clone();
let socket = socket.clone();
tokio::spawn(async move { handle_request(request, thermometer, socket, addr).await });
}
})
} }