Implement cancelling by section from MQTT
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
5f3f417040
commit
b72d89e1dd
@ -1,11 +1,12 @@
|
|||||||
use crate::{model::Sections, section_runner::SectionRunner};
|
use crate::{model::Sections, section_runner::SectionRunner};
|
||||||
|
|
||||||
|
use futures_util::ready;
|
||||||
use futures_util::FutureExt;
|
use futures_util::FutureExt;
|
||||||
use num_derive::FromPrimitive;
|
use num_derive::FromPrimitive;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{fmt, future::Future, pin::Pin};
|
use std::{fmt, future::Future, pin::Pin, task::Poll};
|
||||||
|
|
||||||
mod run_section;
|
mod sections;
|
||||||
|
|
||||||
pub struct RequestContext {
|
pub struct RequestContext {
|
||||||
pub sections: Sections,
|
pub sections: Sections,
|
||||||
@ -14,8 +15,6 @@ pub struct RequestContext {
|
|||||||
|
|
||||||
type BoxFuture<Output> = Pin<Box<dyn Future<Output = Output>>>;
|
type BoxFuture<Output> = Pin<Box<dyn Future<Output = Output>>>;
|
||||||
|
|
||||||
pub type ResponseValue = serde_json::Value;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
pub enum ErrorCode {
|
pub enum ErrorCode {
|
||||||
@ -156,17 +155,69 @@ impl RequestError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type RequestResult = Result<ResponseValue, RequestError>;
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
type RequestFuture = BoxFuture<RequestResult>;
|
struct ResponseMessage(#[serde(rename = "message")] String);
|
||||||
|
|
||||||
|
impl ResponseMessage {
|
||||||
|
fn new<M>(message: M) -> Self
|
||||||
|
where
|
||||||
|
M: ToString,
|
||||||
|
{
|
||||||
|
ResponseMessage(message.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for ResponseMessage {
|
||||||
|
fn from(message: String) -> Self {
|
||||||
|
ResponseMessage(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ResponseValue = serde_json::Value;
|
||||||
|
|
||||||
|
type RequestResult<Ok = ResponseValue> = Result<Ok, RequestError>;
|
||||||
|
type RequestFuture<Ok = ResponseValue> = BoxFuture<RequestResult<Ok>>;
|
||||||
|
|
||||||
trait IRequest {
|
trait IRequest {
|
||||||
fn exec(&mut self, ctx: &mut RequestContext) -> RequestFuture;
|
type Response: Serialize;
|
||||||
|
|
||||||
|
fn exec(&mut self, ctx: &mut RequestContext) -> RequestFuture<Self::Response>;
|
||||||
|
|
||||||
|
fn exec_erased(&mut self, ctx: &mut RequestContext) -> RequestFuture
|
||||||
|
where
|
||||||
|
Self::Response: 'static,
|
||||||
|
{
|
||||||
|
// TODO: figure out how to get rid of this nested box
|
||||||
|
Box::pin(ErasedRequestFuture(self.exec(ctx)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ErasedRequestFuture<Response>(RequestFuture<Response>)
|
||||||
|
where
|
||||||
|
Response: Serialize;
|
||||||
|
|
||||||
|
impl<Response> Future for ErasedRequestFuture<Response>
|
||||||
|
where
|
||||||
|
Response: Serialize,
|
||||||
|
{
|
||||||
|
type Output = RequestResult;
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
|
||||||
|
use eyre::WrapErr;
|
||||||
|
let response = ready!(self.as_mut().0.poll_unpin(cx));
|
||||||
|
Poll::Ready(response.and_then(|res| {
|
||||||
|
serde_json::to_value(res)
|
||||||
|
.wrap_err("could not serialize response")
|
||||||
|
.map_err(RequestError::from)
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase", tag = "type")]
|
#[serde(rename_all = "camelCase", tag = "type")]
|
||||||
pub enum Request {
|
pub enum Request {
|
||||||
RunSection(run_section::RequestData),
|
RunSection(sections::RunSectionRequest),
|
||||||
|
CancelSection(sections::CancelSectionRequest),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
@ -192,9 +243,12 @@ impl From<RequestError> for Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IRequest for Request {
|
impl IRequest for Request {
|
||||||
fn exec(&mut self, ctx: &mut RequestContext) -> BoxFuture<RequestResult> {
|
type Response = ResponseValue;
|
||||||
|
|
||||||
|
fn exec(&mut self, ctx: &mut RequestContext) -> RequestFuture {
|
||||||
match self {
|
match self {
|
||||||
Request::RunSection(req) => req.exec(ctx),
|
Request::RunSection(req) => req.exec_erased(ctx),
|
||||||
|
Request::CancelSection(req) => req.exec_erased(ctx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
use crate::{model::SectionId, section_runner::SectionRunHandle};
|
|
||||||
use eyre::WrapErr;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct RequestData {
|
|
||||||
pub section_id: SectionId,
|
|
||||||
#[serde(with = "crate::serde::duration")]
|
|
||||||
pub duration: Duration,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct ResponseData {
|
|
||||||
pub message: String,
|
|
||||||
pub run_id: SectionRunHandle,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IRequest for RequestData {
|
|
||||||
fn exec(&mut self, ctx: &mut RequestContext) -> RequestFuture {
|
|
||||||
let mut section_runner = ctx.section_runner.clone();
|
|
||||||
let section = ctx.sections.get(&self.section_id).cloned();
|
|
||||||
let duration = self.duration;
|
|
||||||
Box::pin(async move {
|
|
||||||
let section = section.ok_or_else(|| {
|
|
||||||
RequestError::with_name(ErrorCode::NotFound, "section not found", "section")
|
|
||||||
})?;
|
|
||||||
let handle = section_runner
|
|
||||||
.queue_run(section.clone(), duration)
|
|
||||||
.await
|
|
||||||
.wrap_err("could not queue run")?;
|
|
||||||
let res = ResponseData {
|
|
||||||
message: format!("running section '{}' for {:?}", §ion.name, duration),
|
|
||||||
run_id: handle,
|
|
||||||
};
|
|
||||||
let res_value = serde_json::to_value(res).wrap_err("could not serialize response")?;
|
|
||||||
Ok(res_value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
87
src/mqtt/request/sections.rs
Normal file
87
src/mqtt/request/sections.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::{model::SectionRef, section_runner::SectionRunHandle};
|
||||||
|
use eyre::WrapErr;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct SectionId(pub crate::model::SectionId);
|
||||||
|
|
||||||
|
impl SectionId {
|
||||||
|
fn get_section(self, sections: &Sections) -> Result<SectionRef, RequestError> {
|
||||||
|
sections.get(&self.0).cloned().ok_or_else(|| {
|
||||||
|
RequestError::with_name(ErrorCode::NotFound, "section not found", "section")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RunSectionRequest {
|
||||||
|
pub section_id: SectionId,
|
||||||
|
#[serde(with = "crate::serde::duration")]
|
||||||
|
pub duration: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RunSectionResponse {
|
||||||
|
pub message: String,
|
||||||
|
pub run_id: SectionRunHandle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IRequest for RunSectionRequest {
|
||||||
|
type Response = RunSectionResponse;
|
||||||
|
fn exec(&mut self, ctx: &mut RequestContext) -> RequestFuture<Self::Response> {
|
||||||
|
let mut section_runner = ctx.section_runner.clone();
|
||||||
|
let section = self.section_id.get_section(&ctx.sections);
|
||||||
|
let duration = self.duration;
|
||||||
|
Box::pin(async move {
|
||||||
|
let section = section?;
|
||||||
|
let handle = section_runner
|
||||||
|
.queue_run(section.clone(), duration)
|
||||||
|
.await
|
||||||
|
.wrap_err("could not queue run")?;
|
||||||
|
Ok(RunSectionResponse {
|
||||||
|
message: format!("running section '{}' for {:?}", §ion.name, duration),
|
||||||
|
run_id: handle,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CancelSectionRequest {
|
||||||
|
pub section_id: SectionId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CancelSectionResponse {
|
||||||
|
pub message: String,
|
||||||
|
pub cancelled: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IRequest for CancelSectionRequest {
|
||||||
|
type Response = CancelSectionResponse;
|
||||||
|
fn exec(&mut self, ctx: &mut RequestContext) -> RequestFuture<Self::Response> {
|
||||||
|
let mut section_runner = ctx.section_runner.clone();
|
||||||
|
let section = self.section_id.get_section(&ctx.sections);
|
||||||
|
Box::pin(async move {
|
||||||
|
let section = section?;
|
||||||
|
let cancelled = section_runner
|
||||||
|
.cancel_by_section(section.id)
|
||||||
|
.await
|
||||||
|
.wrap_err("could not cancel section")?;
|
||||||
|
Ok(CancelSectionResponse {
|
||||||
|
message: format!(
|
||||||
|
"cancelled {} runs for section '{}'",
|
||||||
|
cancelled, section.name
|
||||||
|
),
|
||||||
|
cancelled,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
use crate::model::SectionRef;
|
use crate::model::{SectionId, SectionRef};
|
||||||
use crate::section_interface::SectionInterface;
|
use crate::section_interface::SectionInterface;
|
||||||
use actix::{
|
use actix::{
|
||||||
Actor, ActorContext, Addr, AsyncContext, Handler, Message, MessageResult, SpawnHandle,
|
Actor, ActorContext, Addr, AsyncContext, Handler, Message, MessageResult, SpawnHandle,
|
||||||
@ -20,8 +20,7 @@ use tokio::{
|
|||||||
};
|
};
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)]
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
pub struct SectionRunHandle(i32);
|
pub struct SectionRunHandle(i32);
|
||||||
|
|
||||||
impl SectionRunHandle {
|
impl SectionRunHandle {
|
||||||
@ -177,19 +176,24 @@ impl SectionRunnerInner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cancel_run(&mut self, run: &mut Arc<SecRun>) {
|
fn cancel_run(&mut self, run: &mut Arc<SecRun>) -> 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!(section_id = run.section.id, "cancelling running section");
|
||||||
self.interface
|
self.interface
|
||||||
.set_section_state(run.section.interface_id, false);
|
.set_section_state(run.section.interface_id, false);
|
||||||
}
|
}
|
||||||
run.state = SecRunState::Cancelled;
|
if run.state != SecRunState::Cancelled {
|
||||||
self.send_event(SectionEvent::RunCancel(
|
run.state = SecRunState::Cancelled;
|
||||||
run.handle.clone(),
|
self.send_event(SectionEvent::RunCancel(
|
||||||
run.section.clone(),
|
run.handle.clone(),
|
||||||
));
|
run.section.clone(),
|
||||||
self.did_change = true;
|
));
|
||||||
|
self.did_change = true;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pause_run(&mut self, run: &mut Arc<SecRun>) {
|
fn pause_run(&mut self, run: &mut Arc<SecRun>) {
|
||||||
@ -322,40 +326,78 @@ impl Handler<QueueRun> for SectionRunnerActor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Message, Debug, Clone)]
|
#[derive(Message, Debug, Clone)]
|
||||||
#[rtype(result = "()")]
|
#[rtype(result = "bool")]
|
||||||
struct CancelRun(SectionRunHandle);
|
struct CancelRun(SectionRunHandle);
|
||||||
|
|
||||||
impl Handler<CancelRun> for SectionRunnerActor {
|
impl Handler<CancelRun> for SectionRunnerActor {
|
||||||
type Result = ();
|
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 {
|
||||||
let CancelRun(handle) = msg;
|
let CancelRun(handle) = msg;
|
||||||
for run in self.state.run_queue.iter_mut() {
|
let mut cancelled = false;
|
||||||
if run.handle != handle {
|
for run in self
|
||||||
continue;
|
.state
|
||||||
}
|
.run_queue
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|run| run.handle == handle)
|
||||||
|
{
|
||||||
trace!(handle = handle.0, "cancelling run by handle");
|
trace!(handle = handle.0, "cancelling run by handle");
|
||||||
self.inner.cancel_run(run);
|
cancelled = self.inner.cancel_run(run);
|
||||||
}
|
}
|
||||||
ctx.notify(Process);
|
ctx.notify(Process);
|
||||||
|
cancelled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Message, Debug, Clone)]
|
#[derive(Message, Debug, Clone)]
|
||||||
#[rtype(result = "()")]
|
#[rtype(result = "usize")]
|
||||||
|
struct CancelBySection(SectionId);
|
||||||
|
|
||||||
|
impl Handler<CancelBySection> for SectionRunnerActor {
|
||||||
|
type Result = usize;
|
||||||
|
|
||||||
|
fn handle(&mut self, msg: CancelBySection, ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
let CancelBySection(section_id) = msg;
|
||||||
|
let mut count = 0_usize;
|
||||||
|
for run in self
|
||||||
|
.state
|
||||||
|
.run_queue
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|run| run.section.id == section_id)
|
||||||
|
{
|
||||||
|
trace!(
|
||||||
|
handle = run.handle.0,
|
||||||
|
section_id,
|
||||||
|
"cancelling run by section"
|
||||||
|
);
|
||||||
|
if self.inner.cancel_run(run) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.notify(Process);
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Message, Debug, Clone)]
|
||||||
|
#[rtype(result = "usize")]
|
||||||
struct CancelAll;
|
struct CancelAll;
|
||||||
|
|
||||||
impl Handler<CancelAll> for SectionRunnerActor {
|
impl Handler<CancelAll> for SectionRunnerActor {
|
||||||
type Result = ();
|
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 = SecRunQueue::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;
|
||||||
for mut run in old_runs {
|
for mut run in old_runs {
|
||||||
self.inner.cancel_run(&mut run);
|
if self.inner.cancel_run(&mut run) {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ctx.notify(Process);
|
ctx.notify(Process);
|
||||||
|
count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,11 +559,7 @@ impl SectionRunner {
|
|||||||
(QueueRun(handle.clone(), section, duration), handle)
|
(QueueRun(handle.clone(), section, duration), handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_queue_run(
|
pub fn do_queue_run(&mut self, section: SectionRef, duration: Duration) -> SectionRunHandle {
|
||||||
&mut self,
|
|
||||||
section: SectionRef,
|
|
||||||
duration: Duration,
|
|
||||||
) -> SectionRunHandle {
|
|
||||||
let (queue_run, handle) = self.queue_run_inner(section, duration);
|
let (queue_run, handle) = self.queue_run_inner(section, duration);
|
||||||
self.addr.do_send(queue_run);
|
self.addr.do_send(queue_run);
|
||||||
handle
|
handle
|
||||||
@ -543,11 +581,20 @@ impl SectionRunner {
|
|||||||
self.addr.do_send(CancelRun(handle))
|
self.addr.do_send(CancelRun(handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cancel_run(&mut self, handle: SectionRunHandle) -> impl Future<Output = Result<()>> {
|
pub fn cancel_run(&mut self, handle: SectionRunHandle) -> 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_all(&mut self) -> impl Future<Output = Result<()>> {
|
pub fn cancel_by_section(
|
||||||
|
&mut self,
|
||||||
|
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>> {
|
||||||
self.addr.send(CancelAll).map_err(From::from)
|
self.addr.send(CancelAll).map_err(From::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user