forked from Sollimann/bonsai
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.rs
More file actions
110 lines (98 loc) · 3.42 KB
/
Copy pathmain.rs
File metadata and controls
110 lines (98 loc) · 3.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use bonsai_bt::Behavior::Wait;
use bonsai_bt::{
Behavior::Action, Behavior::Race, Behavior::Sequence, Event, Float, Status, Timer, UpdateArgs, BT, RUNNING,
};
use futures::FutureExt;
use rand::Rng;
use std::collections::HashMap;
use std::sync::mpsc::{channel, Receiver};
use std::thread::sleep;
use std::time::Duration;
use tokio::time::sleep as async_sleep;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub enum MissionAction {
/// The main job that finishes after a random delay
DoWork,
/// Hard deadline that fires after a fixed delay
OnTimeout,
}
pub struct MissionState {
pub work: Option<Receiver<Status>>,
}
/// Simulates a unit of work whose duration is random.
/// Sometimes it finishes before the timeout and sometimes it doesn't.
async fn do_work_task(tx: std::sync::mpsc::Sender<Status>) {
let work_ms: u64 = rand::thread_rng().gen_range(200..=1200);
println!("[do_work] started.");
let step = Duration::from_millis(100);
let mut elapsed = 0u64;
while elapsed < work_ms {
if tx.send(Status::Running).is_err() {
println!("[do_work] preempted by timeout, stopping.");
return;
}
async_sleep(step).await;
elapsed += step.as_millis() as u64;
}
println!("[do_work] finished after {elapsed} ms");
let _ = tx.send(Status::Success);
}
async fn tick(
timer: &mut Timer,
state: &mut MissionState,
bt: &mut BT<MissionAction, HashMap<String, serde_json::Value>>,
) -> std::option::Option<(Status, Float)> {
let dt: Float = timer.get_dt();
let e: Event = UpdateArgs { dt }.into();
bt.tick(
&e,
&mut |args: bonsai_bt::ActionArgs<Event, MissionAction>, _| match *args.action {
MissionAction::DoWork => {
if let Some(rx) = &state.work {
match rx.recv() {
Ok(Status::Running) => RUNNING,
Ok(Status::Success) => {
state.work = None;
(Status::Success, args.dt)
}
Ok(Status::Failure) | Err(_) => {
state.work = None;
(Status::Failure, args.dt)
}
}
} else {
let (tx, rx) = channel();
let (job, handle) = do_work_task(tx).remote_handle();
handle.forget();
tokio::spawn(job);
state.work = Some(rx);
match state.work.as_ref().unwrap().recv().unwrap() {
Status::Running => RUNNING,
s => (s, args.dt),
}
}
}
MissionAction::OnTimeout => {
eprintln!("do_work timed out!");
(Status::Failure, args.dt)
}
},
)
}
#[tokio::main]
async fn main() {
const TIMEOUT_S: Float = 0.6;
let behavior = Sequence(vec![Race(vec![
Action(MissionAction::DoWork),
Sequence(vec![Wait(TIMEOUT_S), Action(MissionAction::OnTimeout)]),
])]);
let mut bt = BT::new(behavior, HashMap::new());
let mut timer = Timer::init_time();
let mut state = MissionState { work: None };
loop {
sleep(Duration::from_millis(50));
if tick(&mut timer, &mut state, &mut bt).await.is_none() {
break;
}
}
}