homework: upgrade power socket
This commit is contained in:
@@ -122,9 +122,9 @@
|
|||||||
### Описание/Пошаговая инструкция выполнения домашнего задания:
|
### Описание/Пошаговая инструкция выполнения домашнего задания:
|
||||||
|
|
||||||
Для типа умной розетки:
|
Для типа умной розетки:
|
||||||
- [ ] Функционал не изменяется: включение/выключение + запрос мощности.
|
- [x] Функционал не изменяется: включение/выключение + запрос мощности.
|
||||||
- [ ] Взаимодействие организовано синхронно, через TCP.
|
- [x] Взаимодействие организовано синхронно, через TCP.
|
||||||
- [ ] Розетка может использовать как реальный TCP-обмен, так и имитировать реальную работу (для тестов).
|
- [x] Розетка может использовать как реальный TCP-обмен, так и имитировать реальную работу (для тестов).
|
||||||
|
|
||||||
Для имитатора умной розетки:
|
Для имитатора умной розетки:
|
||||||
- [ ] Читает адрес для приёма TCP-соединений из аргументов командной строки.
|
- [ ] Читает адрес для приёма TCP-соединений из аргументов командной строки.
|
||||||
|
|||||||
@@ -41,12 +41,13 @@ impl PrintStatus for Device {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::power_socket::PowerSocketStub;
|
||||||
use crate::{PowerSocket, Thermometer};
|
use crate::{PowerSocket, Thermometer};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn smoke_test() {
|
fn smoke_test() {
|
||||||
let dev_thermometer = Device::Thermometer(Thermometer::new(20.1));
|
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_thermometer.print_status();
|
||||||
dev_power_socket.print_status();
|
dev_power_socket.print_status();
|
||||||
@@ -63,7 +64,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn display_test() {
|
fn display_test() {
|
||||||
let dev_thermometer = Device::Thermometer(Thermometer::new(20.1));
|
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_thermometer.display()), "DEV:Thermometer[ 20.1 ]");
|
||||||
assert_eq!(format!("{}", dev_power_socket.display()), "DEV:PowerSocket[ OFF : 0.0 ]");
|
assert_eq!(format!("{}", dev_power_socket.display()), "DEV:PowerSocket[ OFF : 0.0 ]");
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ mod tests {
|
|||||||
Room::new(
|
Room::new(
|
||||||
[
|
[
|
||||||
("ThermA".to_string(), Thermometer::new(20.0).into()),
|
("ThermA".to_string(), Thermometer::new(20.0).into()),
|
||||||
("PSocA".to_string(), PowerSocket::new(12.34, false).into()),
|
("PSocA".to_string(), PowerSocket::stub(12.34, false).into()),
|
||||||
("PSocB".to_string(), PowerSocket::new(10.01, true).into()),
|
("PSocB".to_string(), PowerSocket::stub(10.01, true).into()),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect(),
|
.collect(),
|
||||||
@@ -73,7 +73,7 @@ mod tests {
|
|||||||
"bedroom".to_string(),
|
"bedroom".to_string(),
|
||||||
Room::new(
|
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()),
|
("ThermB".to_string(), Thermometer::new(17.99).into()),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|||||||
@@ -25,23 +25,23 @@ fn create_house_demo() -> House {
|
|||||||
(
|
(
|
||||||
"Hall".to_string(),
|
"Hall".to_string(),
|
||||||
room!(
|
room!(
|
||||||
"PSocA" => PowerSocket::new(9.5, true),
|
"PSocA" => PowerSocket::stub(9.5, true),
|
||||||
"ThermA" => Thermometer::new(20.1),
|
"ThermA" => Thermometer::new(20.1),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"Main".to_string(),
|
"Main".to_string(),
|
||||||
room!(
|
room!(
|
||||||
"PSocB" => PowerSocket::new(11.2, true),
|
"PSocB" => PowerSocket::stub(11.2, true),
|
||||||
"ThermB" => Thermometer::new(24.5),
|
"ThermB" => Thermometer::new(24.5),
|
||||||
"PSocC" => PowerSocket::new(10.4, true),
|
"PSocC" => PowerSocket::stub(10.4, true),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"Bedroom".to_string(),
|
"Bedroom".to_string(),
|
||||||
room!(
|
room!(
|
||||||
"ThermC" => Thermometer::new(19.3),
|
"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) {
|
fn add_power_socket_to_closet_room_demo(house: &mut House) {
|
||||||
println!("# Add power socket to 'Closet' room");
|
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();
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct PowerSocket {
|
pub struct PowerSocket {
|
||||||
power_rate: f32,
|
handle: Box<dyn PowerSocketHandle>,
|
||||||
on: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PowerSocket {
|
impl PowerSocket {
|
||||||
pub fn new(power_rate: f32, on: bool) -> Self {
|
pub fn stub(power_rate: f32, on: bool) -> Self {
|
||||||
Self { power_rate, on }
|
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 {
|
pub fn is_on(&self) -> bool {
|
||||||
self.on
|
self.handle.is_on()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_on(&mut self, on: bool) {
|
pub fn set_on(&mut self, on: bool) {
|
||||||
self.on = on
|
self.handle.set_on(on)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_power(&self) -> f32 {
|
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 {
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn smoke_test() {
|
fn smoke_test() {
|
||||||
let mut power_socket = PowerSocket::new(12.4, false);
|
let mut power_socket = PowerSocket::new(Box::new(PowerSocketStub::new(12.4, false)));
|
||||||
assert_eq!(power_socket.power_rate, 12.4);
|
|
||||||
assert!(!power_socket.on);
|
|
||||||
assert!(!power_socket.is_on());
|
assert!(!power_socket.is_on());
|
||||||
assert_eq!(power_socket.get_power(), 0.0);
|
assert_eq!(power_socket.get_power(), 0.0);
|
||||||
|
|
||||||
@@ -48,8 +143,17 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn display_test() {
|
fn display_test() {
|
||||||
assert_eq!(format!("{}", PowerSocket::new(11.549, false).display()), "PowerSocket[ OFF : 0.0 ]");
|
assert_eq!(
|
||||||
assert_eq!(format!("{}", PowerSocket::new(11.549, true).display()), "PowerSocket[ ON : 11.5 ]");
|
format!("{}", PowerSocket::new(Box::new(PowerSocketStub::new(11.549, false))).display()),
|
||||||
assert_eq!(format!("{}", PowerSocket::new(11.550, true).display()), "PowerSocket[ ON : 11.6 ]");
|
"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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::power_socket::PowerSocketStub;
|
||||||
use crate::{PowerSocket, Thermometer};
|
use crate::{PowerSocket, Thermometer};
|
||||||
|
|
||||||
fn create_test_room() -> Room {
|
fn create_test_room() -> Room {
|
||||||
room!(
|
room!(
|
||||||
"PSoc" => PowerSocket::new(12.34, false),
|
"PSoc" => PowerSocket::new(Box::new(PowerSocketStub::new(12.34, false))),
|
||||||
"Therm" => Thermometer::new(21.56),
|
"Therm" => Thermometer::new(21.56),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user