homework: upgrade power socket
This commit is contained in:
@@ -122,9 +122,9 @@
|
||||
### Описание/Пошаговая инструкция выполнения домашнего задания:
|
||||
|
||||
Для типа умной розетки:
|
||||
- [ ] Функционал не изменяется: включение/выключение + запрос мощности.
|
||||
- [ ] Взаимодействие организовано синхронно, через TCP.
|
||||
- [ ] Розетка может использовать как реальный TCP-обмен, так и имитировать реальную работу (для тестов).
|
||||
- [x] Функционал не изменяется: включение/выключение + запрос мощности.
|
||||
- [x] Взаимодействие организовано синхронно, через TCP.
|
||||
- [x] Розетка может использовать как реальный TCP-обмен, так и имитировать реальную работу (для тестов).
|
||||
|
||||
Для имитатора умной розетки:
|
||||
- [ ] Читает адрес для приёма TCP-соединений из аргументов командной строки.
|
||||
|
||||
@@ -41,12 +41,13 @@ impl PrintStatus for Device {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::power_socket::PowerSocketStub;
|
||||
use crate::{PowerSocket, Thermometer};
|
||||
|
||||
#[test]
|
||||
fn smoke_test() {
|
||||
let dev_thermometer = Device::Thermometer(Thermometer::new(20.1));
|
||||
let dev_power_socket = Device::PowerSocket(PowerSocket::new(11.2, false));
|
||||
let dev_power_socket = Device::PowerSocket(PowerSocket::stub(11.2, false));
|
||||
|
||||
dev_thermometer.print_status();
|
||||
dev_power_socket.print_status();
|
||||
@@ -63,7 +64,7 @@ mod tests {
|
||||
#[test]
|
||||
fn display_test() {
|
||||
let dev_thermometer = Device::Thermometer(Thermometer::new(20.1));
|
||||
let dev_power_socket = Device::PowerSocket(PowerSocket::new(11.2, false));
|
||||
let dev_power_socket = Device::PowerSocket(PowerSocket::new(Box::new(PowerSocketStub::new(11.2, false))));
|
||||
|
||||
assert_eq!(format!("{}", dev_thermometer.display()), "DEV:Thermometer[ 20.1 ]");
|
||||
assert_eq!(format!("{}", dev_power_socket.display()), "DEV:PowerSocket[ OFF : 0.0 ]");
|
||||
|
||||
@@ -62,8 +62,8 @@ mod tests {
|
||||
Room::new(
|
||||
[
|
||||
("ThermA".to_string(), Thermometer::new(20.0).into()),
|
||||
("PSocA".to_string(), PowerSocket::new(12.34, false).into()),
|
||||
("PSocB".to_string(), PowerSocket::new(10.01, true).into()),
|
||||
("PSocA".to_string(), PowerSocket::stub(12.34, false).into()),
|
||||
("PSocB".to_string(), PowerSocket::stub(10.01, true).into()),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
@@ -73,7 +73,7 @@ mod tests {
|
||||
"bedroom".to_string(),
|
||||
Room::new(
|
||||
[
|
||||
("PSocC".to_string(), PowerSocket::new(11.11, true).into()),
|
||||
("PSocC".to_string(), PowerSocket::stub(11.11, true).into()),
|
||||
("ThermB".to_string(), Thermometer::new(17.99).into()),
|
||||
]
|
||||
.into_iter()
|
||||
|
||||
@@ -25,23 +25,23 @@ fn create_house_demo() -> House {
|
||||
(
|
||||
"Hall".to_string(),
|
||||
room!(
|
||||
"PSocA" => PowerSocket::new(9.5, true),
|
||||
"PSocA" => PowerSocket::stub(9.5, true),
|
||||
"ThermA" => Thermometer::new(20.1),
|
||||
),
|
||||
),
|
||||
(
|
||||
"Main".to_string(),
|
||||
room!(
|
||||
"PSocB" => PowerSocket::new(11.2, true),
|
||||
"PSocB" => PowerSocket::stub(11.2, true),
|
||||
"ThermB" => Thermometer::new(24.5),
|
||||
"PSocC" => PowerSocket::new(10.4, true),
|
||||
"PSocC" => PowerSocket::stub(10.4, true),
|
||||
),
|
||||
),
|
||||
(
|
||||
"Bedroom".to_string(),
|
||||
room!(
|
||||
"ThermC" => Thermometer::new(19.3),
|
||||
"PSocD" => PowerSocket::new(12.1, true),
|
||||
"PSocD" => PowerSocket::stub(12.1, true),
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -76,7 +76,10 @@ fn add_new_room_in_house_demo(house: &mut House) {
|
||||
|
||||
fn add_power_socket_to_closet_room_demo(house: &mut House) {
|
||||
println!("# Add power socket to 'Closet' room");
|
||||
house.get_room_mut("Closet").unwrap().insert_device("PSocE", PowerSocket::new(8.0, true).into());
|
||||
house
|
||||
.get_room_mut("Closet")
|
||||
.unwrap()
|
||||
.insert_device("PSocE", PowerSocket::stub(8.0, true).into());
|
||||
house.print_status();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,41 @@
|
||||
use std::fmt::Display;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{SocketAddr, TcpStream};
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PowerSocket {
|
||||
power_rate: f32,
|
||||
on: bool,
|
||||
handle: Box<dyn PowerSocketHandle>,
|
||||
}
|
||||
|
||||
impl PowerSocket {
|
||||
pub fn new(power_rate: f32, on: bool) -> Self {
|
||||
Self { power_rate, on }
|
||||
pub fn stub(power_rate: f32, on: bool) -> Self {
|
||||
Self {
|
||||
handle: Box::new(PowerSocketStub::new(power_rate, on)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect(addr: &str) -> Self {
|
||||
Self {
|
||||
handle: Box::new(PowerSocketClient::new(addr)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(handle: Box<dyn PowerSocketHandle>) -> Self {
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub fn is_on(&self) -> bool {
|
||||
self.on
|
||||
self.handle.is_on()
|
||||
}
|
||||
|
||||
pub fn set_on(&mut self, on: bool) {
|
||||
self.on = on
|
||||
self.handle.set_on(on)
|
||||
}
|
||||
|
||||
pub fn get_power(&self) -> f32 {
|
||||
if self.on { self.power_rate } else { 0.0 }
|
||||
self.handle.get_power()
|
||||
}
|
||||
|
||||
pub fn display(&self) -> impl Display {
|
||||
@@ -29,15 +44,95 @@ impl PowerSocket {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PowerSocketHandle: Debug {
|
||||
fn is_on(&self) -> bool;
|
||||
|
||||
fn set_on(&mut self, on: bool);
|
||||
|
||||
fn get_power(&self) -> f32;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PowerSocketStub {
|
||||
power_rate: f32,
|
||||
on: bool,
|
||||
}
|
||||
|
||||
impl PowerSocketStub {
|
||||
pub fn new(power_rate: f32, on: bool) -> Self {
|
||||
Self { power_rate, on }
|
||||
}
|
||||
}
|
||||
|
||||
impl PowerSocketHandle for PowerSocketStub {
|
||||
fn is_on(&self) -> bool {
|
||||
self.on
|
||||
}
|
||||
|
||||
fn set_on(&mut self, on: bool) {
|
||||
self.on = on
|
||||
}
|
||||
|
||||
fn get_power(&self) -> f32 {
|
||||
if self.on { self.power_rate } else { 0.0 }
|
||||
}
|
||||
}
|
||||
|
||||
const TIMEOUT: Duration = Duration::from_secs(5);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PowerSocketClient {
|
||||
stream: RefCell<TcpStream>,
|
||||
}
|
||||
|
||||
impl PowerSocketClient {
|
||||
pub fn new(addr: &str) -> Self {
|
||||
let addr: SocketAddr = addr.parse().expect("addr should be valid tcp address");
|
||||
let stream = TcpStream::connect_timeout(&addr, TIMEOUT).expect("address should be available to connect");
|
||||
stream.set_write_timeout(Some(TIMEOUT)).expect("write timeout should be set");
|
||||
stream.set_read_timeout(Some(TIMEOUT)).expect("read timeout should be set");
|
||||
Self { stream: RefCell::new(stream) }
|
||||
}
|
||||
}
|
||||
|
||||
const CMD_GET_ON: u8 = 1;
|
||||
const CMD_TURN_ON: u8 = 2;
|
||||
const CMD_TURN_OFF: u8 = 3;
|
||||
const CMD_GET_POWER: u8 = 4;
|
||||
|
||||
impl PowerSocketHandle for PowerSocketClient {
|
||||
fn is_on(&self) -> bool {
|
||||
let mut buf = [CMD_GET_ON; 1];
|
||||
self.stream.borrow_mut().write_all(&buf).expect("CMD_GET_ON request should be sent");
|
||||
self.stream.borrow_mut().read_exact(&mut buf).expect("CMD_GET_ON response should be received");
|
||||
!matches!(buf, [0])
|
||||
}
|
||||
|
||||
fn set_on(&mut self, on: bool) {
|
||||
let cmd = if on { CMD_TURN_ON } else { CMD_TURN_OFF };
|
||||
let mut buf = [cmd; 1];
|
||||
self.stream.borrow_mut().write_all(&buf).expect("change state request should be sent");
|
||||
self.stream.borrow_mut().read_exact(&mut buf).expect("change state response should be received");
|
||||
}
|
||||
|
||||
fn get_power(&self) -> f32 {
|
||||
let mut buf = [CMD_GET_POWER; 4];
|
||||
self.stream.borrow_mut().write_all(&buf[0..1]).expect("CMD_GET_POWER request should be sent");
|
||||
self.stream
|
||||
.borrow_mut()
|
||||
.read_exact(&mut buf)
|
||||
.expect("CMD_GET_POWER response should be received");
|
||||
f32::from_le_bytes(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke_test() {
|
||||
let mut power_socket = PowerSocket::new(12.4, false);
|
||||
assert_eq!(power_socket.power_rate, 12.4);
|
||||
assert!(!power_socket.on);
|
||||
let mut power_socket = PowerSocket::new(Box::new(PowerSocketStub::new(12.4, false)));
|
||||
assert!(!power_socket.is_on());
|
||||
assert_eq!(power_socket.get_power(), 0.0);
|
||||
|
||||
@@ -48,8 +143,17 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn display_test() {
|
||||
assert_eq!(format!("{}", PowerSocket::new(11.549, false).display()), "PowerSocket[ OFF : 0.0 ]");
|
||||
assert_eq!(format!("{}", PowerSocket::new(11.549, true).display()), "PowerSocket[ ON : 11.5 ]");
|
||||
assert_eq!(format!("{}", PowerSocket::new(11.550, true).display()), "PowerSocket[ ON : 11.6 ]");
|
||||
assert_eq!(
|
||||
format!("{}", PowerSocket::new(Box::new(PowerSocketStub::new(11.549, false))).display()),
|
||||
"PowerSocket[ OFF : 0.0 ]"
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", PowerSocket::new(Box::new(PowerSocketStub::new(11.549, true))).display()),
|
||||
"PowerSocket[ ON : 11.5 ]"
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", PowerSocket::new(Box::new(PowerSocketStub::new(11.550, true))).display()),
|
||||
"PowerSocket[ ON : 11.6 ]"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,11 +49,12 @@ macro_rules! room {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::power_socket::PowerSocketStub;
|
||||
use crate::{PowerSocket, Thermometer};
|
||||
|
||||
fn create_test_room() -> Room {
|
||||
room!(
|
||||
"PSoc" => PowerSocket::new(12.34, false),
|
||||
"PSoc" => PowerSocket::new(Box::new(PowerSocketStub::new(12.34, false))),
|
||||
"Therm" => Thermometer::new(21.56),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user