homework: thermometer client

This commit is contained in:
3 changed files with 49 additions and 14 deletions

View File

@@ -133,11 +133,11 @@
- [x] Позволяет управлять розеткой множеству клиентов одновременно. - [x] Позволяет управлять розеткой множеству клиентов одновременно.
Для умного термометра: Для умного термометра:
- [ ] Функционал не изменяется: возвращает температуру. - [x] Функционал не изменяется: возвращает температуру.
- [ ] Получает значения температуры в виде UDP-пакетов в параллельном потоке. - [x] Получает значения температуры в виде UDP-пакетов в параллельном потоке.
- [ ] Параллельный поток запускается при создании объекта термометра и завершается при уничтожении этого объекта. - [x] Параллельный поток запускается при создании объекта термометра и завершается при уничтожении этого объекта.
- [ ] Объект термометра возвращает последнее полученное значение температуры. - [x] Объект термометра возвращает последнее полученное значение температуры.
- [ ] Термометр может имитировать удалённое получение данных о температуре (для тестов). - [x] Термометр может имитировать удалённое получение данных о температуре (для тестов).
Для имитатора умного термометра: Для имитатора умного термометра:
- [ ] Реализован с использованием неблокирующего сетевого взаимодействия. - [ ] Реализован с использованием неблокирующего сетевого взаимодействия.

View File

@@ -1,8 +1,6 @@
use smart_house::PowerSocket; use smart_house::Thermometer;
use std::time::Duration;
fn main() { fn main() {
let power_socket = PowerSocket::connect("127.0.0.1:10001"); let thermometer = Thermometer::connect("127.0.0.1:10001").unwrap();
println!("{}", power_socket.display()); println!("{}", thermometer.display());
std::thread::sleep(Duration::from_secs(30));
} }

View File

@@ -1,4 +1,8 @@
use std::fmt::{Debug, Display}; 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)] #[derive(Debug)]
pub struct Thermometer { pub struct Thermometer {
@@ -12,6 +16,11 @@ impl Thermometer {
} }
} }
pub fn connect(addr: &str) -> std::io::Result<Self> {
let handle = ThermometerClient::new(addr)?;
Ok(Self { handle: Box::new(handle) })
}
pub fn get_temperature(&self) -> f32 { pub fn get_temperature(&self) -> f32 {
self.handle.get_temperature() self.handle.get_temperature()
} }
@@ -42,20 +51,48 @@ impl ThermometerHandle for ThermometerHandleStub {
} }
} }
const TIMEOUT: Duration = Duration::from_secs(5);
#[derive(Debug)] #[derive(Debug)]
struct ThermometerClient { struct ThermometerClient {
value: f32, value: Arc<AtomicU32>,
} }
impl ThermometerClient { impl ThermometerClient {
fn new() -> Self { fn read(socket: &UdpSocket) -> std::io::Result<u32> {
Self { value: f32::NAN } 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<Self> {
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 { impl ThermometerHandle for ThermometerClient {
fn get_temperature(&self) -> f32 { fn get_temperature(&self) -> f32 {
self.value f32::from_le_bytes(self.value.load(Ordering::Relaxed).to_le_bytes())
} }
} }