use async_trait::async_trait;
use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
use std::time::{Duration, SystemTime};
use tokio::sync::Mutex;
use crate::action::ActionResponse;
use crate::error::Result;
use crate::planner::Planner;
use crate::world_state::WorldState;
use crate::Action;
use crate::Sensor;
#[derive(Debug, Clone)]
pub struct Fact {
binding: String,
data: String,
timestamp: SystemTime,
parent_sensor: String,
}
impl Fact {
pub fn new(
binding: impl Into<String>,
data: impl Into<String>,
parent_sensor: impl Into<String>,
) -> Self {
Self {
binding: binding.into(),
data: data.into(),
timestamp: SystemTime::now(),
parent_sensor: parent_sensor.into(),
}
}
pub fn binding(&self) -> &str {
&self.binding
}
pub fn data(&self) -> &str {
&self.data
}
pub fn timestamp(&self) -> SystemTime {
self.timestamp
}
pub fn parent_sensor(&self) -> &str {
&self.parent_sensor
}
}
impl fmt::Display for Fact {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.binding, self.data)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum State {
WaitingOrders,
Sensing,
Planning,
Acting,
}
#[async_trait]
pub trait ActionFn: Send + Sync {
async fn exec(&self, world_state: &HashMap<String, Fact>) -> bool;
}
pub struct Automaton {
name: String,
state: Arc<Mutex<State>>,
world_state: Arc<Mutex<WorldState>>,
goal: Arc<Mutex<WorldState>>,
working_memory: Arc<Mutex<Vec<Fact>>>,
sensors: Vec<Sensor>,
#[allow(dead_code)]
actions: Vec<Action>,
planner: Planner,
action_plan: Arc<Mutex<Vec<crate::action::Action>>>,
}
impl Automaton {
pub fn new(
name: impl Into<String>,
sensors: Vec<Sensor>,
actions: Vec<Action>,
world_state_facts: HashMap<String, String>,
) -> Self {
Self {
name: name.into(),
state: Arc::new(Mutex::new(State::WaitingOrders)),
world_state: Arc::new(Mutex::new(WorldState::from_hashmap(world_state_facts))),
goal: Arc::new(Mutex::new(WorldState::new())),
working_memory: Arc::new(Mutex::new(Vec::new())),
sensors: sensors,
actions: actions.clone(),
planner: Planner::new(actions),
action_plan: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub async fn state(&self) -> Result<State> {
let state_lock = self.state.lock().await;
Ok(*state_lock)
}
pub async fn set_goal(&self, goal: HashMap<String, String>) -> Result<()> {
{
let mut goal_lock = self.goal.lock().await;
*goal_lock = WorldState::from_hashmap(goal);
}
{
let mut state_lock = self.state.lock().await;
*state_lock = State::WaitingOrders;
}
Ok(())
}
pub async fn goal(&self) -> Result<WorldState> {
let goal_lock = self.goal.lock().await;
Ok(goal_lock.clone())
}
pub async fn world_state(&self) -> Result<WorldState> {
let world_state_lock = self.world_state.lock().await;
Ok(world_state_lock.clone())
}
pub async fn sense(&self) -> Result<()> {
{
let mut state_lock = self.state.lock().await;
*state_lock = State::Sensing;
}
{
let mut working_memory = self.working_memory.lock().await;
working_memory.clear();
}
for sensor in self.sensors.iter() {
let response = sensor.exec().await?;
{
let mut working_memory = self.working_memory.lock().await;
working_memory.push(Fact::new(
sensor.binding(),
response.response(),
sensor.name(),
));
}
}
{
let facts: Vec<Fact> = {
let working_memory = self.working_memory.lock().await;
working_memory.clone()
};
let mut world_state = self.world_state.lock().await;
for fact in facts.iter() {
world_state.insert(fact.binding().to_string(), fact.data().to_string());
}
}
Ok(())
}
pub async fn plan(&self) -> Result<Vec<crate::action::Action>> {
{
let mut state_lock = self.state.lock().await;
*state_lock = State::Planning;
}
let world_hash: HashMap<String, String>;
let goal_hash: HashMap<String, String>;
{
let world_state = self.world_state.lock().await;
world_hash = world_state
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
}
{
let goal = self.goal.lock().await;
goal_hash = goal.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
}
let mut current_state = crate::state::State::new();
for (k, v) in world_hash.iter() {
current_state.set(k, v == "true");
}
let mut goal_state = crate::state::State::new();
for (k, v) in goal_hash.iter() {
goal_state.set(k, v == "true");
}
let planner = self.planner.clone();
let plan = planner.plan(¤t_state, &goal_state)?;
{
let mut action_plan = self.action_plan.lock().await;
*action_plan = plan.clone();
}
Ok(plan)
}
pub async fn act(&self) -> Result<Vec<ActionResponse>> {
{
let mut state_lock = self.state.lock().await;
*state_lock = State::Acting;
}
let actions_to_execute = {
let action_plan = self.action_plan.lock().await;
action_plan.clone()
};
let mut responses = Vec::new();
for action in actions_to_execute.iter() {
let response = action.exec().await?;
responses.push(response.clone());
}
Ok(responses)
}
pub async fn wait(&self) -> Result<()> {
{
let mut state_lock = self.state.lock().await;
*state_lock = State::WaitingOrders;
}
{
let mut working_memory = self.working_memory.lock().await;
working_memory.clear();
}
Ok(())
}
}
pub struct AutomatonController {
automaton: Arc<Automaton>,
running: Arc<Mutex<bool>>,
}
impl AutomatonController {
pub fn new(
actions: Vec<crate::action::Action>,
sensors: Vec<crate::sensor::Sensor>,
name: impl Into<String>,
world_state: HashMap<String, String>,
) -> Self {
Self {
automaton: Arc::new(Automaton::new(name, sensors, actions, world_state)),
running: Arc::new(Mutex::new(false)),
}
}
pub fn automaton(&self) -> &Automaton {
&self.automaton
}
pub async fn set_goal(&self, goal: HashMap<String, String>) -> Result<()> {
self.automaton.set_goal(goal).await
}
pub async fn world_state(&self) -> Result<WorldState> {
self.automaton.world_state().await
}
pub async fn goal(&self) -> Result<WorldState> {
self.automaton.goal().await
}
pub async fn start(&self) -> Result<()> {
{
let mut running = self.running.lock().await;
*running = true;
}
let automaton = self.automaton.clone();
let running = self.running.clone();
tokio::task::spawn_local(async move {
loop {
let is_running = {
let running_guard = running.lock().await;
*running_guard
};
if !is_running {
break;
}
let world_state = automaton.world_state().await.unwrap();
let goal = automaton.goal().await.unwrap();
if world_state.satisfies(&goal) {
println!("World state satisfies goal: {:?}", goal);
automaton.wait().await.unwrap();
} else {
println!(
"World state differs from goal: \nState: {:?}\nGoal: {:?}",
world_state, goal
);
println!("Need to find an action plan");
let plan = automaton.plan().await.unwrap();
println!("Plan found. Will execute the action plan: {:?}", plan);
automaton.act().await.unwrap();
}
automaton.sense().await.unwrap();
tokio::time::sleep(Duration::from_secs(5)).await;
}
});
Ok(())
}
pub async fn stop(&self) -> Result<()> {
let mut running = self.running.lock().await;
*running = false;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Result;
use crate::sensor::{self, SensorResponse};
use crate::utils::actor::{self, SensorFn};
use async_trait::async_trait;
use std::sync::Arc;
struct TestSensor;
#[async_trait]
impl SensorFn for TestSensor {
async fn exec(&self, _world_state: &HashMap<String, actor::Fact>) -> Vec<actor::Fact> {
vec![actor::Fact::new("test_key", "test value", "test_sensor")]
}
}
struct SensorAdapter {
sensor: Arc<dyn SensorFn>,
}
impl SensorAdapter {
fn new(sensor: Arc<dyn SensorFn>) -> Self {
Self { sensor }
}
}
#[async_trait]
impl sensor::SensorFn for SensorAdapter {
async fn exec(&self) -> Result<SensorResponse> {
let facts = self.sensor.exec(&HashMap::new()).await;
let value = if !facts.is_empty() {
facts[0].data().to_string()
} else {
"".to_string()
};
Ok(SensorResponse::new(value, "".to_string(), 0))
}
}
#[tokio::test]
async fn test_automaton_basic_cycle() {
let test_sensor = Arc::new(TestSensor);
let sensor_adapter = SensorAdapter::new(test_sensor);
let sensors = vec![Sensor::new("test_sensor", "test_key", sensor_adapter)];
let mut actions = vec![];
let mut conditions = HashMap::new();
conditions.insert("test_key".to_string(), "test value".to_string());
let mut effects = HashMap::new();
effects.insert("goal_key".to_string(), "goal value".to_string());
let mut test_action = Action::new("test_action", 1.0).unwrap();
for (key, value) in conditions {
test_action.preconditions.set(&key, value == "true");
}
for (key, value) in effects {
test_action.effects.set(&key, value == "true");
}
actions.push(test_action);
let mut world_state = HashMap::new();
world_state.insert("initial_key".to_string(), "initial value".to_string());
let automaton = Automaton::new("test", sensors, actions, world_state);
let mut goal = HashMap::new();
goal.insert("goal_key".to_string(), "goal value".to_string());
automaton.set_goal(goal).await.unwrap();
assert_eq!(automaton.state().await.unwrap(), State::WaitingOrders);
automaton.sense().await.unwrap();
assert_eq!(automaton.state().await.unwrap(), State::Sensing);
let ws = automaton.world_state().await.unwrap();
assert_eq!(ws.get("test_key"), Some(&"test value".to_string()));
let plan = automaton.plan().await.unwrap();
assert_eq!(automaton.state().await.unwrap(), State::Planning);
assert_eq!(plan.len(), 1);
assert_eq!(plan[0].name, "test_action");
let responses = automaton.act().await.unwrap();
assert_eq!(automaton.state().await.unwrap(), State::Acting);
assert_eq!(responses.len(), 1);
assert_eq!(responses[0].stdout(), "Executed action: test_action");
}
}