homework: thermometer mock (correct)
This commit is contained in:
@@ -1,76 +1,54 @@
|
||||
//! Имитатор термометра
|
||||
|
||||
use rand::prelude::*;
|
||||
use std::io::Read;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
fn parse_args() -> Result<SocketAddr, Box<dyn std::error::Error>> {
|
||||
let mut args = std::env::args();
|
||||
args.next();
|
||||
Ok(args
|
||||
.next()
|
||||
.ok_or(std::io::Error::other("no server address parameter must be specified"))?
|
||||
.parse()?)
|
||||
struct Params {
|
||||
addr: SocketAddr,
|
||||
interval: Duration,
|
||||
}
|
||||
|
||||
const CONFIG_FILE: &str = "thermometer_mock.cfg";
|
||||
|
||||
fn read_parameters_from_file() -> Result<Params, std::io::Error> {
|
||||
let mut file = std::fs::File::open(CONFIG_FILE)?;
|
||||
let mut content = String::new();
|
||||
file.read_to_string(&mut content)?;
|
||||
let lines = content.split("\n").collect::<Vec<&str>>();
|
||||
let addr = lines
|
||||
.first()
|
||||
.map(|v| SocketAddr::from_str(v))
|
||||
.ok_or(std::io::Error::other("no address found in config file"))?
|
||||
.map_err(std::io::Error::other)?;
|
||||
let interval = lines
|
||||
.get(1)
|
||||
.map(|v| v.parse::<u64>())
|
||||
.ok_or(std::io::Error::other("no interval found in config file"))?
|
||||
.map(Duration::from_millis)
|
||||
.map_err(std::io::Error::other)?;
|
||||
Ok(Params { addr, interval })
|
||||
}
|
||||
|
||||
fn generate_temperature() -> f32 {
|
||||
rand::rng().random_range(18.0..23.0)
|
||||
}
|
||||
|
||||
struct RealThermometer {
|
||||
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>> {
|
||||
let real_thermometer = Arc::new(RwLock::new(RealThermometer {
|
||||
temperature: generate_temperature(),
|
||||
}));
|
||||
let params = read_parameters_from_file()?;
|
||||
let rt = tokio::runtime::Builder::new_current_thread().enable_all().build()?;
|
||||
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?);
|
||||
let mut buf = [0u8; 4];
|
||||
let socket = Arc::new(tokio::net::UdpSocket::bind(params.addr).await?);
|
||||
let mut interval = tokio::time::interval(params.interval);
|
||||
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 });
|
||||
interval.tick().await;
|
||||
let new_temperature = generate_temperature();
|
||||
println!("new temperature: {:02.1}", new_temperature);
|
||||
let data = new_temperature.to_le_bytes();
|
||||
socket.send_to(&data, ¶ms.addr).await?;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
2
smart-house/thermometer_mock.cfg
Normal file
2
smart-house/thermometer_mock.cfg
Normal file
@@ -0,0 +1,2 @@
|
||||
127.0.0.1:10002
|
||||
500
|
||||
Reference in New Issue
Block a user