Alex Mikhalev
6 years ago
7 changed files with 508 additions and 489 deletions
@ -1,489 +0,0 @@
@@ -1,489 +0,0 @@
|
||||
use std::cell::RefCell; |
||||
use std::rc::Rc; |
||||
|
||||
pub trait CommandId { |
||||
fn command_id(&self) -> u64; |
||||
} |
||||
|
||||
impl CommandId for u64 { |
||||
fn command_id(&self) -> u64 { |
||||
*self |
||||
} |
||||
} |
||||
|
||||
pub trait WithCommandInfo { |
||||
fn command_info(&self) -> &CommandInfo; |
||||
} |
||||
|
||||
pub trait GetCommandInfo: CommandId { |
||||
fn name(&self) -> &str; |
||||
} |
||||
|
||||
pub struct CommandInfo { |
||||
command_id: u64, |
||||
name: String, |
||||
} |
||||
|
||||
impl CommandInfo { |
||||
pub fn new<TName: Into<String>>(command_id: u64, name: TName) -> Self { |
||||
Self { |
||||
command_id, |
||||
name: name.into(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl CommandId for CommandInfo { |
||||
fn command_id(&self) -> u64 { |
||||
self.command_id |
||||
} |
||||
} |
||||
|
||||
impl GetCommandInfo for CommandInfo { |
||||
fn name(&self) -> &str { |
||||
&self.name |
||||
} |
||||
} |
||||
|
||||
impl<T: WithCommandInfo> CommandId for T { |
||||
fn command_id(&self) -> u64 { |
||||
self.command_info().command_id |
||||
} |
||||
} |
||||
|
||||
impl<T: WithCommandInfo> GetCommandInfo for T { |
||||
fn name(&self) -> &str { |
||||
&self.command_info().name |
||||
} |
||||
} |
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)] |
||||
pub enum CommandResult { |
||||
Continue, |
||||
Done, |
||||
Cancel, |
||||
} |
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)] |
||||
pub enum CommandStatus { |
||||
Stopped, |
||||
Running, |
||||
Ended, |
||||
Cancelling, |
||||
Cancelled, |
||||
} |
||||
|
||||
pub trait CommandParent { |
||||
fn start_command(&mut self, cmd: CommandRef) -> bool; |
||||
} |
||||
|
||||
pub struct CommandContext<T> |
||||
where |
||||
T: CommandImp, |
||||
{ |
||||
info: CommandInfo, |
||||
status: CommandStatus, |
||||
parent: Option<*mut CommandParent>, |
||||
state: <T as CommandImp>::State, |
||||
} |
||||
|
||||
impl<T: CommandImp> CommandContext<T> { |
||||
pub fn status(&self) -> CommandStatus { |
||||
self.status |
||||
} |
||||
|
||||
pub fn state(&mut self) -> &mut T::State { |
||||
&mut self.state |
||||
} |
||||
|
||||
pub fn parent(&self) -> &mut CommandParent { |
||||
self.try_parent() |
||||
.expect("CommandContext parent called but no parent exists") |
||||
} |
||||
|
||||
pub fn try_parent<'a>(&'a self) -> Option<&'a mut CommandParent> { |
||||
self.parent |
||||
.map(|ptr| unsafe { &mut *ptr } as &'a mut CommandParent) |
||||
} |
||||
} |
||||
|
||||
impl<T: CommandImp> WithCommandInfo for CommandContext<T> { |
||||
fn command_info(&self) -> &CommandInfo { |
||||
&self.info |
||||
} |
||||
} |
||||
|
||||
pub trait CommandImp { |
||||
type State: Default; |
||||
|
||||
fn init(&self, ctx: &mut CommandContext<Self>) |
||||
where |
||||
Self: Sized; |
||||
fn step(&self, ctx: &mut CommandContext<Self>) -> CommandResult |
||||
where |
||||
Self: Sized; |
||||
fn finish(&self, ctx: &mut CommandContext<Self>) |
||||
where |
||||
Self: Sized; |
||||
} |
||||
|
||||
pub trait Command: WithCommandInfo { |
||||
fn reset(&mut self); |
||||
fn status(&self) -> CommandStatus; |
||||
fn execute(&mut self, &mut CommandParent) -> CommandStatus; |
||||
fn cancel(&mut self); |
||||
} |
||||
|
||||
pub struct GenericCommand<T: CommandImp> { |
||||
context: CommandContext<T>, |
||||
pub imp: T, |
||||
} |
||||
|
||||
impl<T: CommandImp + 'static> GenericCommand<T> { |
||||
pub fn new(info: CommandInfo, imp: T) -> Self { |
||||
Self { |
||||
context: CommandContext { |
||||
info, |
||||
status: CommandStatus::Stopped, |
||||
parent: None, |
||||
state: T::State::default(), |
||||
}, |
||||
imp, |
||||
} |
||||
} |
||||
|
||||
pub fn new_ref(info: CommandInfo, imp: T) -> CommandRef<Self> { |
||||
Rc::new(RefCell::new(Self::new(info, imp))) |
||||
} |
||||
} |
||||
|
||||
impl<T: CommandImp> WithCommandInfo for GenericCommand<T> { |
||||
fn command_info(&self) -> &CommandInfo { |
||||
&self.context.info |
||||
} |
||||
} |
||||
|
||||
impl<T: CommandImp> Command for GenericCommand<T> { |
||||
fn status(&self) -> CommandStatus { |
||||
self.context.status |
||||
} |
||||
|
||||
fn execute(&mut self, parent: &mut CommandParent) -> CommandStatus { |
||||
use self::CommandResult::*; |
||||
use self::CommandStatus::*; |
||||
let parent_ptr: *mut CommandParent = unsafe { std::mem::transmute(parent) }; |
||||
self.context.parent = Some(parent_ptr); |
||||
let do_cancel = |me: &mut Self| { |
||||
me.context.status = Cancelling; |
||||
me.imp.finish(&mut me.context); |
||||
Cancelled |
||||
}; |
||||
let do_step = |me: &mut Self| { |
||||
let result = me.imp.step(&mut me.context); |
||||
match result { |
||||
Continue => Running, |
||||
Done => { |
||||
me.imp.finish(&mut me.context); |
||||
Ended |
||||
} |
||||
Cancel => do_cancel(me), |
||||
} |
||||
}; |
||||
let new_status = match self.status() { |
||||
Stopped => { |
||||
self.imp.init(&mut self.context); |
||||
do_step(self) |
||||
} |
||||
Running => do_step(self), |
||||
Ended => Ended, |
||||
Cancelling => do_cancel(self), |
||||
Cancelled => Cancelled, |
||||
}; |
||||
self.context.status = new_status; |
||||
self.context.parent = None; |
||||
new_status |
||||
} |
||||
|
||||
fn reset(&mut self) { |
||||
use self::CommandStatus::*; |
||||
self.context.status = match self.context.status { |
||||
Stopped | Ended | Cancelled => Stopped, |
||||
other => { |
||||
warn!( |
||||
"GenericCommand({}) reset called in non-resetable status {:?}", |
||||
self.name(), |
||||
other |
||||
); |
||||
self.imp.finish(&mut self.context); |
||||
Stopped |
||||
} |
||||
}; |
||||
} |
||||
|
||||
fn cancel(&mut self) { |
||||
use self::CommandStatus::*; |
||||
self.context.status = match self.context.status { |
||||
Running => Cancelling, |
||||
other => other, |
||||
}; |
||||
} |
||||
} |
||||
|
||||
macro_rules! command_impl { |
||||
( |
||||
$command:ident<$tconfig:ty>; |
||||
$slf:ident => $init:block |
||||
) => { |
||||
command_impl! { $command<$tconfig>; |
||||
$slf, ctx => $init } |
||||
}; |
||||
( |
||||
$command:ident<$tconfig:ty>; |
||||
$slf:ident, $ctx:ident => $init:block |
||||
) => { |
||||
command_impl! { $command<$tconfig,()>; |
||||
$slf, $ctx => $init; { $crate::command::CommandResult::Done }; {} } |
||||
}; |
||||
( |
||||
$command:ident<$tconfig:ty,$tstate:ty>; |
||||
$slf:ident, $ctx:ident => $init:block; |
||||
$step:block; |
||||
$finish:block |
||||
) => { |
||||
impl $crate::command::CommandImp for $tconfig { |
||||
type State = $tstate; |
||||
|
||||
fn init(&$slf, $ctx: &mut $crate::command::CommandContext<Self>) { |
||||
use $crate::command::GetCommandInfo; |
||||
debug!( |
||||
"{}({}) init", |
||||
stringify!($command), |
||||
$ctx.name() |
||||
); |
||||
$init; |
||||
} |
||||
|
||||
fn step(&$slf, $ctx: &mut $crate::command::CommandContext<Self>) -> $crate::command::CommandResult { |
||||
use $crate::command::GetCommandInfo; |
||||
debug!( |
||||
"{}({}) step", |
||||
stringify!($command), |
||||
$ctx.name() |
||||
); |
||||
$step |
||||
} |
||||
|
||||
fn finish(&$slf, $ctx: &mut $crate::command::CommandContext<Self>) { |
||||
use $crate::command::GetCommandInfo; |
||||
debug!( |
||||
"{}({}) finish({:?})", |
||||
stringify!($command), |
||||
$ctx.name(), |
||||
$ctx.status(), |
||||
); |
||||
$finish; |
||||
} |
||||
} |
||||
pub type $command = $crate::command::GenericCommand<$tconfig>; |
||||
}; |
||||
( |
||||
$command:ident<$tconfig:ty,$tstate:ty>; |
||||
$slf:ident => $init:block; |
||||
$step:block; |
||||
$finish:block |
||||
) => { |
||||
command_impl! { $command<$tconfig,$tstate>; |
||||
$init_self, cts => $init; $step; $finish } |
||||
}; |
||||
|
||||
} |
||||
|
||||
pub type CommandRef<T = dyn Command> = Rc<RefCell<T>>; |
||||
|
||||
pub struct CommandGroupConfig { |
||||
pub commands: Vec<CommandRef>, |
||||
} |
||||
|
||||
impl CommandGroupConfig { |
||||
pub fn new(commands: Vec<CommandRef>) -> Self { |
||||
Self { commands } |
||||
} |
||||
} |
||||
|
||||
pub struct CommandGroupState { |
||||
current_idx: usize, |
||||
has_cancelled: bool, |
||||
} |
||||
|
||||
impl Default for CommandGroupState { |
||||
fn default() -> Self { |
||||
CommandGroupState { |
||||
current_idx: 0, |
||||
has_cancelled: false, |
||||
} |
||||
} |
||||
} |
||||
|
||||
pub type CommandGroup = GenericCommand<CommandGroupConfig>; |
||||
|
||||
impl CommandImp for CommandGroupConfig { |
||||
type State = CommandGroupState; |
||||
|
||||
fn init(&self, ctx: &mut CommandContext<Self>) { |
||||
*ctx.state() = Default::default(); |
||||
debug!("CommandGroup {} init", ctx.name()); |
||||
} |
||||
|
||||
fn step(&self, ctx: &mut CommandContext<Self>) -> CommandResult { |
||||
use self::CommandResult::*; |
||||
use self::CommandStatus::*; |
||||
let commands = &self.commands; |
||||
if ctx.state().has_cancelled { |
||||
debug!("CommandGroup step called after cancelled"); |
||||
return CommandResult::Cancel; |
||||
} |
||||
loop { |
||||
if ctx.state().current_idx >= commands.len() { |
||||
debug!("CommandGroup out of commands"); |
||||
return CommandResult::Done; |
||||
} |
||||
let mut command = commands[ctx.state().current_idx].borrow_mut(); |
||||
let status = command.execute(ctx.parent()); |
||||
match status { |
||||
Ended => { |
||||
command.reset(); |
||||
ctx.state().current_idx += 1; |
||||
} |
||||
Cancelled => { |
||||
command.reset(); |
||||
ctx.state().has_cancelled = true; |
||||
return Cancel; |
||||
} |
||||
Running => return Continue, |
||||
other => { |
||||
warn!("CommandGroup unhandled status {:?}", other); |
||||
return Continue; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn finish(&self, ctx: &mut CommandContext<Self>) { |
||||
let commands = &self.commands; |
||||
if ctx.status() == CommandStatus::Cancelling { |
||||
if let Some(command) = commands.get(ctx.state().current_idx) { |
||||
let mut command = command.borrow_mut(); |
||||
command.cancel(); |
||||
command.execute(ctx.parent()); |
||||
command.reset(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
struct SchedulerCommandParent { |
||||
start_queue: Vec<CommandRef>, |
||||
} |
||||
|
||||
impl SchedulerCommandParent { |
||||
fn new() -> Self { |
||||
Self { |
||||
start_queue: Vec::new(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl CommandParent for SchedulerCommandParent { |
||||
fn start_command(&mut self, command: CommandRef) -> bool { |
||||
self.start_queue.push(command); |
||||
true |
||||
} |
||||
} |
||||
|
||||
pub struct Scheduler { |
||||
running_commands: Vec<CommandRef>, |
||||
} |
||||
|
||||
impl Scheduler { |
||||
pub fn new() -> Self { |
||||
Self { |
||||
running_commands: Vec::new(), |
||||
} |
||||
} |
||||
|
||||
fn get_command<T: CommandId>(&self, id: T) -> Option<&CommandRef> { |
||||
let id = id.command_id(); |
||||
for command in &self.running_commands { |
||||
if command.borrow().command_info().command_id() == id { |
||||
return Some(command); |
||||
} |
||||
} |
||||
None |
||||
} |
||||
|
||||
fn get_command_mut<T: CommandId>(&mut self, id: T) -> Option<&mut CommandRef> { |
||||
let id = id.command_id(); |
||||
for command in &mut self.running_commands { |
||||
if command.borrow().command_info().command_id() == id { |
||||
return Some(command); |
||||
} |
||||
} |
||||
None |
||||
} |
||||
|
||||
pub fn start_command(&mut self, command: CommandRef) -> bool { |
||||
let command_id = command.borrow().command_info().command_id(); |
||||
if self.is_running(command_id) { |
||||
false |
||||
} else { |
||||
self.running_commands.push(command); |
||||
true |
||||
} |
||||
} |
||||
|
||||
pub fn is_running<T: CommandId>(&self, id: T) -> bool { |
||||
self.get_command(id).is_some() |
||||
} |
||||
|
||||
pub fn cancel_command<T: CommandId>(&mut self, id: T) -> bool { |
||||
self.get_command_mut(id).map_or(false, |command| { |
||||
command.borrow_mut().cancel(); |
||||
true |
||||
}) |
||||
} |
||||
|
||||
pub fn execute(&mut self) { |
||||
use self::CommandStatus::*; |
||||
let mut parent = SchedulerCommandParent::new(); |
||||
let exec_command = |
||||
|command: &mut Command, parent: &mut CommandParent| match command.execute(parent) { |
||||
Ended | Cancelled => { |
||||
command.reset(); |
||||
true |
||||
} |
||||
Stopped | Running | Cancelling => false, |
||||
}; |
||||
self.running_commands.drain_filter(|command| { |
||||
let mut command = command.borrow_mut(); |
||||
exec_command(&mut *command, &mut parent) |
||||
}); |
||||
while !parent.start_queue.is_empty() { |
||||
let mut new_parent = SchedulerCommandParent::new(); |
||||
{ |
||||
let more_commands = parent.start_queue.into_iter().filter_map(|command_ref| { |
||||
let done = { |
||||
let mut command = command_ref.borrow_mut(); |
||||
exec_command(&mut *command, &mut new_parent) |
||||
}; |
||||
if !done { |
||||
Some(command_ref) |
||||
} else { |
||||
None |
||||
} |
||||
}); |
||||
self.running_commands.extend(more_commands); |
||||
} |
||||
parent = new_parent; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,80 @@
@@ -0,0 +1,80 @@
|
||||
use std::cell::RefCell; |
||||
use std::rc::Rc; |
||||
|
||||
pub trait CommandId { |
||||
fn command_id(&self) -> u64; |
||||
} |
||||
|
||||
impl CommandId for u64 { |
||||
fn command_id(&self) -> u64 { |
||||
*self |
||||
} |
||||
} |
||||
|
||||
pub trait WithCommandInfo { |
||||
fn command_info(&self) -> &CommandInfo; |
||||
} |
||||
|
||||
pub trait GetCommandInfo: CommandId { |
||||
fn name(&self) -> &str; |
||||
} |
||||
|
||||
pub struct CommandInfo { |
||||
command_id: u64, |
||||
name: String, |
||||
} |
||||
|
||||
impl CommandInfo { |
||||
pub fn new<TName: Into<String>>(command_id: u64, name: TName) -> Self { |
||||
Self { |
||||
command_id, |
||||
name: name.into(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl CommandId for CommandInfo { |
||||
fn command_id(&self) -> u64 { |
||||
self.command_id |
||||
} |
||||
} |
||||
|
||||
impl GetCommandInfo for CommandInfo { |
||||
fn name(&self) -> &str { |
||||
&self.name |
||||
} |
||||
} |
||||
|
||||
impl<T: WithCommandInfo> CommandId for T { |
||||
fn command_id(&self) -> u64 { |
||||
self.command_info().command_id |
||||
} |
||||
} |
||||
|
||||
impl<T: WithCommandInfo> GetCommandInfo for T { |
||||
fn name(&self) -> &str { |
||||
&self.command_info().name |
||||
} |
||||
} |
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)] |
||||
pub enum CommandStatus { |
||||
Stopped, |
||||
Running, |
||||
Ended, |
||||
Cancelling, |
||||
Cancelled, |
||||
} |
||||
|
||||
pub trait CommandParent { |
||||
fn start_command(&mut self, cmd: CommandRef) -> bool; |
||||
} |
||||
|
||||
pub trait Command: WithCommandInfo { |
||||
fn reset(&mut self); |
||||
fn status(&self) -> CommandStatus; |
||||
fn execute(&mut self, &mut CommandParent) -> CommandStatus; |
||||
fn cancel(&mut self); |
||||
} |
||||
|
||||
pub type CommandRef<T = dyn Command> = Rc<RefCell<T>>; |
@ -0,0 +1,82 @@
@@ -0,0 +1,82 @@
|
||||
use super::{CommandContext, CommandImp, CommandRef, CommandResult, GetCommandInfo, CommandStatus, GenericCommand}; |
||||
|
||||
pub struct CommandGroupConfig { |
||||
pub commands: Vec<CommandRef>, |
||||
} |
||||
|
||||
impl CommandGroupConfig { |
||||
pub fn new(commands: Vec<CommandRef>) -> Self { |
||||
Self { commands } |
||||
} |
||||
} |
||||
|
||||
pub struct CommandGroupState { |
||||
current_idx: usize, |
||||
has_cancelled: bool, |
||||
} |
||||
|
||||
impl Default for CommandGroupState { |
||||
fn default() -> Self { |
||||
CommandGroupState { |
||||
current_idx: 0, |
||||
has_cancelled: false, |
||||
} |
||||
} |
||||
} |
||||
|
||||
pub type CommandGroup = GenericCommand<CommandGroupConfig>; |
||||
|
||||
impl CommandImp for CommandGroupConfig { |
||||
type State = CommandGroupState; |
||||
|
||||
fn init(&self, ctx: &mut CommandContext<Self>) { |
||||
*ctx.state() = Default::default(); |
||||
debug!("CommandGroup {} init", ctx.name()); |
||||
} |
||||
|
||||
fn step(&self, ctx: &mut CommandContext<Self>) -> CommandResult { |
||||
use self::CommandResult::*; |
||||
use self::CommandStatus::*; |
||||
let commands = &self.commands; |
||||
if ctx.state().has_cancelled { |
||||
debug!("CommandGroup step called after cancelled"); |
||||
return CommandResult::Cancel; |
||||
} |
||||
loop { |
||||
if ctx.state().current_idx >= commands.len() { |
||||
debug!("CommandGroup out of commands"); |
||||
return CommandResult::Done; |
||||
} |
||||
let mut command = commands[ctx.state().current_idx].borrow_mut(); |
||||
let status = command.execute(ctx.parent()); |
||||
match status { |
||||
Ended => { |
||||
command.reset(); |
||||
ctx.state().current_idx += 1; |
||||
} |
||||
Cancelled => { |
||||
command.reset(); |
||||
ctx.state().has_cancelled = true; |
||||
return Cancel; |
||||
} |
||||
Running => return Continue, |
||||
other => { |
||||
warn!("CommandGroup unhandled status {:?}", other); |
||||
return Continue; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn finish(&self, ctx: &mut CommandContext<Self>) { |
||||
let commands = &self.commands; |
||||
if ctx.status() == CommandStatus::Cancelling { |
||||
if let Some(command) = commands.get(ctx.state().current_idx) { |
||||
let mut command = command.borrow_mut(); |
||||
command.cancel(); |
||||
command.execute(ctx.parent()); |
||||
command.reset(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,157 @@
@@ -0,0 +1,157 @@
|
||||
use super::{ |
||||
Command, CommandInfo, CommandParent, CommandRef, CommandStatus, GetCommandInfo, WithCommandInfo, |
||||
}; |
||||
use std::cell::RefCell; |
||||
use std::rc::Rc; |
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)] |
||||
pub enum CommandResult { |
||||
Continue, |
||||
Done, |
||||
Cancel, |
||||
} |
||||
|
||||
pub struct CommandContext<T> |
||||
where |
||||
T: CommandImp, |
||||
{ |
||||
info: CommandInfo, |
||||
status: CommandStatus, |
||||
parent: Option<*mut CommandParent>, |
||||
state: <T as CommandImp>::State, |
||||
} |
||||
|
||||
impl<T: CommandImp> CommandContext<T> { |
||||
pub fn status(&self) -> CommandStatus { |
||||
self.status |
||||
} |
||||
|
||||
pub fn state(&mut self) -> &mut T::State { |
||||
&mut self.state |
||||
} |
||||
|
||||
pub fn parent(&self) -> &mut CommandParent { |
||||
self.try_parent() |
||||
.expect("CommandContext parent called but no parent exists") |
||||
} |
||||
|
||||
pub fn try_parent<'a>(&'a self) -> Option<&'a mut CommandParent> { |
||||
self.parent |
||||
.map(|ptr| unsafe { &mut *ptr } as &'a mut CommandParent) |
||||
} |
||||
} |
||||
|
||||
impl<T: CommandImp> WithCommandInfo for CommandContext<T> { |
||||
fn command_info(&self) -> &CommandInfo { |
||||
&self.info |
||||
} |
||||
} |
||||
|
||||
pub trait CommandImp { |
||||
type State: Default; |
||||
|
||||
fn init(&self, ctx: &mut CommandContext<Self>) |
||||
where |
||||
Self: Sized; |
||||
fn step(&self, ctx: &mut CommandContext<Self>) -> CommandResult |
||||
where |
||||
Self: Sized; |
||||
fn finish(&self, ctx: &mut CommandContext<Self>) |
||||
where |
||||
Self: Sized; |
||||
} |
||||
|
||||
pub struct GenericCommand<T: CommandImp> { |
||||
context: CommandContext<T>, |
||||
pub imp: T, |
||||
} |
||||
|
||||
impl<T: CommandImp + 'static> GenericCommand<T> { |
||||
pub fn new(info: CommandInfo, imp: T) -> Self { |
||||
Self { |
||||
context: CommandContext { |
||||
info, |
||||
status: CommandStatus::Stopped, |
||||
parent: None, |
||||
state: T::State::default(), |
||||
}, |
||||
imp, |
||||
} |
||||
} |
||||
|
||||
pub fn new_ref(info: CommandInfo, imp: T) -> CommandRef<Self> { |
||||
Rc::new(RefCell::new(Self::new(info, imp))) |
||||
} |
||||
} |
||||
|
||||
impl<T: CommandImp> WithCommandInfo for GenericCommand<T> { |
||||
fn command_info(&self) -> &CommandInfo { |
||||
&self.context.info |
||||
} |
||||
} |
||||
|
||||
impl<T: CommandImp> Command for GenericCommand<T> { |
||||
fn status(&self) -> CommandStatus { |
||||
self.context.status |
||||
} |
||||
|
||||
fn execute(&mut self, parent: &mut CommandParent) -> CommandStatus { |
||||
use self::CommandResult::*; |
||||
use self::CommandStatus::*; |
||||
let parent_ptr: *mut CommandParent = unsafe { std::mem::transmute(parent) }; |
||||
self.context.parent = Some(parent_ptr); |
||||
let do_cancel = |me: &mut Self| { |
||||
me.context.status = Cancelling; |
||||
me.imp.finish(&mut me.context); |
||||
Cancelled |
||||
}; |
||||
let do_step = |me: &mut Self| { |
||||
let result = me.imp.step(&mut me.context); |
||||
match result { |
||||
Continue => Running, |
||||
Done => { |
||||
me.imp.finish(&mut me.context); |
||||
Ended |
||||
} |
||||
Cancel => do_cancel(me), |
||||
} |
||||
}; |
||||
let new_status = match self.status() { |
||||
Stopped => { |
||||
self.imp.init(&mut self.context); |
||||
do_step(self) |
||||
} |
||||
Running => do_step(self), |
||||
Ended => Ended, |
||||
Cancelling => do_cancel(self), |
||||
Cancelled => Cancelled, |
||||
}; |
||||
self.context.status = new_status; |
||||
self.context.parent = None; |
||||
new_status |
||||
} |
||||
|
||||
fn reset(&mut self) { |
||||
use self::CommandStatus::*; |
||||
self.context.status = match self.context.status { |
||||
Stopped | Ended | Cancelled => Stopped, |
||||
other => { |
||||
warn!( |
||||
"GenericCommand({}) reset called in non-resetable status {:?}", |
||||
self.name(), |
||||
other |
||||
); |
||||
self.imp.finish(&mut self.context); |
||||
Stopped |
||||
} |
||||
}; |
||||
} |
||||
|
||||
fn cancel(&mut self) { |
||||
use self::CommandStatus::*; |
||||
self.context.status = match self.context.status { |
||||
Running => Cancelling, |
||||
other => other, |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
macro_rules! command_impl { |
||||
( |
||||
$command:ident<$tconfig:ty>; |
||||
$slf:ident => $init:block |
||||
) => { |
||||
command_impl! { $command<$tconfig>; |
||||
$slf, ctx => $init } |
||||
}; |
||||
( |
||||
$command:ident<$tconfig:ty>; |
||||
$slf:ident, $ctx:ident => $init:block |
||||
) => { |
||||
command_impl! { $command<$tconfig,()>; |
||||
$slf, $ctx => $init; { $crate::command::CommandResult::Done }; {} } |
||||
}; |
||||
( |
||||
$command:ident<$tconfig:ty,$tstate:ty>; |
||||
$slf:ident, $ctx:ident => $init:block; |
||||
$step:block; |
||||
$finish:block |
||||
) => { |
||||
impl $crate::command::CommandImp for $tconfig { |
||||
type State = $tstate; |
||||
|
||||
fn init(&$slf, $ctx: &mut $crate::command::CommandContext<Self>) { |
||||
use $crate::command::GetCommandInfo; |
||||
debug!( |
||||
"{}({}) init", |
||||
stringify!($command), |
||||
$ctx.name() |
||||
); |
||||
$init; |
||||
} |
||||
|
||||
fn step(&$slf, $ctx: &mut $crate::command::CommandContext<Self>) -> $crate::command::CommandResult { |
||||
use $crate::command::GetCommandInfo; |
||||
debug!( |
||||
"{}({}) step", |
||||
stringify!($command), |
||||
$ctx.name() |
||||
); |
||||
$step |
||||
} |
||||
|
||||
fn finish(&$slf, $ctx: &mut $crate::command::CommandContext<Self>) { |
||||
use $crate::command::GetCommandInfo; |
||||
debug!( |
||||
"{}({}) finish({:?})", |
||||
stringify!($command), |
||||
$ctx.name(), |
||||
$ctx.status(), |
||||
); |
||||
$finish; |
||||
} |
||||
} |
||||
pub type $command = $crate::command::GenericCommand<$tconfig>; |
||||
}; |
||||
( |
||||
$command:ident<$tconfig:ty,$tstate:ty>; |
||||
$slf:ident => $init:block; |
||||
$step:block; |
||||
$finish:block |
||||
) => { |
||||
command_impl! { $command<$tconfig,$tstate>; |
||||
$init_self, cts => $init; $step; $finish } |
||||
}; |
||||
} |
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
#[macro_use] |
||||
mod macros; |
||||
pub mod command; |
||||
pub mod command_group; |
||||
pub mod generic_command; |
||||
pub mod scheduler; |
||||
|
||||
pub use self::command::{ |
||||
Command, CommandId, CommandInfo, CommandParent, CommandRef, CommandStatus, GetCommandInfo, |
||||
WithCommandInfo, |
||||
}; |
||||
pub use self::command_group::{CommandGroup, CommandGroupConfig}; |
||||
pub use self::generic_command::{CommandContext, CommandImp, CommandResult, GenericCommand}; |
||||
pub use self::scheduler::Scheduler; |
@ -0,0 +1,108 @@
@@ -0,0 +1,108 @@
|
||||
use super::{Command, CommandId, CommandParent, CommandRef, CommandStatus}; |
||||
|
||||
struct SchedulerCommandParent { |
||||
start_queue: Vec<CommandRef>, |
||||
} |
||||
|
||||
impl SchedulerCommandParent { |
||||
fn new() -> Self { |
||||
Self { |
||||
start_queue: Vec::new(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl CommandParent for SchedulerCommandParent { |
||||
fn start_command(&mut self, command: CommandRef) -> bool { |
||||
self.start_queue.push(command); |
||||
true |
||||
} |
||||
} |
||||
|
||||
pub struct Scheduler { |
||||
running_commands: Vec<CommandRef>, |
||||
} |
||||
|
||||
impl Scheduler { |
||||
pub fn new() -> Self { |
||||
Self { |
||||
running_commands: Vec::new(), |
||||
} |
||||
} |
||||
|
||||
fn get_command<T: CommandId>(&self, id: T) -> Option<&CommandRef> { |
||||
let id = id.command_id(); |
||||
for command in &self.running_commands { |
||||
if command.borrow().command_info().command_id() == id { |
||||
return Some(command); |
||||
} |
||||
} |
||||
None |
||||
} |
||||
|
||||
fn get_command_mut<T: CommandId>(&mut self, id: T) -> Option<&mut CommandRef> { |
||||
let id = id.command_id(); |
||||
for command in &mut self.running_commands { |
||||
if command.borrow().command_info().command_id() == id { |
||||
return Some(command); |
||||
} |
||||
} |
||||
None |
||||
} |
||||
|
||||
pub fn start_command(&mut self, command: CommandRef) -> bool { |
||||
let command_id = command.borrow().command_info().command_id(); |
||||
if self.is_running(command_id) { |
||||
false |
||||
} else { |
||||
self.running_commands.push(command); |
||||
true |
||||
} |
||||
} |
||||
|
||||
pub fn is_running<T: CommandId>(&self, id: T) -> bool { |
||||
self.get_command(id).is_some() |
||||
} |
||||
|
||||
pub fn cancel_command<T: CommandId>(&mut self, id: T) -> bool { |
||||
self.get_command_mut(id).map_or(false, |command| { |
||||
command.borrow_mut().cancel(); |
||||
true |
||||
}) |
||||
} |
||||
|
||||
pub fn execute(&mut self) { |
||||
use self::CommandStatus::*; |
||||
let mut parent = SchedulerCommandParent::new(); |
||||
let exec_command = |
||||
|command: &mut Command, parent: &mut CommandParent| match command.execute(parent) { |
||||
Ended | Cancelled => { |
||||
command.reset(); |
||||
true |
||||
} |
||||
Stopped | Running | Cancelling => false, |
||||
}; |
||||
self.running_commands.drain_filter(|command| { |
||||
let mut command = command.borrow_mut(); |
||||
exec_command(&mut *command, &mut parent) |
||||
}); |
||||
while !parent.start_queue.is_empty() { |
||||
let mut new_parent = SchedulerCommandParent::new(); |
||||
{ |
||||
let more_commands = parent.start_queue.into_iter().filter_map(|command_ref| { |
||||
let done = { |
||||
let mut command = command_ref.borrow_mut(); |
||||
exec_command(&mut *command, &mut new_parent) |
||||
}; |
||||
if !done { |
||||
Some(command_ref) |
||||
} else { |
||||
None |
||||
} |
||||
}); |
||||
self.running_commands.extend(more_commands); |
||||
} |
||||
parent = new_parent; |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue