Skip to content

Commit

Permalink
Merge pull request #67 from samouwow/non-reactive-handling-of-running…
Browse files Browse the repository at this point in the history
…-state

Non reactive handling of running state
  • Loading branch information
besok authored Jun 4, 2024
2 parents a75cc8e + 84a032d commit 9ed0b03
Show file tree
Hide file tree
Showing 16 changed files with 503 additions and 153 deletions.
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description = "Workflow framework based on the behavior trees"
authors = ["BorisZhguchev <[email protected]>"]
homepage = "https://github.com/besok/forester"
repository = "https://github.com/besok/forester"
version = "0.3.1"
version = "0.3.2"
edition = "2021"
license-file = "LICENSE"

Expand Down Expand Up @@ -34,4 +34,5 @@ url = "2.4.1"

[dev-dependencies]
wiremock = "0.6.0"
forester-http = "0.1.0"
forester-http = "0.1.0"

37 changes: 20 additions & 17 deletions docs/src/seq.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ Otherwise, (when a child returns `failure` a sequence aborted)
In the language, the tree definitions and lambda invocations of this element are marked with the key word `sequence`.

```f-tree
impl store(key:string, value:string); // store a string value to a key in blackboard
impl store(key:string, value:string); // store a string value to a key in blackboard
root main {
sequence {
store("a","1") // first tick and proceed if succeded
store("b","2") // sec tick and proceed if succeded
store("c","3") // thrd tick and finish if succeded
store("a","1") // first tick and proceed if succeded
store("b","2") // sec tick and proceed if succeded
store("c","3") // thrd tick and finish if succeded
}
}
```
Expand All @@ -23,11 +23,11 @@ with a graph representation
strict digraph {
1[label="root
main ",shape=rect,color=black]
1 -> 2
1 -> 2
2[label="sequence",shape=rect,color=darkred]
2 -> 3
2 -> 4
2 -> 5
2 -> 3
2 -> 4
2 -> 5
3[label="store (key=a,default=1)",shape=component,color=green]
4[label="store (key=b,default=2)",shape=component,color=green]
5[label="store (key=c,default=3)",shape=component,color=green]
Expand All @@ -45,12 +45,12 @@ main ",shape=rect,color=black]
## Intention
Often, it is used as a straight chain of instructions
```f-tree
// if the definition has only one child
// if the definition has only one child
// (root has only one child) '{''}' can be omitted
root main sequence {
validate_env()
perform_action()
finish_and_save()
validate_env()
perform_action()
finish_and_save()
}
```
Expand All @@ -69,13 +69,16 @@ root main {
retry(5) m_sequence {
store("key",1) // returns success
perform_action() // returns failure
finish_and_save()
finish_and_save()
}
}
```

The node `perform_action` returns `failure` and the decorator `retry` restarts `sequence`.
The main difference with a sequence is an execution starts from the node `perform_action` skipping the node`store`.
The main difference with a sequence is an execution starts from the node `perform_action` skipping the node `store`.

The memory will be reset once the final action has returned `success`.
That is, if `finish_and_save` returns `success`, the next iteration will start with `store` again.

## Reactive Sequence

Expand All @@ -87,10 +90,10 @@ root main {
m_sequence {
store("key",1) // returns success
perform_action() // returns running
finish_and_save()
finish_and_save()
}
}
```

The node `perform_action` returns `running` and the whole sequence returns `running`
but on the next tick it starts from the node `store` again.
The node `perform_action` returns `running` and the whole sequence returns `running`
but on the next tick it starts from the node `store` again.
32 changes: 20 additions & 12 deletions src/runtime/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use itertools::Itertools;

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::fmt::{format, Display, Formatter};

/// Just a Key class for the arguments that represents the key in BB
pub type RtAKey = String;
Expand Down Expand Up @@ -108,16 +108,16 @@ impl RtValueCast {
}
/// tries to convert to vec and map each element
pub fn map_vec<Map, To>(self, map: Map) -> RtResult<Option<Vec<To>>>
where
Map: Fn(RtValue) -> To,
where
Map: Fn(RtValue) -> To,
{
self.with_ptr().map(|v| v.as_vec(map))
}

/// tries to convert obj to map
pub fn map_obj<Map, To>(self, map: Map) -> RtResult<Option<HashMap<String, To>>>
where
Map: Fn((String, RtValue)) -> (String, To),
where
Map: Fn((String, RtValue)) -> (String, To),
{
self.with_ptr().map(|v| v.as_map(map))
}
Expand Down Expand Up @@ -151,17 +151,17 @@ impl RtValue {
}
}
pub fn as_vec<Map, To>(self, map: Map) -> Option<Vec<To>>
where
Map: Fn(RtValue) -> To,
where
Map: Fn(RtValue) -> To,
{
match self {
RtValue::Array(elems) => Some(elems.into_iter().map(map).collect()),
_ => None,
}
}
pub fn as_map<Map, To>(self, map: Map) -> Option<HashMap<String, To>>
where
Map: Fn((String, RtValue)) -> (String, To),
where
Map: Fn((String, RtValue)) -> (String, To),
{
match self {
RtValue::Object(elems) => Some(HashMap::from_iter(
Expand Down Expand Up @@ -232,8 +232,8 @@ impl RtArgs {
}
/// takes the first one and transform
pub fn first_as<M, To>(&self, map: M) -> Option<To>
where
M: Fn(RtValue) -> Option<To>,
where
M: Fn(RtValue) -> Option<To>,
{
self.0.first().and_then(|v| map(v.value.clone()))
}
Expand All @@ -245,6 +245,14 @@ impl RtArgs {
.find(|a| a.name == key)
.map(|a| a.clone().value)
}
/// finds by name and transform
pub fn find_as<M, To>(&self, key: RtAKey, map: M) -> Option<To>
where
M: Fn(RtValue) -> Option<To>,
{
self.find(key).and_then(|v| map(v.clone()))
}

/// finds by name or takes by index
pub fn find_or_ith(&self, key: RtAKey, ith: usize) -> Option<RtValue> {
self.0
Expand Down Expand Up @@ -399,4 +407,4 @@ impl RtArgument {
}
}
}
}
}
25 changes: 16 additions & 9 deletions src/runtime/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub struct TreeContextRef {
bb: BBRef,
tracer: TracerRef,
curr_ts: Timestamp,
trimmer: TrimmingQueueRef,
_trimmer: TrimmingQueueRef,
env: RtEnvRef,
}

Expand All @@ -55,7 +55,13 @@ impl From<&mut TreeContext> for TreeContextRef {

impl TreeContextRef {
pub fn from_ctx(ctx: &TreeContext, trimmer: Arc<Mutex<TrimmingQueue>>) -> Self {
TreeContextRef::new(ctx.bb.clone(), ctx.tracer.clone(), ctx.curr_ts, trimmer, ctx.rt_env.clone())
TreeContextRef::new(
ctx.bb.clone(),
ctx.tracer.clone(),
ctx.curr_ts,
trimmer,
ctx.rt_env.clone(),
)
}
/// A pointer to tracer struct.
pub fn tracer(&self) -> TracerRef {
Expand Down Expand Up @@ -84,14 +90,14 @@ impl TreeContextRef {
bb: Arc<Mutex<BlackBoard>>,
tracer: Arc<Mutex<Tracer>>,
curr_ts: Timestamp,
trimmer: Arc<Mutex<TrimmingQueue>>,
_trimmer: Arc<Mutex<TrimmingQueue>>,
env: RtEnvRef,
) -> Self {
Self {
bb,
tracer,
curr_ts,
trimmer,
_trimmer,
env,
}
}
Expand Down Expand Up @@ -124,7 +130,6 @@ pub struct TreeContext {
rt_env: RtEnvRef,
}


impl TreeContext {
pub fn state(&self) -> &HashMap<RNodeId, RNodeState> {
&self.state
Expand Down Expand Up @@ -216,12 +221,14 @@ impl TreeContext {
self.trace(NewState(id, state.clone()))?;
Ok(self.state.insert(id, state))
}
pub(crate) fn state_in_ts(&self, id: &RNodeId) -> RNodeState {
let actual_state = self
.state
pub(crate) fn state_last_set(&self, id: &RNodeId) -> RNodeState {
self.state
.get(id)
.cloned()
.unwrap_or(RNodeState::Ready(RtArgs::default()));
.unwrap_or(RNodeState::Ready(RtArgs::default()))
}
pub(crate) fn state_in_ts(&self, id: &RNodeId) -> RNodeState {
let actual_state = self.state_last_set(id);
if self.is_curr_ts(&id) {
actual_state
} else {
Expand Down
30 changes: 16 additions & 14 deletions src/runtime/forester.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ pub mod decorator;
pub mod flow;
pub mod serv;


use crate::runtime::action::keeper::ActionKeeper;
use crate::runtime::action::{recover, Tick};
use crate::runtime::args::RtArgs;
use crate::runtime::blackboard::BlackBoard;
use crate::runtime::context::{RNodeState, TreeContext, TreeContextRef};
use crate::runtime::env::RtEnv;
use crate::runtime::forester::flow::{FlowDecision, read_cursor, run_with, run_with_par};
use crate::runtime::forester::flow::{read_cursor, run_with, run_with_par, FlowDecision};
use crate::runtime::forester::serv::ServInfo;
use crate::runtime::rtree::rnode::RNode;
use crate::runtime::rtree::RuntimeTree;
Expand All @@ -20,7 +19,6 @@ use crate::runtime::{trimmer, RtOk, RtResult, RuntimeError};
use crate::tracer::{Event, Tracer};
use log::debug;
use std::sync::{Arc, Mutex};

use tokio::task::JoinHandle;

/// The entry point to process execution.
Expand All @@ -32,7 +30,7 @@ use tokio::task::JoinHandle;
/// - Optimizer holds tasks to modify the tree or other components on the fly
///
///# Note:
/// Better to use `ForesterBuilder` to create a Forester.
/// Better to use `ForesterBuilder` to create a Forester.
pub struct Forester {
pub tree: RuntimeTree,
pub bb: Arc<Mutex<BlackBoard>>,
Expand Down Expand Up @@ -177,12 +175,11 @@ impl Forester {
RNodeState::Ready(tick_args) => {
let len = children.len() as i64;
debug!(target:"flow[ready]", "tick:{}, {tpe}. Start node",ctx.curr_ts());
let new_state =
if tpe.is_par() {
RNodeState::Running(run_with_par(tick_args, len))
} else {
RNodeState::Running(run_with(tick_args, 0, len))
};
let new_state = if tpe.is_par() {
RNodeState::Running(run_with_par(tick_args, len))
} else {
RNodeState::Running(run_with(tick_args, 0, len))
};

debug!(target:"flow[ready]", "tick:{}, {tpe}. Switch to the new_state:{}",ctx.curr_ts(),&new_state);
ctx.new_state(id, new_state)?;
Expand Down Expand Up @@ -270,9 +267,15 @@ impl Forester {
// since it is ready we need to prepare decorator to start
// But then we do nothing but switch the state to running in the current tick.
RNodeState::Ready(tick_args) => {
debug!(target:"decorator[ready]", "tick:{}, {tpe}. Start decorator({init_args}) and child args({tick_args})",ctx.curr_ts());
let new_state =
decorator::prepare(tpe, init_args.clone(), tick_args, &mut ctx)?;
let prev_state = ctx.state_last_set(&id);
debug!(target:"decorator[ready]", "tick:{}, {tpe}. Start decorator({init_args}), child args({tick_args}) and prev state({prev_state})",ctx.curr_ts());
// Decorators only prepare themselves if they didn't return Running in the previous tick
let new_state = match prev_state {
RNodeState::Running(_) => {
RNodeState::Running(run_with(tick_args, 0, 1))
}
_ => decorator::prepare(tpe, init_args.clone(), tick_args, &mut ctx)?,
};
debug!(target:"decorator[ready]", "tick:{}, the new_state: {}",ctx.curr_ts(),&new_state);
ctx.new_state(id, new_state)?;
}
Expand Down Expand Up @@ -348,7 +351,6 @@ impl Forester {
self.stop_http();
self.env.lock().map(|mut e| e.stop_all_daemons())?;


ctx.root_state(self.tree.root)
}

Expand Down
Loading

0 comments on commit 9ed0b03

Please sign in to comment.