Use actix for update_listener
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
709f8c2760
commit
4eb03f2e22
@ -3,37 +3,38 @@ use crate::{
|
|||||||
program_runner::{ProgramEvent, ProgramEventRecv},
|
program_runner::{ProgramEvent, ProgramEventRecv},
|
||||||
section_runner::{SecRunnerState, SecRunnerStateRecv, SectionEvent, SectionEventRecv},
|
section_runner::{SecRunnerState, SecRunnerStateRecv, SectionEvent, SectionEventRecv},
|
||||||
};
|
};
|
||||||
use tokio::{
|
use actix::{fut::wrap_future, Actor, ActorContext, Addr, AsyncContext, Handler, StreamHandler};
|
||||||
select,
|
use tokio::sync::broadcast;
|
||||||
sync::{broadcast, oneshot},
|
use tracing::{trace, warn};
|
||||||
task::JoinHandle,
|
|
||||||
};
|
|
||||||
use tracing::{trace_span, warn};
|
|
||||||
use tracing_futures::Instrument;
|
|
||||||
|
|
||||||
struct UpdateListenerTask {
|
struct UpdateListenerActor {
|
||||||
section_events: SectionEventRecv,
|
|
||||||
program_events: ProgramEventRecv,
|
|
||||||
sec_runner_state: SecRunnerStateRecv,
|
|
||||||
mqtt_interface: MqttInterface,
|
mqtt_interface: MqttInterface,
|
||||||
running: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdateListenerTask {
|
impl Actor for UpdateListenerActor {
|
||||||
async fn handle_section_event(
|
type Context = actix::Context<Self>;
|
||||||
|
|
||||||
|
fn started(&mut self, _ctx: &mut Self::Context) {
|
||||||
|
trace!("starting UpdateListener");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stopped(&mut self, _ctx: &mut Self::Context) {
|
||||||
|
trace!("stopped UpdateListener")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StreamHandler<Result<SectionEvent, broadcast::RecvError>> for UpdateListenerActor {
|
||||||
|
fn handle(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: Result<SectionEvent, broadcast::RecvError>,
|
event: Result<SectionEvent, broadcast::RecvError>,
|
||||||
) -> eyre::Result<()> {
|
ctx: &mut Self::Context,
|
||||||
|
) {
|
||||||
let event = match event {
|
let event = match event {
|
||||||
Ok(ev) => ev,
|
Ok(ev) => ev,
|
||||||
Err(broadcast::RecvError::Closed) => {
|
Err(broadcast::RecvError::Closed) => unreachable!(),
|
||||||
warn!("section events channel closed");
|
|
||||||
self.running = false;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Err(broadcast::RecvError::Lagged(n)) => {
|
Err(broadcast::RecvError::Lagged(n)) => {
|
||||||
warn!("section events lagged by {}", n);
|
warn!("section events lagged by {}", n);
|
||||||
return Ok(());
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some((sec_id, state)) = match event {
|
if let Some((sec_id, state)) = match event {
|
||||||
@ -45,94 +46,116 @@ impl UpdateListenerTask {
|
|||||||
| SectionEvent::RunCancel(_, sec) => Some((sec.id, false)),
|
| SectionEvent::RunCancel(_, sec) => Some((sec.id, false)),
|
||||||
SectionEvent::RunnerPause | SectionEvent::RunnerUnpause => None,
|
SectionEvent::RunnerPause | SectionEvent::RunnerUnpause => None,
|
||||||
} {
|
} {
|
||||||
self.mqtt_interface
|
let mut mqtt_interface = self.mqtt_interface.clone();
|
||||||
.publish_section_state(sec_id, state)
|
let fut = async move {
|
||||||
.await?;
|
if let Err(err) = mqtt_interface.publish_section_state(sec_id, state).await {
|
||||||
|
warn!("could not publish section state: {}", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ctx.spawn(wrap_future(fut));
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_program_event(
|
impl StreamHandler<Result<ProgramEvent, broadcast::RecvError>> for UpdateListenerActor {
|
||||||
|
fn handle(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: Result<ProgramEvent, broadcast::RecvError>,
|
event: Result<ProgramEvent, broadcast::RecvError>,
|
||||||
) -> eyre::Result<()> {
|
ctx: &mut Self::Context,
|
||||||
|
) {
|
||||||
let event = match event {
|
let event = match event {
|
||||||
Ok(ev) => ev,
|
Ok(ev) => ev,
|
||||||
Err(broadcast::RecvError::Closed) => {
|
Err(broadcast::RecvError::Closed) => unreachable!(),
|
||||||
warn!("program events channel closed");
|
|
||||||
self.running = false;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Err(broadcast::RecvError::Lagged(n)) => {
|
Err(broadcast::RecvError::Lagged(n)) => {
|
||||||
warn!("program events lagged by {}", n);
|
warn!("program events lagged by {}", n);
|
||||||
return Ok(());
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let (program_id, running) = match event {
|
let (program_id, running) = match event {
|
||||||
ProgramEvent::RunStart(prog) => (prog.id, true),
|
ProgramEvent::RunStart(prog) => (prog.id, true),
|
||||||
ProgramEvent::RunFinish(prog) | ProgramEvent::RunCancel(prog) => (prog.id, false),
|
ProgramEvent::RunFinish(prog) | ProgramEvent::RunCancel(prog) => (prog.id, false),
|
||||||
};
|
};
|
||||||
self.mqtt_interface
|
let mut mqtt_interface = self.mqtt_interface.clone();
|
||||||
.publish_program_running(program_id, running)
|
let fut = async move {
|
||||||
.await?;
|
if let Err(err) = mqtt_interface
|
||||||
Ok(())
|
.publish_program_running(program_id, running)
|
||||||
}
|
.await
|
||||||
|
{
|
||||||
async fn handle_sec_runner_state(&mut self, state: Option<SecRunnerState>) -> eyre::Result<()> {
|
warn!("could not publish program running: {}", err);
|
||||||
let state = match state {
|
|
||||||
Some(state) => state,
|
|
||||||
None => {
|
|
||||||
warn!("section runner events channel closed");
|
|
||||||
self.running = false;
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.mqtt_interface.publish_section_runner(&state).await?;
|
ctx.spawn(wrap_future(fut));
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn run_impl(&mut self) -> eyre::Result<()> {
|
impl StreamHandler<SecRunnerState> for UpdateListenerActor {
|
||||||
while self.running {
|
fn handle(&mut self, state: SecRunnerState, ctx: &mut Self::Context) {
|
||||||
select! {
|
let mut mqtt_interface = self.mqtt_interface.clone();
|
||||||
section_event = self.section_events.recv() => {
|
let fut = async move {
|
||||||
self.handle_section_event(section_event).await?
|
if let Err(err) = mqtt_interface.publish_section_runner(&state).await {
|
||||||
}
|
warn!("could not publish section runner: {}", err);
|
||||||
program_event = self.program_events.recv() => {
|
|
||||||
self.handle_program_event(program_event).await?
|
|
||||||
}
|
|
||||||
sec_runner_state = self.sec_runner_state.recv() => {
|
|
||||||
self.handle_sec_runner_state(sec_runner_state).await?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run_or_quit(mut self, mut quit_rx: oneshot::Receiver<()>) -> eyre::Result<()> {
|
|
||||||
select! {
|
|
||||||
_ = &mut quit_rx => {
|
|
||||||
self.running = false;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
res = self.run_impl() => {
|
};
|
||||||
res
|
ctx.spawn(wrap_future(fut));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn run(self, quit_rx: oneshot::Receiver<()>) {
|
#[derive(actix::Message)]
|
||||||
let span = trace_span!("update_listener task");
|
#[rtype(result = "()")]
|
||||||
|
struct Quit;
|
||||||
|
|
||||||
self.run_or_quit(quit_rx)
|
impl Handler<Quit> for UpdateListenerActor {
|
||||||
.instrument(span)
|
type Result = ();
|
||||||
.await
|
|
||||||
.expect("error in UpdateListenerTask");
|
fn handle(&mut self, _msg: Quit, ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
ctx.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Listenable<A>: Send
|
||||||
|
where
|
||||||
|
A: Actor,
|
||||||
|
{
|
||||||
|
fn listen(self, ctx: &mut A::Context);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(actix::Message)]
|
||||||
|
#[rtype(result = "()")]
|
||||||
|
struct Listen<L>(L)
|
||||||
|
where
|
||||||
|
L: Listenable<UpdateListenerActor>;
|
||||||
|
|
||||||
|
impl<L> Handler<Listen<L>> for UpdateListenerActor
|
||||||
|
where
|
||||||
|
L: Listenable<Self>,
|
||||||
|
{
|
||||||
|
type Result = ();
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: Listen<L>, ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
msg.0.listen(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Listenable<UpdateListenerActor> for SectionEventRecv {
|
||||||
|
fn listen(self, ctx: &mut <UpdateListenerActor as Actor>::Context) {
|
||||||
|
ctx.add_stream(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Listenable<UpdateListenerActor> for ProgramEventRecv {
|
||||||
|
fn listen(self, ctx: &mut <UpdateListenerActor as Actor>::Context) {
|
||||||
|
ctx.add_stream(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Listenable<UpdateListenerActor> for SecRunnerStateRecv {
|
||||||
|
fn listen(self, ctx: &mut <UpdateListenerActor as Actor>::Context) {
|
||||||
|
ctx.add_stream(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UpdateListener {
|
pub struct UpdateListener {
|
||||||
quit_tx: oneshot::Sender<()>,
|
addr: Addr<UpdateListenerActor>,
|
||||||
join_handle: JoinHandle<()>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdateListener {
|
impl UpdateListener {
|
||||||
@ -142,24 +165,22 @@ impl UpdateListener {
|
|||||||
sec_runner_state: SecRunnerStateRecv,
|
sec_runner_state: SecRunnerStateRecv,
|
||||||
mqtt_interface: MqttInterface,
|
mqtt_interface: MqttInterface,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let task = UpdateListenerTask {
|
let addr = UpdateListenerActor { mqtt_interface }.start();
|
||||||
section_events,
|
let mut l = Self { addr };
|
||||||
program_events,
|
l.listen(section_events);
|
||||||
sec_runner_state,
|
l.listen(program_events);
|
||||||
mqtt_interface,
|
l.listen(sec_runner_state);
|
||||||
running: true,
|
l
|
||||||
};
|
}
|
||||||
let (quit_tx, quit_rx) = oneshot::channel();
|
|
||||||
let join_handle = tokio::spawn(task.run(quit_rx));
|
fn listen<L: 'static>(&mut self, listener: L)
|
||||||
Self {
|
where
|
||||||
quit_tx,
|
L: Listenable<UpdateListenerActor>,
|
||||||
join_handle,
|
{
|
||||||
}
|
self.addr.do_send(Listen(listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn quit(self) -> eyre::Result<()> {
|
pub async fn quit(self) -> eyre::Result<()> {
|
||||||
let _ = self.quit_tx.send(());
|
Ok(self.addr.send(Quit).await?)
|
||||||
self.join_handle.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user