homework: thermometer client
This commit is contained in:
@@ -133,11 +133,11 @@
|
||||
- [x] Позволяет управлять розеткой множеству клиентов одновременно.
|
||||
|
||||
Для умного термометра:
|
||||
- [ ] Функционал не изменяется: возвращает температуру.
|
||||
- [ ] Получает значения температуры в виде UDP-пакетов в параллельном потоке.
|
||||
- [ ] Параллельный поток запускается при создании объекта термометра и завершается при уничтожении этого объекта.
|
||||
- [ ] Объект термометра возвращает последнее полученное значение температуры.
|
||||
- [ ] Термометр может имитировать удалённое получение данных о температуре (для тестов).
|
||||
- [x] Функционал не изменяется: возвращает температуру.
|
||||
- [x] Получает значения температуры в виде UDP-пакетов в параллельном потоке.
|
||||
- [x] Параллельный поток запускается при создании объекта термометра и завершается при уничтожении этого объекта.
|
||||
- [x] Объект термометра возвращает последнее полученное значение температуры.
|
||||
- [x] Термометр может имитировать удалённое получение данных о температуре (для тестов).
|
||||
|
||||
Для имитатора умного термометра:
|
||||
- [ ] Реализован с использованием неблокирующего сетевого взаимодействия.
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user