wrap into workspace
This commit is contained in:
177
smart-house/house/src/power_socket.rs
Normal file
177
smart-house/house/src/power_socket.rs
Normal file
@@ -0,0 +1,177 @@
|
||||
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 {
|
||||
handle: Box<dyn PowerSocketHandle>,
|
||||
}
|
||||
|
||||
impl PowerSocket {
|
||||
pub fn stub(power_rate: f32, on: bool) -> Self {
|
||||
Self {
|
||||
handle: Box::new(PowerSocketStub::new(power_rate, on)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect(addr: &str) -> Result<Self, std::io::Error> {
|
||||
let handle = PowerSocketClient::new(addr)?;
|
||||
Ok(Self { handle: Box::new(handle) })
|
||||
}
|
||||
|
||||
pub fn is_on(&self) -> Result<bool, std::io::Error> {
|
||||
self.handle.is_on()
|
||||
}
|
||||
|
||||
pub fn set_on(&mut self, on: bool) -> Result<(), std::io::Error> {
|
||||
self.handle.set_on(on)
|
||||
}
|
||||
|
||||
pub fn get_power(&self) -> Result<f32, std::io::Error> {
|
||||
self.handle.get_power()
|
||||
}
|
||||
|
||||
pub fn display(&self) -> impl Display {
|
||||
const ERR: &str = "PowerSocket[ ERR ]";
|
||||
let power = match self.get_power() {
|
||||
Ok(power) => power,
|
||||
Err(e) => {
|
||||
eprintln!("error on get power: {:?}", e);
|
||||
return ERR.to_string();
|
||||
}
|
||||
};
|
||||
let on = match self.is_on() {
|
||||
Ok(state) => state,
|
||||
Err(e) => {
|
||||
eprintln!("error on get state: {:?}", e);
|
||||
return ERR.to_string();
|
||||
}
|
||||
};
|
||||
let state = if on { "ON" } else { "OFF" };
|
||||
format!("PowerSocket[ {} : {:02.1} ]", state, power)
|
||||
}
|
||||
}
|
||||
|
||||
trait PowerSocketHandle: Debug {
|
||||
fn is_on(&self) -> Result<bool, std::io::Error>;
|
||||
|
||||
fn set_on(&mut self, on: bool) -> Result<(), std::io::Error>;
|
||||
|
||||
fn get_power(&self) -> Result<f32, std::io::Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
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) -> Result<bool, std::io::Error> {
|
||||
Ok(self.on)
|
||||
}
|
||||
|
||||
fn set_on(&mut self, on: bool) -> Result<(), std::io::Error> {
|
||||
self.on = on;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_power(&self) -> Result<f32, std::io::Error> {
|
||||
Ok(if self.on { self.power_rate } else { 0.0 })
|
||||
}
|
||||
}
|
||||
|
||||
const TIMEOUT: Duration = Duration::from_secs(5);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PowerSocketClient {
|
||||
stream: RefCell<TcpStream>,
|
||||
}
|
||||
|
||||
impl PowerSocketClient {
|
||||
fn new(addr: &str) -> Result<Self, std::io::Error> {
|
||||
let addr: SocketAddr = addr.parse().map_err(std::io::Error::other)?;
|
||||
let stream = TcpStream::connect_timeout(&addr, TIMEOUT)?;
|
||||
stream.set_write_timeout(Some(TIMEOUT))?;
|
||||
stream.set_read_timeout(Some(TIMEOUT))?;
|
||||
Ok(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) -> Result<bool, std::io::Error> {
|
||||
let mut buf = [CMD_GET_ON; 1];
|
||||
self.stream
|
||||
.borrow_mut()
|
||||
.write_all(&buf)
|
||||
.map_err(|e| std::io::Error::other(format!("CMD_GET_ON request error: {:?}", e)))?;
|
||||
self.stream
|
||||
.borrow_mut()
|
||||
.read_exact(&mut buf)
|
||||
.map_err(|e| std::io::Error::other(format!("CMD_GET_ON response error: {:?}", e)))?;
|
||||
Ok(!matches!(buf, [0]))
|
||||
}
|
||||
|
||||
fn set_on(&mut self, on: bool) -> Result<(), std::io::Error> {
|
||||
let cmd = if on { CMD_TURN_ON } else { CMD_TURN_OFF };
|
||||
let mut buf = [cmd; 1];
|
||||
self.stream
|
||||
.borrow_mut()
|
||||
.write_all(&buf)
|
||||
.map_err(|e| std::io::Error::other(format!("change state request error: {:?}", e)))?;
|
||||
self.stream
|
||||
.borrow_mut()
|
||||
.read_exact(&mut buf)
|
||||
.map_err(|e| std::io::Error::other(format!("change state response error: {:?}", e)))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_power(&self) -> Result<f32, std::io::Error> {
|
||||
let mut buf = [CMD_GET_POWER; 4];
|
||||
self.stream
|
||||
.borrow_mut()
|
||||
.write_all(&buf[0..1])
|
||||
.map_err(|e| std::io::Error::other(format!("CMD_GET_POWER request error: {:?}", e)))?;
|
||||
self.stream
|
||||
.borrow_mut()
|
||||
.read_exact(&mut buf)
|
||||
.map_err(|e| std::io::Error::other(format!("CMD_GET_POWER response error: {:?}", e)))?;
|
||||
Ok(f32::from_le_bytes(buf))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke_test() {
|
||||
let mut power_socket = PowerSocket::stub(12.4, false);
|
||||
assert!(!power_socket.is_on().unwrap());
|
||||
assert_eq!(power_socket.get_power().unwrap(), 0.0);
|
||||
|
||||
power_socket.set_on(true).unwrap();
|
||||
assert!(power_socket.is_on().unwrap());
|
||||
assert_eq!(power_socket.get_power().unwrap(), 12.4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_test() {
|
||||
assert_eq!(format!("{}", PowerSocket::stub(11.549, false).display()), "PowerSocket[ OFF : 0.0 ]");
|
||||
assert_eq!(format!("{}", PowerSocket::stub(11.549, true).display()), "PowerSocket[ ON : 11.5 ]");
|
||||
assert_eq!(format!("{}", PowerSocket::stub(11.550, true).display()), "PowerSocket[ ON : 11.6 ]");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user