behaviortree_core/behaviors/
parallel.rs1use alloc::{boxed::Box, collections::btree_set::BTreeSet};
5
6use behaviortree_derive::Control;
7use dataport::{PortArray, PortCollectionAccessors, create_inbound_entry_parseable};
8use tinyscript::SharedRuntime;
9
10use crate::{
11 self as behaviortree_core, BehaviorResult, port_array_init,
12 tree::BehaviorTreeElementList,
13 {behavior_data::BehaviorData, behavior_state::BehaviorState, error::Error},
14};
15
16#[derive(Control, Debug)]
36#[behavior(groot2)]
37pub struct Parallel {
38 success_count: i32,
40 failure_count: i32,
42 completed_list: BTreeSet<usize>,
44 #[behavior(portlist)]
46 portlist: PortArray<2>,
47}
48
49const SUCCESS_COUNT: &str = "success_count";
51const FAILURE_COUNT: &str = "failure_count";
52
53#[async_trait::async_trait]
54impl crate::behavior_traits::Behavior for Parallel {
55 fn on_halt(&mut self) -> Result<(), Error> {
56 self.completed_list.clear();
57 self.success_count = 0;
58 self.failure_count = 0;
59 Ok(())
60 }
61
62 #[allow(clippy::cast_possible_truncation)]
63 #[allow(clippy::cast_possible_wrap)]
64 fn on_start(
65 &mut self,
66 behavior: &mut BehaviorData,
67 children: &mut BehaviorTreeElementList,
68 _runtime: &SharedRuntime,
69 ) -> Result<(), Error> {
70 let children_count = children.len();
72 let success_threshold = self.portlist.get(SUCCESS_COUNT).unwrap_or(-1);
75
76 if (children_count as i32) < success_threshold {
77 return Err(Error::Composition {
78 txt: "Number of children is less than success threshold. Can never succeed.".into(),
79 });
80 }
81
82 let failure_threshold = self.portlist.get(FAILURE_COUNT).unwrap_or(-1);
85
86 if (children_count as i32) < failure_threshold {
87 return Err(Error::Composition {
88 txt: "Number of children is less than failure threshold. Can never fail.".into(),
89 });
90 }
91 behavior.set_state(BehaviorState::Running);
92 Ok(())
93 }
94
95 #[allow(clippy::cast_possible_truncation)]
96 #[allow(clippy::cast_possible_wrap)]
97 #[allow(clippy::set_contains_or_insert)]
98 async fn tick(
99 &mut self,
100 _behavior: &mut BehaviorData,
101 children: &mut BehaviorTreeElementList,
102 runtime: &SharedRuntime,
103 ) -> BehaviorResult {
104 let success_threshold = self.portlist.get(SUCCESS_COUNT).unwrap_or(-1);
107 let failure_threshold = self.portlist.get(FAILURE_COUNT).unwrap_or(-1);
110
111 let children_count = children.len();
112 let mut skipped_count = 0;
113
114 for i in 0..children_count {
115 if !self.completed_list.contains(&i) {
117 let child = &mut children[i];
118 match child.tick(runtime).await? {
119 BehaviorState::Failure => {
120 self.completed_list.insert(i);
121 self.failure_count += 1;
122 }
123 BehaviorState::Idle => {
125 return Err(Error::State {
126 behavior: "Parallel".into(),
127 state: BehaviorState::Idle,
128 });
129 }
130 BehaviorState::Running => {}
131 BehaviorState::Skipped => skipped_count += 1,
132 BehaviorState::Success => {
133 self.completed_list.insert(i);
134 self.success_count += 1;
135 }
136 }
137 }
138
139 let sum = self.failure_count + self.success_count + skipped_count;
140 if sum >= children_count as i32 {
141 let state = if skipped_count == children_count as i32 {
142 BehaviorState::Skipped
143 } else if failure_threshold <= 0 && success_threshold <= 0 {
144 BehaviorState::Success
145 } else if failure_threshold <= 0 {
146 if self.success_count >= success_threshold {
147 BehaviorState::Success
148 } else {
149 BehaviorState::Failure
150 }
151 } else if (self.failure_count > failure_threshold) || (self.success_count < success_threshold) {
152 BehaviorState::Failure
153 } else {
154 BehaviorState::Success
155 };
156
157 self.completed_list.clear();
158 self.success_count = 0;
159 self.failure_count = 0;
160 children.halt(runtime)?;
161
162 return Ok(state);
163 }
164 }
165
166 if skipped_count == children_count as i32 {
167 return Ok(BehaviorState::Skipped);
168 }
169
170 let sum = skipped_count + self.completed_list.len() as i32;
171 if sum >= children_count as i32 {
172 let state = if (failure_threshold >= 0) && (self.failure_count > failure_threshold) {
173 BehaviorState::Failure
174 } else {
175 BehaviorState::Success
176 };
177
178 children.halt(runtime)?;
180 self.completed_list.clear();
181
182 return Ok(state);
183 }
184
185 Ok(BehaviorState::Running)
186 }
187}
188
189impl Parallel {
190 port_array_init! {
191 2,
192 create_inbound_entry_parseable!(SUCCESS_COUNT, i32, -1),
193 create_inbound_entry_parseable!(FAILURE_COUNT, i32, -1),
194 }
195}