diff --git a/smart-house/README.md b/smart-house/README.md index d234f54..dfa38fb 100644 --- a/smart-house/README.md +++ b/smart-house/README.md @@ -133,11 +133,11 @@ - [x] Позволяет управлять розеткой множеству клиентов одновременно. Для умного термометра: -- [ ] Функционал не изменяется: возвращает температуру. -- [ ] Получает значения температуры в виде UDP-пакетов в параллельном потоке. -- [ ] Параллельный поток запускается при создании объекта термометра и завершается при уничтожении этого объекта. -- [ ] Объект термометра возвращает последнее полученное значение температуры. -- [ ] Термометр может имитировать удалённое получение данных о температуре (для тестов). +- [x] Функционал не изменяется: возвращает температуру. +- [x] Получает значения температуры в виде UDP-пакетов в параллельном потоке. +- [x] Параллельный поток запускается при создании объекта термометра и завершается при уничтожении этого объекта. +- [x] Объект термометра возвращает последнее полученное значение температуры. +- [x] Термометр может имитировать удалённое получение данных о температуре (для тестов). Для имитатора умного термометра: - [ ] Реализован с использованием неблокирующего сетевого взаимодействия. diff --git a/smart-house/src/main.rs b/smart-house/src/main.rs index 898c7a9..9a492c2 100644 --- a/smart-house/src/main.rs +++ b/smart-house/src/main.rs @@ -1,8 +1,6 @@ -use smart_house::PowerSocket; -use std::time::Duration; +use smart_house::Thermometer; fn main() { - let power_socket = PowerSocket::connect("127.0.0.1:10001"); - println!("{}", power_socket.display()); - std::thread::sleep(Duration::from_secs(30)); + let thermometer = Thermometer::connect("127.0.0.1:10001").unwrap(); + println!("{}", thermometer.display()); } diff --git a/smart-house/src/thermometer.rs b/smart-house/src/thermometer.rs index 9925aaa..1f69d58 100644 --- a/smart-house/src/thermometer.rs +++ b/smart-house/src/thermometer.rs @@ -1,4 +1,8 @@ use std::fmt::{Debug, Display}; +use std::net::UdpSocket; +use std::sync::Arc; +use std::sync::atomic::{AtomicU32, Ordering}; +use std::time::Duration; #[derive(Debug)] pub struct Thermometer { @@ -12,6 +16,11 @@ impl Thermometer { } } + pub fn connect(addr: &str) -> std::io::Result { + let handle = ThermometerClient::new(addr)?; + Ok(Self { handle: Box::new(handle) }) + } + pub fn get_temperature(&self) -> f32 { self.handle.get_temperature() } @@ -42,20 +51,48 @@ impl ThermometerHandle for ThermometerHandleStub { } } +const TIMEOUT: Duration = Duration::from_secs(5); + #[derive(Debug)] struct ThermometerClient { - value: f32, + value: Arc, } impl ThermometerClient { - fn new() -> Self { - Self { value: f32::NAN } + fn read(socket: &UdpSocket) -> std::io::Result { + let mut buf = [0u8; 4]; + let result = socket.recv_from(&mut buf); + result.map(|_| Ok(u32::from_le_bytes(buf)))? + } + + fn new(addr: &str) -> std::io::Result { + let value = Arc::new(AtomicU32::new(u32::from_le_bytes(f32::NAN.to_le_bytes()))); + let socket = UdpSocket::bind(addr)?; + socket.set_read_timeout(Some(TIMEOUT))?; + + let data = ThermometerClient::read(&socket)?; + value.store(data, Ordering::Relaxed); + + let weak_value = Arc::downgrade(&value); + std::thread::spawn(move || { + while let Some(value) = weak_value.upgrade() { + let result = ThermometerClient::read(&socket); + match result { + Ok(data) => value.store(data, Ordering::Relaxed), + Err(e) => { + eprintln!("receiving data failed: {:?}", e); + break; + } + }; + } + }); + Ok(Self { value }) } } impl ThermometerHandle for ThermometerClient { fn get_temperature(&self) -> f32 { - self.value + f32::from_le_bytes(self.value.load(Ordering::Relaxed).to_le_bytes()) } }