homework: thermometer client

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

View File

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

View File

@@ -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());
}

View File

@@ -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<Self> {
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<AtomicU32>,
}
impl ThermometerClient {
fn new() -> Self {
Self { value: f32::NAN }
fn read(socket: &UdpSocket) -> std::io::Result<u32> {
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 {
fn get_temperature(&self) -> f32 {
self.value
f32::from_le_bytes(self.value.load(Ordering::Relaxed).to_le_bytes())
}
}