|
|
@ -73,12 +73,17 @@ pub enum CommandStatus { |
|
|
|
Cancelled, |
|
|
|
Cancelled, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub trait CommandParent { |
|
|
|
|
|
|
|
fn start_command(&mut self, cmd: CommandRef) -> bool; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub struct CommandContext<T> |
|
|
|
pub struct CommandContext<T> |
|
|
|
where |
|
|
|
where |
|
|
|
T: CommandImp, |
|
|
|
T: CommandImp, |
|
|
|
{ |
|
|
|
{ |
|
|
|
info: CommandInfo, |
|
|
|
info: CommandInfo, |
|
|
|
status: CommandStatus, |
|
|
|
status: CommandStatus, |
|
|
|
|
|
|
|
parent: Option<*mut CommandParent>, |
|
|
|
state: <T as CommandImp>::State, |
|
|
|
state: <T as CommandImp>::State, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -90,6 +95,16 @@ impl<T: CommandImp> CommandContext<T> { |
|
|
|
pub fn state(&mut self) -> &mut T::State { |
|
|
|
pub fn state(&mut self) -> &mut T::State { |
|
|
|
&mut self.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> { |
|
|
|
impl<T: CommandImp> WithCommandInfo for CommandContext<T> { |
|
|
@ -115,13 +130,13 @@ pub trait CommandImp { |
|
|
|
pub trait Command: WithCommandInfo { |
|
|
|
pub trait Command: WithCommandInfo { |
|
|
|
fn reset(&mut self); |
|
|
|
fn reset(&mut self); |
|
|
|
fn status(&self) -> CommandStatus; |
|
|
|
fn status(&self) -> CommandStatus; |
|
|
|
fn execute(&mut self) -> CommandStatus; |
|
|
|
fn execute(&mut self, &mut CommandParent) -> CommandStatus; |
|
|
|
fn cancel(&mut self); |
|
|
|
fn cancel(&mut self); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub struct GenericCommand<T: CommandImp> { |
|
|
|
pub struct GenericCommand<T: CommandImp> { |
|
|
|
context: CommandContext<T>, |
|
|
|
context: CommandContext<T>, |
|
|
|
imp: T, |
|
|
|
pub imp: T, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl<T: CommandImp + 'static> GenericCommand<T> { |
|
|
|
impl<T: CommandImp + 'static> GenericCommand<T> { |
|
|
@ -130,13 +145,14 @@ impl<T: CommandImp + 'static> GenericCommand<T> { |
|
|
|
context: CommandContext { |
|
|
|
context: CommandContext { |
|
|
|
info, |
|
|
|
info, |
|
|
|
status: CommandStatus::Stopped, |
|
|
|
status: CommandStatus::Stopped, |
|
|
|
|
|
|
|
parent: None, |
|
|
|
state: T::State::default(), |
|
|
|
state: T::State::default(), |
|
|
|
}, |
|
|
|
}, |
|
|
|
imp, |
|
|
|
imp, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn new_ref(info: CommandInfo, imp: T) -> CommandRef { |
|
|
|
pub fn new_ref(info: CommandInfo, imp: T) -> CommandRef<Self> { |
|
|
|
Rc::new(RefCell::new(Self::new(info, imp))) |
|
|
|
Rc::new(RefCell::new(Self::new(info, imp))) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -152,9 +168,11 @@ impl<T: CommandImp> Command for GenericCommand<T> { |
|
|
|
self.context.status |
|
|
|
self.context.status |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn execute(&mut self) -> CommandStatus { |
|
|
|
fn execute(&mut self, parent: &mut CommandParent) -> CommandStatus { |
|
|
|
use self::CommandResult::*; |
|
|
|
use self::CommandResult::*; |
|
|
|
use self::CommandStatus::*; |
|
|
|
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| { |
|
|
|
let do_cancel = |me: &mut Self| { |
|
|
|
me.context.status = Cancelling; |
|
|
|
me.context.status = Cancelling; |
|
|
|
me.imp.finish(&mut me.context); |
|
|
|
me.imp.finish(&mut me.context); |
|
|
@ -182,14 +200,23 @@ impl<T: CommandImp> Command for GenericCommand<T> { |
|
|
|
Cancelled => Cancelled, |
|
|
|
Cancelled => Cancelled, |
|
|
|
}; |
|
|
|
}; |
|
|
|
self.context.status = new_status; |
|
|
|
self.context.status = new_status; |
|
|
|
|
|
|
|
self.context.parent = None; |
|
|
|
new_status |
|
|
|
new_status |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn reset(&mut self) { |
|
|
|
fn reset(&mut self) { |
|
|
|
use self::CommandStatus::*; |
|
|
|
use self::CommandStatus::*; |
|
|
|
self.context.status = match self.context.status { |
|
|
|
self.context.status = match self.context.status { |
|
|
|
Ended | Cancelled => Stopped, |
|
|
|
Stopped | Ended | Cancelled => Stopped, |
|
|
|
other => other, |
|
|
|
other => { |
|
|
|
|
|
|
|
warn!( |
|
|
|
|
|
|
|
"GenericCommand({}) reset called in non-resetable status {:?}", |
|
|
|
|
|
|
|
self.name(), |
|
|
|
|
|
|
|
other |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
self.imp.finish(&mut self.context); |
|
|
|
|
|
|
|
Stopped |
|
|
|
|
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -203,6 +230,20 @@ impl<T: CommandImp> Command for GenericCommand<T> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
macro_rules! command_impl { |
|
|
|
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>; |
|
|
|
$command:ident<$tconfig:ty,$tstate:ty>; |
|
|
|
$slf:ident, $ctx:ident => $init:block; |
|
|
|
$slf:ident, $ctx:ident => $init:block; |
|
|
@ -254,16 +295,10 @@ macro_rules! command_impl { |
|
|
|
command_impl! { $command<$tconfig,$tstate>; |
|
|
|
command_impl! { $command<$tconfig,$tstate>; |
|
|
|
$init_self, cts => $init; $step; $finish } |
|
|
|
$init_self, cts => $init; $step; $finish } |
|
|
|
}; |
|
|
|
}; |
|
|
|
( |
|
|
|
|
|
|
|
$command:ident<$tconfig:ty>; |
|
|
|
|
|
|
|
$slf:ident => $init:block |
|
|
|
|
|
|
|
) => { |
|
|
|
|
|
|
|
command_impl! { $command<$tconfig,()>; |
|
|
|
|
|
|
|
$slf, ctx => $init; { $crate::command::CommandResult::Done }; {} } |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub type CommandRef = Rc<RefCell<dyn Command>>; |
|
|
|
pub type CommandRef<T = dyn Command> = Rc<RefCell<T>>; |
|
|
|
|
|
|
|
|
|
|
|
pub struct CommandGroupConfig { |
|
|
|
pub struct CommandGroupConfig { |
|
|
|
pub commands: Vec<CommandRef>, |
|
|
|
pub commands: Vec<CommandRef>, |
|
|
@ -302,28 +337,26 @@ impl CommandImp for CommandGroupConfig { |
|
|
|
fn step(&self, ctx: &mut CommandContext<Self>) -> CommandResult { |
|
|
|
fn step(&self, ctx: &mut CommandContext<Self>) -> CommandResult { |
|
|
|
use self::CommandResult::*; |
|
|
|
use self::CommandResult::*; |
|
|
|
use self::CommandStatus::*; |
|
|
|
use self::CommandStatus::*; |
|
|
|
let state = ctx.state(); |
|
|
|
|
|
|
|
let commands = &self.commands; |
|
|
|
let commands = &self.commands; |
|
|
|
if state.has_cancelled { |
|
|
|
if ctx.state().has_cancelled { |
|
|
|
debug!("CommandGroup step called after cancelled"); |
|
|
|
debug!("CommandGroup step called after cancelled"); |
|
|
|
return CommandResult::Cancel; |
|
|
|
return CommandResult::Cancel; |
|
|
|
} |
|
|
|
} |
|
|
|
let current_idx = &mut state.current_idx; |
|
|
|
|
|
|
|
loop { |
|
|
|
loop { |
|
|
|
if *current_idx >= commands.len() { |
|
|
|
if ctx.state().current_idx >= commands.len() { |
|
|
|
debug!("CommandGroup out of commands"); |
|
|
|
debug!("CommandGroup out of commands"); |
|
|
|
return CommandResult::Done; |
|
|
|
return CommandResult::Done; |
|
|
|
} |
|
|
|
} |
|
|
|
let mut command = commands[*current_idx].borrow_mut(); |
|
|
|
let mut command = commands[ctx.state().current_idx].borrow_mut(); |
|
|
|
let status = command.execute(); |
|
|
|
let status = command.execute(ctx.parent()); |
|
|
|
match status { |
|
|
|
match status { |
|
|
|
Ended => { |
|
|
|
Ended => { |
|
|
|
command.reset(); |
|
|
|
command.reset(); |
|
|
|
*current_idx += 1; |
|
|
|
ctx.state().current_idx += 1; |
|
|
|
} |
|
|
|
} |
|
|
|
Cancelled => { |
|
|
|
Cancelled => { |
|
|
|
command.reset(); |
|
|
|
command.reset(); |
|
|
|
state.has_cancelled = true; |
|
|
|
ctx.state().has_cancelled = true; |
|
|
|
return Cancel; |
|
|
|
return Cancel; |
|
|
|
} |
|
|
|
} |
|
|
|
Running => return Continue, |
|
|
|
Running => return Continue, |
|
|
@ -341,89 +374,116 @@ impl CommandImp for CommandGroupConfig { |
|
|
|
if let Some(command) = commands.get(ctx.state().current_idx) { |
|
|
|
if let Some(command) = commands.get(ctx.state().current_idx) { |
|
|
|
let mut command = command.borrow_mut(); |
|
|
|
let mut command = command.borrow_mut(); |
|
|
|
command.cancel(); |
|
|
|
command.cancel(); |
|
|
|
command.execute(); |
|
|
|
command.execute(ctx.parent()); |
|
|
|
command.reset(); |
|
|
|
command.reset(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub struct CommandRun { |
|
|
|
struct SchedulerCommandParent { |
|
|
|
command: CommandRef, |
|
|
|
start_queue: Vec<CommandRef>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl CommandId for CommandRun { |
|
|
|
impl SchedulerCommandParent { |
|
|
|
fn command_id(&self) -> u64 { |
|
|
|
fn new() -> Self { |
|
|
|
self.command.borrow().command_info().command_id() |
|
|
|
Self { |
|
|
|
|
|
|
|
start_queue: Vec::new(), |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub struct CommandScheduler { |
|
|
|
impl CommandParent for SchedulerCommandParent { |
|
|
|
running_commands: Vec<CommandRun>, |
|
|
|
fn start_command(&mut self, command: CommandRef) -> bool { |
|
|
|
|
|
|
|
self.start_queue.push(command); |
|
|
|
|
|
|
|
true |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl CommandScheduler { |
|
|
|
pub struct Scheduler { |
|
|
|
|
|
|
|
running_commands: Vec<CommandRef>, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Scheduler { |
|
|
|
pub fn new() -> Self { |
|
|
|
pub fn new() -> Self { |
|
|
|
CommandScheduler { |
|
|
|
Self { |
|
|
|
running_commands: Vec::new(), |
|
|
|
running_commands: Vec::new(), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn get_command_run<T: CommandId>(&self, id: T) -> Option<&CommandRun> { |
|
|
|
fn get_command<T: CommandId>(&self, id: T) -> Option<&CommandRef> { |
|
|
|
let id = id.command_id(); |
|
|
|
let id = id.command_id(); |
|
|
|
for command_run in &self.running_commands { |
|
|
|
for command in &self.running_commands { |
|
|
|
if command_run.command_id() == id { |
|
|
|
if command.borrow().command_info().command_id() == id { |
|
|
|
return Some(command_run); |
|
|
|
return Some(command); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
None |
|
|
|
None |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn get_command_run_mut<T: CommandId>(&mut self, id: T) -> Option<&mut CommandRun> { |
|
|
|
fn get_command_mut<T: CommandId>(&mut self, id: T) -> Option<&mut CommandRef> { |
|
|
|
let id = id.command_id(); |
|
|
|
let id = id.command_id(); |
|
|
|
for command_run in &mut self.running_commands { |
|
|
|
for command in &mut self.running_commands { |
|
|
|
if command_run.command_id() == id { |
|
|
|
if command.borrow().command_info().command_id() == id { |
|
|
|
return Some(command_run); |
|
|
|
return Some(command); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
None |
|
|
|
None |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn is_running<T: CommandId>(&self, id: T) -> bool { |
|
|
|
pub fn start_command(&mut self, command: CommandRef) -> bool { |
|
|
|
self.get_command_run(id).is_some() |
|
|
|
let command_id = command.borrow().command_info().command_id(); |
|
|
|
} |
|
|
|
if self.is_running(command_id) { |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Starts running a command. |
|
|
|
|
|
|
|
* Returns true if the command was started, false if it was not started because it was already running |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
pub fn start(&mut self, command: CommandRef) -> bool { |
|
|
|
|
|
|
|
if self.is_running(command.borrow().command_info().command_id()) { |
|
|
|
|
|
|
|
false |
|
|
|
false |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
self.running_commands.push(CommandRun { command }); |
|
|
|
self.running_commands.push(command); |
|
|
|
true |
|
|
|
true |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn cancel<T: CommandId>(&mut self, id: T) -> bool { |
|
|
|
pub fn is_running<T: CommandId>(&self, id: T) -> bool { |
|
|
|
self.get_command_run_mut(id).map_or(false, |command_run| { |
|
|
|
self.get_command(id).is_some() |
|
|
|
command_run.command.borrow_mut().cancel(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
true |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn execute(&mut self) { |
|
|
|
pub fn execute(&mut self) { |
|
|
|
use self::CommandStatus::*; |
|
|
|
use self::CommandStatus::*; |
|
|
|
self.running_commands.drain_filter(|command_run| { |
|
|
|
let mut parent = SchedulerCommandParent::new(); |
|
|
|
let mut command = command_run.command.borrow_mut(); |
|
|
|
let exec_command = |
|
|
|
match command.execute() { |
|
|
|
|command: &mut Command, parent: &mut CommandParent| match command.execute(parent) { |
|
|
|
Ended | Cancelled => { |
|
|
|
Ended | Cancelled => { |
|
|
|
command.reset(); |
|
|
|
command.reset(); |
|
|
|
true |
|
|
|
true |
|
|
|
}, |
|
|
|
} |
|
|
|
Stopped | Running | Cancelling => false, |
|
|
|
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; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|