agent_tk/execution/
tst.rs

1//! Support for executing tst.
2
3use futures::future::FutureExt;
4
5use crate::{definitions, Error, Result};
6
7/// Result of executing a TST
8pub type ExecutionResult<'a> = futures::future::BoxFuture<'a, Result<()>>;
9
10//  _____                     _
11// | ____|_  _____  ___ _   _| |_ ___  _ __ ___
12// |  _| \ \/ / _ \/ __| | | | __/ _ \| '__/ __|
13// | |___ >  <  __/ (__| |_| | || (_) | |  \__ \
14// |_____/_/\_\___|\___|\__,_|\__\___/|_|  |___/
15
16/// Trait representing the executors, used for dispatch
17pub trait Executors: Sync
18{
19  /// Execute a sequential action
20  fn execute_seq(&self, tst_node: definitions::tst::Seq) -> ExecutionResult<'_>;
21  /// Execute a concurrent action
22  fn execute_conc(&self, tst_node: definitions::tst::Conc) -> ExecutionResult<'_>;
23  /// Execute a move to action
24  fn execute_move_to(&self, tst_node: definitions::tst::MoveTo) -> ExecutionResult<'_>;
25  /// Execute a search area action
26  fn execute_search_area(&self, tst_node: definitions::tst::SearchArea) -> ExecutionResult<'_>;
27}
28
29//  _____                     _
30// | ____|_  _____  ___ _   _| |_ ___  _ __
31// |  _| \ \/ / _ \/ __| | | | __/ _ \| '__|
32// | |___ >  <  __/ (__| |_| | || (_) | |
33// |_____/_/\_\___|\___|\__,_|\__\___/|_|
34//
35
36/// Executor trait
37pub trait Executor<T>: Send
38{
39  /// Execute the node t
40  fn execute<'b>(&self, executors: &'b impl Executors, t: T) -> ExecutionResult<'b>;
41}
42
43// Dispatch
44
45fn dispatch_execution(
46  executors: &impl Executors,
47  tst_node: definitions::tst::Node,
48) -> ExecutionResult<'_>
49{
50  match tst_node
51  {
52    definitions::tst::Node::Noop(_) => async { Ok(()) }.boxed(),
53    definitions::tst::Node::Seq(seq) => executors.execute_seq(seq),
54    definitions::tst::Node::Conc(conc) => executors.execute_conc(conc),
55    definitions::tst::Node::MoveTo(move_to) => executors.execute_move_to(move_to),
56    definitions::tst::Node::SearchArea(search_area) => executors.execute_search_area(search_area),
57  }
58}
59
60//  ____             _____                     _
61// / ___|  ___  __ _| ____|_  _____  ___ _   _| |_ ___  _ __
62// \___ \ / _ \/ _` |  _| \ \/ / _ \/ __| | | | __/ _ \| '__|
63//  ___) |  __/ (_| | |___ >  <  __/ (__| |_| | || (_) | |
64// |____/ \___|\__, |_____/_/\_\___|\___|\__,_|\__\___/|_|
65//                |_|
66
67/// Sequential Executor
68pub struct SeqExecutor;
69
70impl Executor<definitions::tst::Seq> for SeqExecutor
71{
72  fn execute<'b>(
73    &self,
74    executors: &'b impl Executors,
75    t: definitions::tst::Seq,
76  ) -> ExecutionResult<'b>
77  {
78    async move {
79      for sub_task in t.children.iter()
80      {
81        dispatch_execution(executors, sub_task.to_owned()).await?;
82      }
83      Ok::<(), Error>(())
84    }
85    .boxed()
86  }
87}
88
89//   ____                 _____                     _
90//  / ___|___  _ __   ___| ____|_  _____  ___ _   _| |_ ___  _ __
91// | |   / _ \| '_ \ / __|  _| \ \/ / _ \/ __| | | | __/ _ \| '__|
92// | |__| (_) | | | | (__| |___ >  <  __/ (__| |_| | || (_) | |
93//  \____\___/|_| |_|\___|_____/_/\_\___|\___|\__,_|\__\___/|_|
94//
95
96/// Concurrent Executor
97pub struct ConcExecutor;
98
99impl Executor<definitions::tst::Conc> for ConcExecutor
100{
101  fn execute<'b>(
102    &self,
103    executors: &'b impl Executors,
104    t: definitions::tst::Conc,
105  ) -> ExecutionResult<'b>
106  {
107    async move {
108      let mut futures = vec![];
109      for sub_task in t.children.iter()
110      {
111        futures.push(dispatch_execution(executors, sub_task.to_owned()));
112      }
113      futures::future::try_join_all(futures).await?;
114      Ok::<(), Error>(())
115    }
116    .boxed()
117  }
118}
119
120//  _   _       _____                     _
121// | \ | | ___ | ____|_  _____  ___ _   _| |_ ___  _ __
122// |  \| |/ _ \|  _| \ \/ / _ \/ __| | | | __/ _ \| '__|
123// | |\  | (_) | |___ >  <  __/ (__| |_| | || (_) | |
124// |_| \_|\___/|_____/_/\_\___|\___|\__,_|\__\___/|_|
125//
126
127/// No Executor, it can be used for agent that don't implement some of the executors
128pub struct NoExecutor<T: Send>
129{
130  ghost: std::marker::PhantomData<T>,
131}
132
133impl<T: Send> Executor<T> for NoExecutor<T>
134{
135  fn execute<'b>(&self, _executors: &'b impl Executors, _t: T) -> ExecutionResult<'b>
136  {
137    async move { Err::<(), Error>(Error::NoExecutor()) }.boxed()
138  }
139}
140
141//     _                    _
142//    / \   __ _  ___ _ __ | |_
143//   / _ \ / _` |/ _ \ '_ \| __|
144//  / ___ \ (_| |  __/ | | | |_
145// /_/   \_\__, |\___|_| |_|\__|
146//         |___/
147
148/// Agent trait for TST execution
149pub trait Agent {}
150
151//   ____                _        _     _      _____                       _                    _
152//  / ___|_ __ ___  __ _| |_ __ _| |__ | | ___|  ___| __ ___  _ __ ___    / \   __ _  ___ _ __ | |_
153// | |   | '__/ _ \/ _` | __/ _` | '_ \| |/ _ \ |_ | '__/ _ \| '_ ` _ \  / _ \ / _` |/ _ \ '_ \| __|
154// | |___| | |  __/ (_| | || (_| | |_) | |  __/  _|| | | (_) | | | | | |/ ___ \ (_| |  __/ | | | |_
155//  \____|_|  \___|\__,_|\__\__,_|_.__/|_|\___|_|  |_|  \___/|_| |_| |_/_/   \_\__, |\___|_| |_|\__|
156//                                                                             |___/
157
158/// Trait for creating executor for an agent
159pub trait CreatableFromAgent<TAgent, T>
160{
161  /// Create an executor from an agent
162  fn from_agent(agent: &TAgent) -> T;
163}
164
165impl<TAgent> CreatableFromAgent<TAgent, SeqExecutor> for SeqExecutor
166{
167  fn from_agent(_agent: &TAgent) -> SeqExecutor
168  {
169    SeqExecutor {}
170  }
171}
172impl<TAgent> CreatableFromAgent<TAgent, ConcExecutor> for ConcExecutor
173{
174  fn from_agent(_agent: &TAgent) -> ConcExecutor
175  {
176    ConcExecutor {}
177  }
178}
179
180impl<TAgent, T: Send> CreatableFromAgent<TAgent, NoExecutor<T>> for NoExecutor<T>
181{
182  fn from_agent(_agent: &TAgent) -> NoExecutor<T>
183  {
184    NoExecutor {
185      ghost: Default::default(),
186    }
187  }
188}
189
190//  _____              _____                     _            _____          _ _
191// |_   _| __ ___  ___| ____|_  _____  ___ _   _| |_ ___  _ _|_   _| __ __ _(_) |_
192//   | || '__/ _ \/ _ \  _| \ \/ / _ \/ __| | | | __/ _ \| '__|| || '__/ _` | | __|
193//   | || | |  __/  __/ |___ >  <  __/ (__| |_| | || (_) | |   | || | | (_| | | |_
194//   |_||_|  \___|\___|_____/_/\_\___|\___|\__,_|\__\___/|_|   |_||_|  \__,_|_|\__|
195
196/// Base trait for the TreeExecutor
197pub trait TreeExecutorTrait
198{
199  /// Executor for sequential nodes
200  type SeqExecutor: Executor<definitions::tst::Seq>;
201  /// Executor for concurrent nodes
202  type ConcExecutor: Executor<definitions::tst::Conc>;
203  /// Executor for move_to nodes
204  type MoveToExecutor: Executor<definitions::tst::MoveTo>;
205  /// Executor for search area nodes
206  type SearchAreaExecutor: Executor<definitions::tst::SearchArea>;
207
208  /// Create a new executor for the given agent
209  fn from_agent<TAgent>(agent: &TAgent) -> Self
210  where
211    Self::SeqExecutor: CreatableFromAgent<TAgent, Self::SeqExecutor>,
212    Self::ConcExecutor: CreatableFromAgent<TAgent, Self::ConcExecutor>,
213    Self::MoveToExecutor: CreatableFromAgent<TAgent, Self::MoveToExecutor>,
214    Self::SearchAreaExecutor: CreatableFromAgent<TAgent, Self::SearchAreaExecutor>;
215  /// Execute the tst
216  fn execute(&self, t: definitions::tst::Node) -> ExecutionResult<'_>;
217}
218
219//  _____              _____                     _
220// |_   _| __ ___  ___| ____|_  _____  ___ _   _| |_ ___  _ __
221//   | || '__/ _ \/ _ \  _| \ \/ / _ \/ __| | | | __/ _ \| '__|
222//   | || | |  __/  __/ |___ >  <  __/ (__| |_| | || (_) | |
223//   |_||_|  \___|\___|_____/_/\_\___|\___|\__,_|\__\___/|_|
224
225/// Executor of TST for an agent
226pub struct TreeExecutor<
227  TSeqExecutor: Executor<definitions::tst::Seq>,
228  TConcExecutor: Executor<definitions::tst::Conc>,
229  TMoveToExecutor: Executor<definitions::tst::MoveTo>,
230  TSearchAreaExecutor: Executor<definitions::tst::SearchArea>,
231> {
232  seq_executor: ccutils::sync::ArcMutex<TSeqExecutor>,
233  conc_executor: ccutils::sync::ArcMutex<TConcExecutor>,
234  move_to_executor: ccutils::sync::ArcMutex<TMoveToExecutor>,
235  search_area_executor: ccutils::sync::ArcMutex<TSearchAreaExecutor>,
236}
237
238impl<
239    TSeqExecutor: Executor<definitions::tst::Seq>,
240    TConcExecutor: Executor<definitions::tst::Conc>,
241    TMoveToExecutor: Executor<definitions::tst::MoveTo>,
242    TSearchAreaExecutor: Executor<definitions::tst::SearchArea>,
243  > TreeExecutorTrait
244  for TreeExecutor<TSeqExecutor, TConcExecutor, TMoveToExecutor, TSearchAreaExecutor>
245{
246  type SeqExecutor = TSeqExecutor;
247  type ConcExecutor = TConcExecutor;
248  type MoveToExecutor = TMoveToExecutor;
249  type SearchAreaExecutor = TSearchAreaExecutor;
250  fn from_agent<TAgent>(agent: &TAgent) -> Self
251  where
252    TSeqExecutor: CreatableFromAgent<TAgent, TSeqExecutor>,
253    TConcExecutor: CreatableFromAgent<TAgent, TConcExecutor>,
254    TMoveToExecutor: CreatableFromAgent<TAgent, TMoveToExecutor>,
255    TSearchAreaExecutor: CreatableFromAgent<TAgent, TSearchAreaExecutor>,
256  {
257    Self {
258      seq_executor: TSeqExecutor::from_agent(agent).into(),
259      conc_executor: TConcExecutor::from_agent(agent).into(),
260      move_to_executor: TMoveToExecutor::from_agent(agent).into(),
261      search_area_executor: TSearchAreaExecutor::from_agent(agent).into(),
262    }
263  }
264  fn execute(&self, t: definitions::tst::Node) -> ExecutionResult<'_>
265  {
266    dispatch_execution(self, t)
267  }
268}
269
270impl<
271    TSeqExecutor: Executor<definitions::tst::Seq>,
272    TConcExecutor: Executor<definitions::tst::Conc>,
273    TMoveToExecutor: Executor<definitions::tst::MoveTo>,
274    TSearchAreaExecutor: Executor<definitions::tst::SearchArea>,
275  > Executors for TreeExecutor<TSeqExecutor, TConcExecutor, TMoveToExecutor, TSearchAreaExecutor>
276{
277  fn execute_seq(&self, tst_node: definitions::tst::Seq) -> ExecutionResult<'_>
278  {
279    self.seq_executor.lock().unwrap().execute(self, tst_node)
280  }
281  fn execute_conc(&self, tst_node: definitions::tst::Conc) -> ExecutionResult<'_>
282  {
283    self.conc_executor.lock().unwrap().execute(self, tst_node)
284  }
285  fn execute_move_to(&self, tst_node: definitions::tst::MoveTo) -> ExecutionResult<'_>
286  {
287    self
288      .move_to_executor
289      .lock()
290      .unwrap()
291      .execute(self, tst_node)
292  }
293  fn execute_search_area(&self, tst_node: definitions::tst::SearchArea) -> ExecutionResult<'_>
294  {
295    self
296      .search_area_executor
297      .lock()
298      .unwrap()
299      .execute(self, tst_node)
300  }
301}
302
303/// Agent executor with default for Sequence and Concurrent
304pub type DefaultTreeExecutor<TMoveToExecutor, TSearchAreaExecutor> =
305  TreeExecutor<SeqExecutor, ConcExecutor, TMoveToExecutor, TSearchAreaExecutor>;
306
307#[cfg(test)]
308mod tests
309{
310  struct FakeAgentPosition
311  {
312    longitude: f64,
313    latitude: f64,
314    altitude: f64,
315  }
316  struct FakeAgent
317  {
318    position: ccutils::sync::ArcMutex<FakeAgentPosition>,
319  }
320  impl Agent for FakeAgent {}
321  struct FakeAgentMoveToExecutor
322  {
323    position: ccutils::sync::ArcMutex<FakeAgentPosition>,
324  }
325  impl CreatableFromAgent<FakeAgent, FakeAgentMoveToExecutor> for FakeAgentMoveToExecutor
326  {
327    fn from_agent(_agent: &FakeAgent) -> FakeAgentMoveToExecutor
328    {
329      FakeAgentMoveToExecutor {
330        position: _agent.position.clone(),
331      }
332    }
333  }
334  impl Executor<definitions::tst::MoveTo> for FakeAgentMoveToExecutor
335  {
336    fn execute<'b>(
337      &self,
338      _executors: &'b impl Executors,
339      t: definitions::tst::MoveTo,
340    ) -> ExecutionResult<'b>
341    {
342      let pos = self.position.clone();
343      async move {
344        let mut p = pos.lock().unwrap();
345        p.longitude = t.params.waypoint.longitude;
346        p.latitude = t.params.waypoint.latitude;
347        p.altitude = t.params.waypoint.altitude;
348        Ok::<(), Error>(())
349      }
350      .boxed()
351    }
352  }
353  use super::*;
354
355  #[test]
356  fn execute()
357  {
358    let node: definitions::tst::Node = definitions::tst::Seq::build(None)
359      .add(
360        definitions::tst::MoveToParameters {
361          waypoint: definitions::tst::GeoPoint {
362            longitude: 16.4,
363            latitude: 59.3,
364            altitude: 110.8,
365          },
366          ..Default::default()
367        },
368        None,
369      )
370      .add(
371        definitions::tst::MoveToParameters {
372          waypoint: definitions::tst::GeoPoint {
373            longitude: 16.5,
374            latitude: 59.5,
375            altitude: 110.7,
376          },
377          ..Default::default()
378        },
379        None,
380      )
381      .into();
382
383    let agent = FakeAgent {
384      position: FakeAgentPosition {
385        longitude: 0.0,
386        latitude: 0.0,
387        altitude: 0.0,
388      }
389      .into(),
390    };
391    let executor = TreeExecutor::<
392      SeqExecutor,
393      ConcExecutor,
394      FakeAgentMoveToExecutor,
395      NoExecutor<definitions::tst::SearchArea>,
396    >::from_agent(&agent);
397    let r = executor.execute(node);
398    let res = futures::executor::block_on(r);
399    assert!(res.is_ok());
400    let p = agent.position.lock().unwrap();
401    assert_eq!(p.longitude, 16.5);
402    assert_eq!(p.latitude, 59.5);
403    assert_eq!(p.altitude, 110.7);
404  }
405}