|
|
@ -1,5 +1,5 @@ |
|
|
|
use sprinklers_core::model::{SectionId, SectionRef}; |
|
|
|
use sprinklers_core::model::{ZoneId, ZoneRef}; |
|
|
|
use sprinklers_core::section_interface::SectionInterface; |
|
|
|
use sprinklers_core::zone_interface::ZoneInterface; |
|
|
|
|
|
|
|
|
|
|
|
use actix::{ |
|
|
|
use actix::{ |
|
|
|
Actor, ActorContext, Addr, AsyncContext, Handler, Message, MessageResult, SpawnHandle, |
|
|
|
Actor, ActorContext, Addr, AsyncContext, Handler, Message, MessageResult, SpawnHandle, |
|
|
@ -22,32 +22,32 @@ use tokio::{ |
|
|
|
use tracing::{debug, trace, warn}; |
|
|
|
use tracing::{debug, trace, warn}; |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)] |
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)] |
|
|
|
pub struct SectionRunHandle(i32); |
|
|
|
pub struct ZoneRunHandle(i32); |
|
|
|
|
|
|
|
|
|
|
|
impl SectionRunHandle { |
|
|
|
impl ZoneRunHandle { |
|
|
|
pub fn into_inner(self) -> i32 { |
|
|
|
pub fn into_inner(self) -> i32 { |
|
|
|
self.0 |
|
|
|
self.0 |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)] |
|
|
|
#[derive(Clone, Debug)] |
|
|
|
pub enum SectionEvent { |
|
|
|
pub enum ZoneEvent { |
|
|
|
RunStart(SectionRunHandle, SectionRef), |
|
|
|
RunStart(ZoneRunHandle, ZoneRef), |
|
|
|
RunFinish(SectionRunHandle, SectionRef), |
|
|
|
RunFinish(ZoneRunHandle, ZoneRef), |
|
|
|
RunPause(SectionRunHandle, SectionRef), |
|
|
|
RunPause(ZoneRunHandle, ZoneRef), |
|
|
|
RunUnpause(SectionRunHandle, SectionRef), |
|
|
|
RunUnpause(ZoneRunHandle, ZoneRef), |
|
|
|
RunCancel(SectionRunHandle, SectionRef), |
|
|
|
RunCancel(ZoneRunHandle, ZoneRef), |
|
|
|
RunnerPause, |
|
|
|
RunnerPause, |
|
|
|
RunnerUnpause, |
|
|
|
RunnerUnpause, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub type SectionEventRecv = broadcast::Receiver<SectionEvent>; |
|
|
|
pub type ZoneEventRecv = broadcast::Receiver<ZoneEvent>; |
|
|
|
type SectionEventSend = broadcast::Sender<SectionEvent>; |
|
|
|
type ZoneEventSend = broadcast::Sender<ZoneEvent>; |
|
|
|
|
|
|
|
|
|
|
|
const EVENT_CAPACITY: usize = 8; |
|
|
|
const EVENT_CAPACITY: usize = 8; |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)] |
|
|
|
#[derive(Clone, Debug, PartialEq)] |
|
|
|
pub enum SecRunState { |
|
|
|
pub enum ZoneRunState { |
|
|
|
Waiting, |
|
|
|
Waiting, |
|
|
|
Running { |
|
|
|
Running { |
|
|
|
start_time: Instant, |
|
|
|
start_time: Instant, |
|
|
@ -61,64 +61,64 @@ pub enum SecRunState { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)] |
|
|
|
#[derive(Clone, Debug)] |
|
|
|
pub struct SecRun { |
|
|
|
pub struct ZoneRun { |
|
|
|
pub handle: SectionRunHandle, |
|
|
|
pub handle: ZoneRunHandle, |
|
|
|
pub section: SectionRef, |
|
|
|
pub zone: ZoneRef, |
|
|
|
pub duration: Duration, |
|
|
|
pub duration: Duration, |
|
|
|
pub total_duration: Duration, |
|
|
|
pub total_duration: Duration, |
|
|
|
pub state: SecRunState, |
|
|
|
pub state: ZoneRunState, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl SecRun { |
|
|
|
impl ZoneRun { |
|
|
|
fn new(handle: SectionRunHandle, section: SectionRef, duration: Duration) -> Self { |
|
|
|
fn new(handle: ZoneRunHandle, zone: ZoneRef, duration: Duration) -> Self { |
|
|
|
Self { |
|
|
|
Self { |
|
|
|
handle, |
|
|
|
handle, |
|
|
|
section, |
|
|
|
zone, |
|
|
|
duration, |
|
|
|
duration, |
|
|
|
total_duration: duration, |
|
|
|
total_duration: duration, |
|
|
|
state: SecRunState::Waiting, |
|
|
|
state: ZoneRunState::Waiting, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn is_running(&self) -> bool { |
|
|
|
pub fn is_running(&self) -> bool { |
|
|
|
matches!(self.state, SecRunState::Running{..}) |
|
|
|
matches!(self.state, ZoneRunState::Running{..}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)] |
|
|
|
#[allow(dead_code)] |
|
|
|
pub fn is_paused(&self) -> bool { |
|
|
|
pub fn is_paused(&self) -> bool { |
|
|
|
matches!(self.state, SecRunState::Paused{..}) |
|
|
|
matches!(self.state, ZoneRunState::Paused{..}) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub type SecRunQueue = im::Vector<Arc<SecRun>>; |
|
|
|
pub type ZoneRunQueue = im::Vector<Arc<ZoneRun>>; |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)] |
|
|
|
#[derive(Clone, Debug)] |
|
|
|
pub struct SecRunnerState { |
|
|
|
pub struct ZoneRunnerState { |
|
|
|
pub run_queue: SecRunQueue, |
|
|
|
pub run_queue: ZoneRunQueue, |
|
|
|
pub paused: bool, |
|
|
|
pub paused: bool, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl Default for SecRunnerState { |
|
|
|
impl Default for ZoneRunnerState { |
|
|
|
fn default() -> Self { |
|
|
|
fn default() -> Self { |
|
|
|
Self { |
|
|
|
Self { |
|
|
|
run_queue: SecRunQueue::default(), |
|
|
|
run_queue: ZoneRunQueue::default(), |
|
|
|
paused: false, |
|
|
|
paused: false, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub type SecRunnerStateRecv = watch::Receiver<SecRunnerState>; |
|
|
|
pub type ZoneRunnerStateRecv = watch::Receiver<ZoneRunnerState>; |
|
|
|
|
|
|
|
|
|
|
|
struct SectionRunnerInner { |
|
|
|
struct ZoneRunnerInner { |
|
|
|
interface: Arc<dyn SectionInterface>, |
|
|
|
interface: Arc<dyn ZoneInterface>, |
|
|
|
event_send: Option<SectionEventSend>, |
|
|
|
event_send: Option<ZoneEventSend>, |
|
|
|
state_send: watch::Sender<SecRunnerState>, |
|
|
|
state_send: watch::Sender<ZoneRunnerState>, |
|
|
|
delay_future: Option<SpawnHandle>, |
|
|
|
delay_future: Option<SpawnHandle>, |
|
|
|
did_change: bool, |
|
|
|
did_change: bool, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl SectionRunnerInner { |
|
|
|
impl ZoneRunnerInner { |
|
|
|
fn send_event(&mut self, event: SectionEvent) { |
|
|
|
fn send_event(&mut self, event: ZoneEvent) { |
|
|
|
if let Some(event_send) = &mut self.event_send { |
|
|
|
if let Some(event_send) = &mut self.event_send { |
|
|
|
match event_send.send(event) { |
|
|
|
match event_send.send(event) { |
|
|
|
Ok(_) => {} |
|
|
|
Ok(_) => {} |
|
|
@ -129,7 +129,7 @@ impl SectionRunnerInner { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn subscribe_event(&mut self) -> SectionEventRecv { |
|
|
|
fn subscribe_event(&mut self) -> ZoneEventRecv { |
|
|
|
match &mut self.event_send { |
|
|
|
match &mut self.event_send { |
|
|
|
Some(event_send) => event_send.subscribe(), |
|
|
|
Some(event_send) => event_send.subscribe(), |
|
|
|
None => { |
|
|
|
None => { |
|
|
@ -140,56 +140,44 @@ impl SectionRunnerInner { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn start_run(&mut self, run: &mut Arc<SecRun>) { |
|
|
|
fn start_run(&mut self, run: &mut Arc<ZoneRun>) { |
|
|
|
use SecRunState::*; |
|
|
|
use ZoneRunState::*; |
|
|
|
let run = Arc::make_mut(run); |
|
|
|
let run = Arc::make_mut(run); |
|
|
|
debug!(section_id = run.section.id, "starting running section"); |
|
|
|
debug!(zone_id = run.zone.id, "starting running zone"); |
|
|
|
self.interface |
|
|
|
self.interface.set_zone_state(run.zone.interface_id, true); |
|
|
|
.set_section_state(run.section.interface_id, true); |
|
|
|
|
|
|
|
run.state = Running { |
|
|
|
run.state = Running { |
|
|
|
start_time: Instant::now(), |
|
|
|
start_time: Instant::now(), |
|
|
|
}; |
|
|
|
}; |
|
|
|
self.send_event(SectionEvent::RunStart( |
|
|
|
self.send_event(ZoneEvent::RunStart(run.handle.clone(), run.zone.clone())); |
|
|
|
run.handle.clone(), |
|
|
|
|
|
|
|
run.section.clone(), |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
self.did_change = true; |
|
|
|
self.did_change = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn finish_run(&mut self, run: &mut Arc<SecRun>) { |
|
|
|
fn finish_run(&mut self, run: &mut Arc<ZoneRun>) { |
|
|
|
let run = Arc::make_mut(run); |
|
|
|
let run = Arc::make_mut(run); |
|
|
|
if run.is_running() { |
|
|
|
if run.is_running() { |
|
|
|
debug!(section_id = run.section.id, "finished running section"); |
|
|
|
debug!(zone_id = run.zone.id, "finished running zone"); |
|
|
|
self.interface |
|
|
|
self.interface.set_zone_state(run.zone.interface_id, false); |
|
|
|
.set_section_state(run.section.interface_id, false); |
|
|
|
run.state = ZoneRunState::Finished; |
|
|
|
run.state = SecRunState::Finished; |
|
|
|
self.send_event(ZoneEvent::RunFinish(run.handle.clone(), run.zone.clone())); |
|
|
|
self.send_event(SectionEvent::RunFinish( |
|
|
|
|
|
|
|
run.handle.clone(), |
|
|
|
|
|
|
|
run.section.clone(), |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
self.did_change = true; |
|
|
|
self.did_change = true; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
warn!( |
|
|
|
warn!( |
|
|
|
section_id = run.section.id, |
|
|
|
zone_id = run.zone.id, |
|
|
|
state = debug(&run.state), |
|
|
|
state = debug(&run.state), |
|
|
|
"cannot finish run which is not running" |
|
|
|
"cannot finish run which is not running" |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn cancel_run(&mut self, run: &mut Arc<SecRun>) -> bool { |
|
|
|
fn cancel_run(&mut self, run: &mut Arc<ZoneRun>) -> bool { |
|
|
|
let run = Arc::make_mut(run); |
|
|
|
let run = Arc::make_mut(run); |
|
|
|
if run.is_running() { |
|
|
|
if run.is_running() { |
|
|
|
debug!(section_id = run.section.id, "cancelling running section"); |
|
|
|
debug!(zone_id = run.zone.id, "cancelling running zone"); |
|
|
|
self.interface |
|
|
|
self.interface.set_zone_state(run.zone.interface_id, false); |
|
|
|
.set_section_state(run.section.interface_id, false); |
|
|
|
} |
|
|
|
} |
|
|
|
if run.state != ZoneRunState::Cancelled { |
|
|
|
if run.state != SecRunState::Cancelled { |
|
|
|
run.state = ZoneRunState::Cancelled; |
|
|
|
run.state = SecRunState::Cancelled; |
|
|
|
self.send_event(ZoneEvent::RunCancel(run.handle.clone(), run.zone.clone())); |
|
|
|
self.send_event(SectionEvent::RunCancel( |
|
|
|
|
|
|
|
run.handle.clone(), |
|
|
|
|
|
|
|
run.section.clone(), |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
self.did_change = true; |
|
|
|
self.did_change = true; |
|
|
|
true |
|
|
|
true |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -197,21 +185,20 @@ impl SectionRunnerInner { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn pause_run(&mut self, run: &mut Arc<SecRun>) { |
|
|
|
fn pause_run(&mut self, run: &mut Arc<ZoneRun>) { |
|
|
|
use SecRunState::*; |
|
|
|
use ZoneRunState::*; |
|
|
|
let run = Arc::make_mut(run); |
|
|
|
let run = Arc::make_mut(run); |
|
|
|
let new_state = match run.state { |
|
|
|
let new_state = match run.state { |
|
|
|
Running { start_time } => { |
|
|
|
Running { start_time } => { |
|
|
|
debug!(section_id = run.section.id, "pausing running section"); |
|
|
|
debug!(zone_id = run.zone.id, "pausing running zone"); |
|
|
|
self.interface |
|
|
|
self.interface.set_zone_state(run.zone.interface_id, false); |
|
|
|
.set_section_state(run.section.interface_id, false); |
|
|
|
|
|
|
|
Paused { |
|
|
|
Paused { |
|
|
|
start_time, |
|
|
|
start_time, |
|
|
|
pause_time: Instant::now(), |
|
|
|
pause_time: Instant::now(), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
Waiting => { |
|
|
|
Waiting => { |
|
|
|
debug!(section_id = run.section.id, "pausing waiting section"); |
|
|
|
debug!(zone_id = run.zone.id, "pausing waiting zone"); |
|
|
|
Paused { |
|
|
|
Paused { |
|
|
|
start_time: Instant::now(), |
|
|
|
start_time: Instant::now(), |
|
|
|
pause_time: Instant::now(), |
|
|
|
pause_time: Instant::now(), |
|
|
@ -222,39 +209,32 @@ impl SectionRunnerInner { |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
run.state = new_state; |
|
|
|
run.state = new_state; |
|
|
|
self.send_event(SectionEvent::RunPause( |
|
|
|
self.send_event(ZoneEvent::RunPause(run.handle.clone(), run.zone.clone())); |
|
|
|
run.handle.clone(), |
|
|
|
|
|
|
|
run.section.clone(), |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
self.did_change = true; |
|
|
|
self.did_change = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn unpause_run(&mut self, run: &mut Arc<SecRun>) { |
|
|
|
fn unpause_run(&mut self, run: &mut Arc<ZoneRun>) { |
|
|
|
use SecRunState::*; |
|
|
|
use ZoneRunState::*; |
|
|
|
let run = Arc::make_mut(run); |
|
|
|
let run = Arc::make_mut(run); |
|
|
|
match run.state { |
|
|
|
match run.state { |
|
|
|
Paused { |
|
|
|
Paused { |
|
|
|
start_time, |
|
|
|
start_time, |
|
|
|
pause_time, |
|
|
|
pause_time, |
|
|
|
} => { |
|
|
|
} => { |
|
|
|
debug!(section_id = run.section.id, "unpausing section"); |
|
|
|
debug!(zone_id = run.zone.id, "unpausing zone"); |
|
|
|
self.interface |
|
|
|
self.interface.set_zone_state(run.zone.interface_id, true); |
|
|
|
.set_section_state(run.section.interface_id, true); |
|
|
|
|
|
|
|
run.state = Running { |
|
|
|
run.state = Running { |
|
|
|
start_time: Instant::now(), |
|
|
|
start_time: Instant::now(), |
|
|
|
}; |
|
|
|
}; |
|
|
|
let ran_for = pause_time - start_time; |
|
|
|
let ran_for = pause_time - start_time; |
|
|
|
run.duration -= ran_for; |
|
|
|
run.duration -= ran_for; |
|
|
|
self.send_event(SectionEvent::RunUnpause( |
|
|
|
self.send_event(ZoneEvent::RunUnpause(run.handle.clone(), run.zone.clone())); |
|
|
|
run.handle.clone(), |
|
|
|
|
|
|
|
run.section.clone(), |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
Waiting | Finished | Cancelled | Running { .. } => { |
|
|
|
Waiting | Finished | Cancelled | Running { .. } => { |
|
|
|
warn!( |
|
|
|
warn!( |
|
|
|
section_id = run.section.id, |
|
|
|
zone_id = run.zone.id, |
|
|
|
state = debug(&run.state), |
|
|
|
state = debug(&run.state), |
|
|
|
"can only unpause paused section" |
|
|
|
"can only unpause paused zone" |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -264,7 +244,7 @@ impl SectionRunnerInner { |
|
|
|
fn process_after_delay( |
|
|
|
fn process_after_delay( |
|
|
|
&mut self, |
|
|
|
&mut self, |
|
|
|
after: Duration, |
|
|
|
after: Duration, |
|
|
|
ctx: &mut <SectionRunnerActor as Actor>::Context, |
|
|
|
ctx: &mut <ZoneRunnerActor as Actor>::Context, |
|
|
|
) { |
|
|
|
) { |
|
|
|
let delay_future = ctx.notify_later(Process, after); |
|
|
|
let delay_future = ctx.notify_later(Process, after); |
|
|
|
if let Some(old_future) = self.delay_future.replace(delay_future) { |
|
|
|
if let Some(old_future) = self.delay_future.replace(delay_future) { |
|
|
@ -272,32 +252,32 @@ impl SectionRunnerInner { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn cancel_process(&mut self, ctx: &mut <SectionRunnerActor as Actor>::Context) { |
|
|
|
fn cancel_process(&mut self, ctx: &mut <ZoneRunnerActor as Actor>::Context) { |
|
|
|
if let Some(old_future) = self.delay_future.take() { |
|
|
|
if let Some(old_future) = self.delay_future.take() { |
|
|
|
ctx.cancel_future(old_future); |
|
|
|
ctx.cancel_future(old_future); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct SectionRunnerActor { |
|
|
|
struct ZoneRunnerActor { |
|
|
|
state: SecRunnerState, |
|
|
|
state: ZoneRunnerState, |
|
|
|
inner: SectionRunnerInner, |
|
|
|
inner: ZoneRunnerInner, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl Actor for SectionRunnerActor { |
|
|
|
impl Actor for ZoneRunnerActor { |
|
|
|
type Context = actix::Context<Self>; |
|
|
|
type Context = actix::Context<Self>; |
|
|
|
|
|
|
|
|
|
|
|
fn started(&mut self, _ctx: &mut Self::Context) { |
|
|
|
fn started(&mut self, _ctx: &mut Self::Context) { |
|
|
|
trace!("section_runner starting"); |
|
|
|
trace!("zone_runner starting"); |
|
|
|
for i in 0..self.inner.interface.num_sections() { |
|
|
|
for i in 0..self.inner.interface.num_zones() { |
|
|
|
self.inner.interface.set_section_state(i, false); |
|
|
|
self.inner.interface.set_zone_state(i, false); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn stopped(&mut self, _ctx: &mut Self::Context) { |
|
|
|
fn stopped(&mut self, _ctx: &mut Self::Context) { |
|
|
|
trace!("section_runner stopped"); |
|
|
|
trace!("zone_runner stopped"); |
|
|
|
for i in 0..self.inner.interface.num_sections() { |
|
|
|
for i in 0..self.inner.interface.num_zones() { |
|
|
|
self.inner.interface.set_section_state(i, false); |
|
|
|
self.inner.interface.set_zone_state(i, false); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -306,7 +286,7 @@ impl Actor for SectionRunnerActor { |
|
|
|
#[rtype(result = "()")] |
|
|
|
#[rtype(result = "()")] |
|
|
|
struct Quit; |
|
|
|
struct Quit; |
|
|
|
|
|
|
|
|
|
|
|
impl Handler<Quit> for SectionRunnerActor { |
|
|
|
impl Handler<Quit> for ZoneRunnerActor { |
|
|
|
type Result = (); |
|
|
|
type Result = (); |
|
|
|
|
|
|
|
|
|
|
|
fn handle(&mut self, _msg: Quit, ctx: &mut Self::Context) -> Self::Result { |
|
|
|
fn handle(&mut self, _msg: Quit, ctx: &mut Self::Context) -> Self::Result { |
|
|
@ -316,15 +296,15 @@ impl Handler<Quit> for SectionRunnerActor { |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Message, Debug, Clone)] |
|
|
|
#[derive(Message, Debug, Clone)] |
|
|
|
#[rtype(result = "()")] |
|
|
|
#[rtype(result = "()")] |
|
|
|
struct QueueRun(SectionRunHandle, SectionRef, Duration); |
|
|
|
struct QueueRun(ZoneRunHandle, ZoneRef, Duration); |
|
|
|
|
|
|
|
|
|
|
|
impl Handler<QueueRun> for SectionRunnerActor { |
|
|
|
impl Handler<QueueRun> for ZoneRunnerActor { |
|
|
|
type Result = (); |
|
|
|
type Result = (); |
|
|
|
|
|
|
|
|
|
|
|
fn handle(&mut self, msg: QueueRun, ctx: &mut Self::Context) -> Self::Result { |
|
|
|
fn handle(&mut self, msg: QueueRun, ctx: &mut Self::Context) -> Self::Result { |
|
|
|
let QueueRun(handle, section, duration) = msg; |
|
|
|
let QueueRun(handle, zone, duration) = msg; |
|
|
|
|
|
|
|
|
|
|
|
let run: Arc<SecRun> = SecRun::new(handle, section, duration).into(); |
|
|
|
let run: Arc<ZoneRun> = ZoneRun::new(handle, zone, duration).into(); |
|
|
|
self.state.run_queue.push_back(run); |
|
|
|
self.state.run_queue.push_back(run); |
|
|
|
self.inner.did_change = true; |
|
|
|
self.inner.did_change = true; |
|
|
|
|
|
|
|
|
|
|
@ -334,9 +314,9 @@ impl Handler<QueueRun> for SectionRunnerActor { |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Message, Debug, Clone)] |
|
|
|
#[derive(Message, Debug, Clone)] |
|
|
|
#[rtype(result = "bool")] |
|
|
|
#[rtype(result = "bool")] |
|
|
|
struct CancelRun(SectionRunHandle); |
|
|
|
struct CancelRun(ZoneRunHandle); |
|
|
|
|
|
|
|
|
|
|
|
impl Handler<CancelRun> for SectionRunnerActor { |
|
|
|
impl Handler<CancelRun> for ZoneRunnerActor { |
|
|
|
type Result = bool; |
|
|
|
type Result = bool; |
|
|
|
|
|
|
|
|
|
|
|
fn handle(&mut self, msg: CancelRun, ctx: &mut Self::Context) -> Self::Result { |
|
|
|
fn handle(&mut self, msg: CancelRun, ctx: &mut Self::Context) -> Self::Result { |
|
|
@ -358,25 +338,21 @@ impl Handler<CancelRun> for SectionRunnerActor { |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Message, Debug, Clone)] |
|
|
|
#[derive(Message, Debug, Clone)] |
|
|
|
#[rtype(result = "usize")] |
|
|
|
#[rtype(result = "usize")] |
|
|
|
struct CancelBySection(SectionId); |
|
|
|
struct CancelByZone(ZoneId); |
|
|
|
|
|
|
|
|
|
|
|
impl Handler<CancelBySection> for SectionRunnerActor { |
|
|
|
impl Handler<CancelByZone> for ZoneRunnerActor { |
|
|
|
type Result = usize; |
|
|
|
type Result = usize; |
|
|
|
|
|
|
|
|
|
|
|
fn handle(&mut self, msg: CancelBySection, ctx: &mut Self::Context) -> Self::Result { |
|
|
|
fn handle(&mut self, msg: CancelByZone, ctx: &mut Self::Context) -> Self::Result { |
|
|
|
let CancelBySection(section_id) = msg; |
|
|
|
let CancelByZone(zone_id) = msg; |
|
|
|
let mut count = 0_usize; |
|
|
|
let mut count = 0_usize; |
|
|
|
for run in self |
|
|
|
for run in self |
|
|
|
.state |
|
|
|
.state |
|
|
|
.run_queue |
|
|
|
.run_queue |
|
|
|
.iter_mut() |
|
|
|
.iter_mut() |
|
|
|
.filter(|run| run.section.id == section_id) |
|
|
|
.filter(|run| run.zone.id == zone_id) |
|
|
|
{ |
|
|
|
{ |
|
|
|
trace!( |
|
|
|
trace!(handle = run.handle.0, zone_id, "cancelling run by zone"); |
|
|
|
handle = run.handle.0, |
|
|
|
|
|
|
|
section_id, |
|
|
|
|
|
|
|
"cancelling run by section" |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
if self.inner.cancel_run(run) { |
|
|
|
if self.inner.cancel_run(run) { |
|
|
|
count += 1; |
|
|
|
count += 1; |
|
|
|
} |
|
|
|
} |
|
|
@ -390,11 +366,11 @@ impl Handler<CancelBySection> for SectionRunnerActor { |
|
|
|
#[rtype(result = "usize")] |
|
|
|
#[rtype(result = "usize")] |
|
|
|
struct CancelAll; |
|
|
|
struct CancelAll; |
|
|
|
|
|
|
|
|
|
|
|
impl Handler<CancelAll> for SectionRunnerActor { |
|
|
|
impl Handler<CancelAll> for ZoneRunnerActor { |
|
|
|
type Result = usize; |
|
|
|
type Result = usize; |
|
|
|
|
|
|
|
|
|
|
|
fn handle(&mut self, _msg: CancelAll, ctx: &mut Self::Context) -> Self::Result { |
|
|
|
fn handle(&mut self, _msg: CancelAll, ctx: &mut Self::Context) -> Self::Result { |
|
|
|
let mut old_runs = SecRunQueue::new(); |
|
|
|
let mut old_runs = ZoneRunQueue::new(); |
|
|
|
swap(&mut old_runs, &mut self.state.run_queue); |
|
|
|
swap(&mut old_runs, &mut self.state.run_queue); |
|
|
|
trace!(count = old_runs.len(), "cancelling all runs"); |
|
|
|
trace!(count = old_runs.len(), "cancelling all runs"); |
|
|
|
let mut count = 0usize; |
|
|
|
let mut count = 0usize; |
|
|
@ -412,7 +388,7 @@ impl Handler<CancelAll> for SectionRunnerActor { |
|
|
|
#[rtype(result = "()")] |
|
|
|
#[rtype(result = "()")] |
|
|
|
struct SetPaused(bool); |
|
|
|
struct SetPaused(bool); |
|
|
|
|
|
|
|
|
|
|
|
impl Handler<SetPaused> for SectionRunnerActor { |
|
|
|
impl Handler<SetPaused> for ZoneRunnerActor { |
|
|
|
type Result = (); |
|
|
|
type Result = (); |
|
|
|
|
|
|
|
|
|
|
|
fn handle(&mut self, msg: SetPaused, ctx: &mut Self::Context) -> Self::Result { |
|
|
|
fn handle(&mut self, msg: SetPaused, ctx: &mut Self::Context) -> Self::Result { |
|
|
@ -420,10 +396,10 @@ impl Handler<SetPaused> for SectionRunnerActor { |
|
|
|
if pause != self.state.paused { |
|
|
|
if pause != self.state.paused { |
|
|
|
if pause { |
|
|
|
if pause { |
|
|
|
self.state.paused = true; |
|
|
|
self.state.paused = true; |
|
|
|
self.inner.send_event(SectionEvent::RunnerPause); |
|
|
|
self.inner.send_event(ZoneEvent::RunnerPause); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
self.state.paused = false; |
|
|
|
self.state.paused = false; |
|
|
|
self.inner.send_event(SectionEvent::RunnerUnpause); |
|
|
|
self.inner.send_event(ZoneEvent::RunnerUnpause); |
|
|
|
} |
|
|
|
} |
|
|
|
self.inner.did_change = true; |
|
|
|
self.inner.did_change = true; |
|
|
|
ctx.notify(Process); |
|
|
|
ctx.notify(Process); |
|
|
@ -432,10 +408,10 @@ impl Handler<SetPaused> for SectionRunnerActor { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Message, Debug, Clone)] |
|
|
|
#[derive(Message, Debug, Clone)] |
|
|
|
#[rtype(result = "SectionEventRecv")] |
|
|
|
#[rtype(result = "ZoneEventRecv")] |
|
|
|
struct Subscribe; |
|
|
|
struct Subscribe; |
|
|
|
|
|
|
|
|
|
|
|
impl Handler<Subscribe> for SectionRunnerActor { |
|
|
|
impl Handler<Subscribe> for ZoneRunnerActor { |
|
|
|
type Result = MessageResult<Subscribe>; |
|
|
|
type Result = MessageResult<Subscribe>; |
|
|
|
|
|
|
|
|
|
|
|
fn handle(&mut self, _msg: Subscribe, _ctx: &mut Self::Context) -> Self::Result { |
|
|
|
fn handle(&mut self, _msg: Subscribe, _ctx: &mut Self::Context) -> Self::Result { |
|
|
@ -448,7 +424,7 @@ impl Handler<Subscribe> for SectionRunnerActor { |
|
|
|
#[rtype(result = "()")] |
|
|
|
#[rtype(result = "()")] |
|
|
|
struct Process; |
|
|
|
struct Process; |
|
|
|
|
|
|
|
|
|
|
|
impl Handler<Process> for SectionRunnerActor { |
|
|
|
impl Handler<Process> for ZoneRunnerActor { |
|
|
|
type Result = (); |
|
|
|
type Result = (); |
|
|
|
|
|
|
|
|
|
|
|
fn handle(&mut self, _msg: Process, ctx: &mut Self::Context) -> Self::Result { |
|
|
|
fn handle(&mut self, _msg: Process, ctx: &mut Self::Context) -> Self::Result { |
|
|
@ -456,14 +432,11 @@ impl Handler<Process> for SectionRunnerActor { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl SectionRunnerActor { |
|
|
|
impl ZoneRunnerActor { |
|
|
|
fn new( |
|
|
|
fn new(interface: Arc<dyn ZoneInterface>, state_send: watch::Sender<ZoneRunnerState>) -> Self { |
|
|
|
interface: Arc<dyn SectionInterface>, |
|
|
|
|
|
|
|
state_send: watch::Sender<SecRunnerState>, |
|
|
|
|
|
|
|
) -> Self { |
|
|
|
|
|
|
|
Self { |
|
|
|
Self { |
|
|
|
state: SecRunnerState::default(), |
|
|
|
state: ZoneRunnerState::default(), |
|
|
|
inner: SectionRunnerInner { |
|
|
|
inner: ZoneRunnerInner { |
|
|
|
interface, |
|
|
|
interface, |
|
|
|
event_send: None, |
|
|
|
event_send: None, |
|
|
|
state_send, |
|
|
|
state_send, |
|
|
@ -474,7 +447,7 @@ impl SectionRunnerActor { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn process_queue(&mut self, ctx: &mut actix::Context<Self>) { |
|
|
|
fn process_queue(&mut self, ctx: &mut actix::Context<Self>) { |
|
|
|
use SecRunState::*; |
|
|
|
use ZoneRunState::*; |
|
|
|
let state = &mut self.state; |
|
|
|
let state = &mut self.state; |
|
|
|
while let Some(current_run) = state.run_queue.front_mut() { |
|
|
|
while let Some(current_run) = state.run_queue.front_mut() { |
|
|
|
let run_finished = match (¤t_run.state, state.paused) { |
|
|
|
let run_finished = match (¤t_run.state, state.paused) { |
|
|
@ -528,23 +501,23 @@ impl SectionRunnerActor { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Error)] |
|
|
|
#[derive(Debug, Clone, Error)] |
|
|
|
#[error("error communicating with SectionRunner: {0}")] |
|
|
|
#[error("error communicating with ZoneRunner: {0}")] |
|
|
|
pub struct Error(#[from] actix::MailboxError); |
|
|
|
pub struct Error(#[from] actix::MailboxError); |
|
|
|
|
|
|
|
|
|
|
|
pub type Result<T, E = Error> = std::result::Result<T, E>; |
|
|
|
pub type Result<T, E = Error> = std::result::Result<T, E>; |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)] |
|
|
|
#[derive(Clone)] |
|
|
|
pub struct SectionRunner { |
|
|
|
pub struct ZoneRunner { |
|
|
|
state_recv: SecRunnerStateRecv, |
|
|
|
state_recv: ZoneRunnerStateRecv, |
|
|
|
addr: Addr<SectionRunnerActor>, |
|
|
|
addr: Addr<ZoneRunnerActor>, |
|
|
|
next_run_id: Arc<AtomicI32>, |
|
|
|
next_run_id: Arc<AtomicI32>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)] |
|
|
|
#[allow(dead_code)] |
|
|
|
impl SectionRunner { |
|
|
|
impl ZoneRunner { |
|
|
|
pub fn new(interface: Arc<dyn SectionInterface>) -> Self { |
|
|
|
pub fn new(interface: Arc<dyn ZoneInterface>) -> Self { |
|
|
|
let (state_send, state_recv) = watch::channel(SecRunnerState::default()); |
|
|
|
let (state_send, state_recv) = watch::channel(ZoneRunnerState::default()); |
|
|
|
let addr = SectionRunnerActor::new(interface, state_send).start(); |
|
|
|
let addr = ZoneRunnerActor::new(interface, state_send).start(); |
|
|
|
Self { |
|
|
|
Self { |
|
|
|
state_recv, |
|
|
|
state_recv, |
|
|
|
addr, |
|
|
|
addr, |
|
|
@ -556,49 +529,40 @@ impl SectionRunner { |
|
|
|
self.addr.send(Quit).map_err(From::from) |
|
|
|
self.addr.send(Quit).map_err(From::from) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn queue_run_inner( |
|
|
|
fn queue_run_inner(&mut self, zone: ZoneRef, duration: Duration) -> (QueueRun, ZoneRunHandle) { |
|
|
|
&mut self, |
|
|
|
|
|
|
|
section: SectionRef, |
|
|
|
|
|
|
|
duration: Duration, |
|
|
|
|
|
|
|
) -> (QueueRun, SectionRunHandle) { |
|
|
|
|
|
|
|
let run_id = self.next_run_id.fetch_add(1, Ordering::SeqCst); |
|
|
|
let run_id = self.next_run_id.fetch_add(1, Ordering::SeqCst); |
|
|
|
let handle = SectionRunHandle(run_id); |
|
|
|
let handle = ZoneRunHandle(run_id); |
|
|
|
(QueueRun(handle.clone(), section, duration), handle) |
|
|
|
(QueueRun(handle.clone(), zone, duration), handle) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn do_queue_run(&mut self, section: SectionRef, duration: Duration) -> SectionRunHandle { |
|
|
|
pub fn do_queue_run(&mut self, zone: ZoneRef, duration: Duration) -> ZoneRunHandle { |
|
|
|
let (queue_run, handle) = self.queue_run_inner(section, duration); |
|
|
|
let (queue_run, handle) = self.queue_run_inner(zone, duration); |
|
|
|
self.addr.do_send(queue_run); |
|
|
|
self.addr.do_send(queue_run); |
|
|
|
handle |
|
|
|
handle |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn queue_run( |
|
|
|
pub fn queue_run( |
|
|
|
&mut self, |
|
|
|
&mut self, |
|
|
|
section: SectionRef, |
|
|
|
zone: ZoneRef, |
|
|
|
duration: Duration, |
|
|
|
duration: Duration, |
|
|
|
) -> impl Future<Output = Result<SectionRunHandle>> { |
|
|
|
) -> impl Future<Output = Result<ZoneRunHandle>> { |
|
|
|
let (queue_run, handle) = self.queue_run_inner(section, duration); |
|
|
|
let (queue_run, handle) = self.queue_run_inner(zone, duration); |
|
|
|
self.addr |
|
|
|
self.addr |
|
|
|
.send(queue_run) |
|
|
|
.send(queue_run) |
|
|
|
.map_err(From::from) |
|
|
|
.map_err(From::from) |
|
|
|
.map_ok(move |_| handle) |
|
|
|
.map_ok(move |_| handle) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn do_cancel_run(&mut self, handle: SectionRunHandle) { |
|
|
|
pub fn do_cancel_run(&mut self, handle: ZoneRunHandle) { |
|
|
|
self.addr.do_send(CancelRun(handle)) |
|
|
|
self.addr.do_send(CancelRun(handle)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn cancel_run(&mut self, handle: SectionRunHandle) -> impl Future<Output = Result<bool>> { |
|
|
|
pub fn cancel_run(&mut self, handle: ZoneRunHandle) -> impl Future<Output = Result<bool>> { |
|
|
|
self.addr.send(CancelRun(handle)).map_err(From::from) |
|
|
|
self.addr.send(CancelRun(handle)).map_err(From::from) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn cancel_by_section( |
|
|
|
pub fn cancel_by_zone(&mut self, zone_id: ZoneId) -> impl Future<Output = Result<usize>> { |
|
|
|
&mut self, |
|
|
|
self.addr.send(CancelByZone(zone_id)).map_err(From::from) |
|
|
|
section_id: SectionId, |
|
|
|
|
|
|
|
) -> impl Future<Output = Result<usize>> { |
|
|
|
|
|
|
|
self.addr |
|
|
|
|
|
|
|
.send(CancelBySection(section_id)) |
|
|
|
|
|
|
|
.map_err(From::from) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn cancel_all(&mut self) -> impl Future<Output = Result<usize>> { |
|
|
|
pub fn cancel_all(&mut self) -> impl Future<Output = Result<usize>> { |
|
|
@ -613,11 +577,11 @@ impl SectionRunner { |
|
|
|
self.addr.send(SetPaused(false)).map_err(From::from) |
|
|
|
self.addr.send(SetPaused(false)).map_err(From::from) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn subscribe(&mut self) -> impl Future<Output = Result<SectionEventRecv>> { |
|
|
|
pub fn subscribe(&mut self) -> impl Future<Output = Result<ZoneEventRecv>> { |
|
|
|
self.addr.send(Subscribe).map_err(From::from) |
|
|
|
self.addr.send(Subscribe).map_err(From::from) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn get_state_recv(&self) -> SecRunnerStateRecv { |
|
|
|
pub fn get_state_recv(&self) -> ZoneRunnerStateRecv { |
|
|
|
self.state_recv.clone() |
|
|
|
self.state_recv.clone() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -627,8 +591,8 @@ mod test { |
|
|
|
use super::*; |
|
|
|
use super::*; |
|
|
|
use crate::trace_listeners::{EventListener, Filters}; |
|
|
|
use crate::trace_listeners::{EventListener, Filters}; |
|
|
|
use sprinklers_core::{ |
|
|
|
use sprinklers_core::{ |
|
|
|
model::{Section, Sections}, |
|
|
|
model::{Zone, Zones}, |
|
|
|
section_interface::MockSectionInterface, |
|
|
|
zone_interface::MockZoneInterface, |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
use assert_matches::assert_matches; |
|
|
|
use assert_matches::assert_matches; |
|
|
@ -639,43 +603,43 @@ mod test { |
|
|
|
async fn test_quit() { |
|
|
|
async fn test_quit() { |
|
|
|
let quit_msg = EventListener::new( |
|
|
|
let quit_msg = EventListener::new( |
|
|
|
Filters::new() |
|
|
|
Filters::new() |
|
|
|
.target("sprinklers_actors::section_runner") |
|
|
|
.target("sprinklers_actors::zone_runner") |
|
|
|
.message("section_runner stopped"), |
|
|
|
.message("zone_runner stopped"), |
|
|
|
); |
|
|
|
); |
|
|
|
let subscriber = tracing_subscriber::registry().with(quit_msg.clone()); |
|
|
|
let subscriber = tracing_subscriber::registry().with(quit_msg.clone()); |
|
|
|
let _sub = tracing::subscriber::set_default(subscriber); |
|
|
|
let _sub = tracing::subscriber::set_default(subscriber); |
|
|
|
|
|
|
|
|
|
|
|
let interface = MockSectionInterface::new(6); |
|
|
|
let interface = MockZoneInterface::new(6); |
|
|
|
let mut runner = SectionRunner::new(Arc::new(interface)); |
|
|
|
let mut runner = ZoneRunner::new(Arc::new(interface)); |
|
|
|
tokio::task::yield_now().await; |
|
|
|
tokio::task::yield_now().await; |
|
|
|
runner.quit().await.unwrap(); |
|
|
|
runner.quit().await.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(quit_msg.get_count(), 1); |
|
|
|
assert_eq!(quit_msg.get_count(), 1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn make_sections_and_interface() -> (Sections, Arc<MockSectionInterface>) { |
|
|
|
fn make_zones_and_interface() -> (Zones, Arc<MockZoneInterface>) { |
|
|
|
let interface = Arc::new(MockSectionInterface::new(2)); |
|
|
|
let interface = Arc::new(MockZoneInterface::new(2)); |
|
|
|
let sections: Sections = ordmap![ |
|
|
|
let zones: Zones = ordmap![ |
|
|
|
1 => Section { |
|
|
|
1 => Zone { |
|
|
|
id: 1, |
|
|
|
id: 1, |
|
|
|
name: "Section 1".into(), |
|
|
|
name: "Zone 1".into(), |
|
|
|
interface_id: 0, |
|
|
|
interface_id: 0, |
|
|
|
}.into(), |
|
|
|
}.into(), |
|
|
|
2 => Section { |
|
|
|
2 => Zone { |
|
|
|
id: 2, |
|
|
|
id: 2, |
|
|
|
name: "Section 2".into(), |
|
|
|
name: "Zone 2".into(), |
|
|
|
interface_id: 1, |
|
|
|
interface_id: 1, |
|
|
|
}.into() |
|
|
|
}.into() |
|
|
|
]; |
|
|
|
]; |
|
|
|
(sections, interface) |
|
|
|
(zones, interface) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn assert_section_states(interface: &MockSectionInterface, states: &[bool]) { |
|
|
|
fn assert_zone_states(interface: &MockZoneInterface, states: &[bool]) { |
|
|
|
for (id, state) in states.iter().enumerate() { |
|
|
|
for (id, state) in states.iter().enumerate() { |
|
|
|
assert_eq!( |
|
|
|
assert_eq!( |
|
|
|
interface.get_section_state(id as u32), |
|
|
|
interface.get_zone_state(id as u32), |
|
|
|
*state, |
|
|
|
*state, |
|
|
|
"section interface id {} did not match", |
|
|
|
"zone interface id {} did not match", |
|
|
|
id |
|
|
|
id |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
@ -691,236 +655,236 @@ mod test { |
|
|
|
|
|
|
|
|
|
|
|
#[actix_rt::test] |
|
|
|
#[actix_rt::test] |
|
|
|
async fn test_queue() { |
|
|
|
async fn test_queue() { |
|
|
|
let (sections, interface) = make_sections_and_interface(); |
|
|
|
let (zones, interface) = make_zones_and_interface(); |
|
|
|
let mut runner = SectionRunner::new(interface.clone()); |
|
|
|
let mut runner = ZoneRunner::new(interface.clone()); |
|
|
|
|
|
|
|
|
|
|
|
assert_section_states(&interface, &[false, false]); |
|
|
|
assert_zone_states(&interface, &[false, false]); |
|
|
|
|
|
|
|
|
|
|
|
// Queue single section, make sure it runs
|
|
|
|
// Queue single zone, make sure it runs
|
|
|
|
runner |
|
|
|
runner |
|
|
|
.queue_run(sections[&1].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&1].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
tokio::task::yield_now().await; |
|
|
|
tokio::task::yield_now().await; |
|
|
|
|
|
|
|
|
|
|
|
assert_section_states(&interface, &[true, false]); |
|
|
|
assert_zone_states(&interface, &[true, false]); |
|
|
|
|
|
|
|
|
|
|
|
advance(Duration::from_secs(11)).await; |
|
|
|
advance(Duration::from_secs(11)).await; |
|
|
|
|
|
|
|
|
|
|
|
assert_section_states(&interface, &[false, false]); |
|
|
|
assert_zone_states(&interface, &[false, false]); |
|
|
|
|
|
|
|
|
|
|
|
// Queue two sections, make sure they run one at a time
|
|
|
|
// Queue two zones, make sure they run one at a time
|
|
|
|
runner |
|
|
|
runner |
|
|
|
.queue_run(sections[&2].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&2].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
runner |
|
|
|
runner |
|
|
|
.queue_run(sections[&1].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&1].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
tokio::task::yield_now().await; |
|
|
|
tokio::task::yield_now().await; |
|
|
|
|
|
|
|
|
|
|
|
assert_section_states(&interface, &[false, true]); |
|
|
|
assert_zone_states(&interface, &[false, true]); |
|
|
|
|
|
|
|
|
|
|
|
advance(Duration::from_secs(11)).await; |
|
|
|
advance(Duration::from_secs(11)).await; |
|
|
|
|
|
|
|
|
|
|
|
assert_section_states(&interface, &[true, false]); |
|
|
|
assert_zone_states(&interface, &[true, false]); |
|
|
|
|
|
|
|
|
|
|
|
advance(Duration::from_secs(10)).await; |
|
|
|
advance(Duration::from_secs(10)).await; |
|
|
|
|
|
|
|
|
|
|
|
assert_section_states(&interface, &[false, false]); |
|
|
|
assert_zone_states(&interface, &[false, false]); |
|
|
|
|
|
|
|
|
|
|
|
runner.quit().await.unwrap(); |
|
|
|
runner.quit().await.unwrap(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[actix_rt::test] |
|
|
|
#[actix_rt::test] |
|
|
|
async fn test_cancel_run() { |
|
|
|
async fn test_cancel_run() { |
|
|
|
let (sections, interface) = make_sections_and_interface(); |
|
|
|
let (zones, interface) = make_zones_and_interface(); |
|
|
|
let mut runner = SectionRunner::new(interface.clone()); |
|
|
|
let mut runner = ZoneRunner::new(interface.clone()); |
|
|
|
|
|
|
|
|
|
|
|
let run1 = runner |
|
|
|
let run1 = runner |
|
|
|
.queue_run(sections[&2].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&2].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
let _run2 = runner |
|
|
|
let _run2 = runner |
|
|
|
.queue_run(sections[&1].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&1].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
let run3 = runner |
|
|
|
let run3 = runner |
|
|
|
.queue_run(sections[&2].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&2].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
tokio::task::yield_now().await; |
|
|
|
tokio::task::yield_now().await; |
|
|
|
|
|
|
|
|
|
|
|
assert_section_states(&interface, &[false, true]); |
|
|
|
assert_zone_states(&interface, &[false, true]); |
|
|
|
|
|
|
|
|
|
|
|
runner.cancel_run(run1).await.unwrap(); |
|
|
|
runner.cancel_run(run1).await.unwrap(); |
|
|
|
tokio::task::yield_now().await; |
|
|
|
tokio::task::yield_now().await; |
|
|
|
|
|
|
|
|
|
|
|
assert_section_states(&interface, &[true, false]); |
|
|
|
assert_zone_states(&interface, &[true, false]); |
|
|
|
|
|
|
|
|
|
|
|
runner.cancel_run(run3).await.unwrap(); |
|
|
|
runner.cancel_run(run3).await.unwrap(); |
|
|
|
advance(Duration::from_secs(11)).await; |
|
|
|
advance(Duration::from_secs(11)).await; |
|
|
|
|
|
|
|
|
|
|
|
assert_section_states(&interface, &[false, false]); |
|
|
|
assert_zone_states(&interface, &[false, false]); |
|
|
|
|
|
|
|
|
|
|
|
runner.quit().await.unwrap(); |
|
|
|
runner.quit().await.unwrap(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[actix_rt::test] |
|
|
|
#[actix_rt::test] |
|
|
|
async fn test_cancel_all() { |
|
|
|
async fn test_cancel_all() { |
|
|
|
let (sections, interface) = make_sections_and_interface(); |
|
|
|
let (zones, interface) = make_zones_and_interface(); |
|
|
|
let mut runner = SectionRunner::new(interface.clone()); |
|
|
|
let mut runner = ZoneRunner::new(interface.clone()); |
|
|
|
|
|
|
|
|
|
|
|
runner |
|
|
|
runner |
|
|
|
.queue_run(sections[&2].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&2].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
runner |
|
|
|
runner |
|
|
|
.queue_run(sections[&1].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&1].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
runner |
|
|
|
runner |
|
|
|
.queue_run(sections[&2].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&2].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
tokio::task::yield_now().await; |
|
|
|
tokio::task::yield_now().await; |
|
|
|
assert_section_states(&interface, &[false, true]); |
|
|
|
assert_zone_states(&interface, &[false, true]); |
|
|
|
|
|
|
|
|
|
|
|
runner.cancel_all().await.unwrap(); |
|
|
|
runner.cancel_all().await.unwrap(); |
|
|
|
tokio::task::yield_now().await; |
|
|
|
tokio::task::yield_now().await; |
|
|
|
assert_section_states(&interface, &[false, false]); |
|
|
|
assert_zone_states(&interface, &[false, false]); |
|
|
|
|
|
|
|
|
|
|
|
runner.cancel_all().await.unwrap(); |
|
|
|
runner.cancel_all().await.unwrap(); |
|
|
|
tokio::task::yield_now().await; |
|
|
|
tokio::task::yield_now().await; |
|
|
|
assert_section_states(&interface, &[false, false]); |
|
|
|
assert_zone_states(&interface, &[false, false]); |
|
|
|
|
|
|
|
|
|
|
|
runner.quit().await.unwrap(); |
|
|
|
runner.quit().await.unwrap(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[actix_rt::test] |
|
|
|
#[actix_rt::test] |
|
|
|
async fn test_pause() { |
|
|
|
async fn test_pause() { |
|
|
|
let (sections, interface) = make_sections_and_interface(); |
|
|
|
let (zones, interface) = make_zones_and_interface(); |
|
|
|
let mut runner = SectionRunner::new(interface.clone()); |
|
|
|
let mut runner = ZoneRunner::new(interface.clone()); |
|
|
|
|
|
|
|
|
|
|
|
let _run1 = runner |
|
|
|
let _run1 = runner |
|
|
|
.queue_run(sections[&2].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&2].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
let run2 = runner |
|
|
|
let run2 = runner |
|
|
|
.queue_run(sections[&1].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&1].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
let _run3 = runner |
|
|
|
let _run3 = runner |
|
|
|
.queue_run(sections[&2].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&2].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
tokio::task::yield_now().await; |
|
|
|
tokio::task::yield_now().await; |
|
|
|
assert_section_states(&interface, &[false, true]); |
|
|
|
assert_zone_states(&interface, &[false, true]); |
|
|
|
|
|
|
|
|
|
|
|
runner.pause().await.unwrap(); |
|
|
|
runner.pause().await.unwrap(); |
|
|
|
tokio::task::yield_now().await; |
|
|
|
tokio::task::yield_now().await; |
|
|
|
assert_section_states(&interface, &[false, false]); |
|
|
|
assert_zone_states(&interface, &[false, false]); |
|
|
|
|
|
|
|
|
|
|
|
advance(Duration::from_secs(10)).await; |
|
|
|
advance(Duration::from_secs(10)).await; |
|
|
|
assert_section_states(&interface, &[false, false]); |
|
|
|
assert_zone_states(&interface, &[false, false]); |
|
|
|
|
|
|
|
|
|
|
|
runner.unpause().await.unwrap(); |
|
|
|
runner.unpause().await.unwrap(); |
|
|
|
tokio::task::yield_now().await; |
|
|
|
tokio::task::yield_now().await; |
|
|
|
assert_section_states(&interface, &[false, true]); |
|
|
|
assert_zone_states(&interface, &[false, true]); |
|
|
|
|
|
|
|
|
|
|
|
advance(Duration::from_secs(8)).await; |
|
|
|
advance(Duration::from_secs(8)).await; |
|
|
|
assert_section_states(&interface, &[false, true]); |
|
|
|
assert_zone_states(&interface, &[false, true]); |
|
|
|
|
|
|
|
|
|
|
|
advance(Duration::from_secs(2)).await; |
|
|
|
advance(Duration::from_secs(2)).await; |
|
|
|
assert_section_states(&interface, &[true, false]); |
|
|
|
assert_zone_states(&interface, &[true, false]); |
|
|
|
|
|
|
|
|
|
|
|
runner.pause().await.unwrap(); |
|
|
|
runner.pause().await.unwrap(); |
|
|
|
tokio::task::yield_now().await; |
|
|
|
tokio::task::yield_now().await; |
|
|
|
assert_section_states(&interface, &[false, false]); |
|
|
|
assert_zone_states(&interface, &[false, false]); |
|
|
|
|
|
|
|
|
|
|
|
// cancel paused run
|
|
|
|
// cancel paused run
|
|
|
|
runner.cancel_run(run2).await.unwrap(); |
|
|
|
runner.cancel_run(run2).await.unwrap(); |
|
|
|
tokio::task::yield_now().await; |
|
|
|
tokio::task::yield_now().await; |
|
|
|
assert_section_states(&interface, &[false, false]); |
|
|
|
assert_zone_states(&interface, &[false, false]); |
|
|
|
|
|
|
|
|
|
|
|
runner.unpause().await.unwrap(); |
|
|
|
runner.unpause().await.unwrap(); |
|
|
|
tokio::task::yield_now().await; |
|
|
|
tokio::task::yield_now().await; |
|
|
|
assert_section_states(&interface, &[false, true]); |
|
|
|
assert_zone_states(&interface, &[false, true]); |
|
|
|
|
|
|
|
|
|
|
|
advance(Duration::from_secs(11)).await; |
|
|
|
advance(Duration::from_secs(11)).await; |
|
|
|
assert_section_states(&interface, &[false, false]); |
|
|
|
assert_zone_states(&interface, &[false, false]); |
|
|
|
|
|
|
|
|
|
|
|
runner.quit().await.unwrap(); |
|
|
|
runner.quit().await.unwrap(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[actix_rt::test] |
|
|
|
#[actix_rt::test] |
|
|
|
async fn test_event() { |
|
|
|
async fn test_event() { |
|
|
|
let (sections, interface) = make_sections_and_interface(); |
|
|
|
let (zones, interface) = make_zones_and_interface(); |
|
|
|
let mut runner = SectionRunner::new(interface.clone()); |
|
|
|
let mut runner = ZoneRunner::new(interface.clone()); |
|
|
|
|
|
|
|
|
|
|
|
let mut event_recv = runner.subscribe().await.unwrap(); |
|
|
|
let mut event_recv = runner.subscribe().await.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
let run1 = runner |
|
|
|
let run1 = runner |
|
|
|
.queue_run(sections[&2].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&2].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
let run2 = runner |
|
|
|
let run2 = runner |
|
|
|
.queue_run(sections[&1].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&1].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
let run3 = runner |
|
|
|
let run3 = runner |
|
|
|
.queue_run(sections[&2].clone(), Duration::from_secs(10)) |
|
|
|
.queue_run(zones[&2].clone(), Duration::from_secs(10)) |
|
|
|
.await |
|
|
|
.await |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
assert_matches!( |
|
|
|
assert_matches!( |
|
|
|
event_recv.recv().await, |
|
|
|
event_recv.recv().await, |
|
|
|
Ok(SectionEvent::RunStart(handle, _)) |
|
|
|
Ok(ZoneEvent::RunStart(handle, _)) |
|
|
|
if handle == run1 |
|
|
|
if handle == run1 |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
runner.pause().await.unwrap(); |
|
|
|
runner.pause().await.unwrap(); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(SectionEvent::RunnerPause)); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(ZoneEvent::RunnerPause)); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(SectionEvent::RunPause(handle, _)) if handle == run1); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(ZoneEvent::RunPause(handle, _)) if handle == run1); |
|
|
|
|
|
|
|
|
|
|
|
runner.unpause().await.unwrap(); |
|
|
|
runner.unpause().await.unwrap(); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(SectionEvent::RunnerUnpause)); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(ZoneEvent::RunnerUnpause)); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(SectionEvent::RunUnpause(handle, _)) if handle == run1); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(ZoneEvent::RunUnpause(handle, _)) if handle == run1); |
|
|
|
|
|
|
|
|
|
|
|
advance(Duration::from_secs(11)).await; |
|
|
|
advance(Duration::from_secs(11)).await; |
|
|
|
assert_matches!(event_recv.recv().await, Ok(SectionEvent::RunFinish(handle, _)) if handle == run1); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(ZoneEvent::RunFinish(handle, _)) if handle == run1); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(SectionEvent::RunStart(handle, _)) if handle == run2); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(ZoneEvent::RunStart(handle, _)) if handle == run2); |
|
|
|
|
|
|
|
|
|
|
|
runner.pause().await.unwrap(); |
|
|
|
runner.pause().await.unwrap(); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(SectionEvent::RunnerPause)); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(ZoneEvent::RunnerPause)); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(SectionEvent::RunPause(handle, _)) if handle == run2); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(ZoneEvent::RunPause(handle, _)) if handle == run2); |
|
|
|
|
|
|
|
|
|
|
|
// cancel paused run
|
|
|
|
// cancel paused run
|
|
|
|
runner.cancel_run(run2.clone()).await.unwrap(); |
|
|
|
runner.cancel_run(run2.clone()).await.unwrap(); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(SectionEvent::RunCancel(handle, _)) if handle == run2); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(ZoneEvent::RunCancel(handle, _)) if handle == run2); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(SectionEvent::RunPause(handle, _)) if handle == run3); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(ZoneEvent::RunPause(handle, _)) if handle == run3); |
|
|
|
|
|
|
|
|
|
|
|
runner.unpause().await.unwrap(); |
|
|
|
runner.unpause().await.unwrap(); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(SectionEvent::RunnerUnpause)); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(ZoneEvent::RunnerUnpause)); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(SectionEvent::RunUnpause(handle, _)) if handle == run3); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(ZoneEvent::RunUnpause(handle, _)) if handle == run3); |
|
|
|
|
|
|
|
|
|
|
|
advance(Duration::from_secs(11)).await; |
|
|
|
advance(Duration::from_secs(11)).await; |
|
|
|
assert_matches!(event_recv.recv().await, Ok(SectionEvent::RunFinish(handle, _)) if handle == run3); |
|
|
|
assert_matches!(event_recv.recv().await, Ok(ZoneEvent::RunFinish(handle, _)) if handle == run3); |
|
|
|
|
|
|
|
|
|
|
|
runner.quit().await.unwrap(); |
|
|
|
runner.quit().await.unwrap(); |
|
|
|
} |
|
|
|
} |