use goaprs::utils::actor::{ActionFn, Fact, PlannerFn, SensorFn};
use goaprs::utils::ActorAutomatonController;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tokio::time::sleep;
#[derive(Debug)]
struct WorldState {
temperature: i32,
heater_on: bool,
target_temperature: i32,
}
impl WorldState {
fn new() -> Self {
Self {
temperature: 18, heater_on: false,
target_temperature: 22, }
}
}
#[derive(Debug)]
struct TemperatureSensor {
world_state: Arc<Mutex<WorldState>>,
}
impl TemperatureSensor {
fn new(world_state: Arc<Mutex<WorldState>>) -> Self {
Self { world_state }
}
}
#[async_trait::async_trait]
impl SensorFn for TemperatureSensor {
async fn exec(&self, _world_state: &HashMap<String, Fact>) -> Vec<Fact> {
let state = self.world_state.lock().unwrap();
vec![
Fact::new(
"temperature",
&state.temperature.to_string(),
"TemperatureSensor",
),
Fact::new(
"target_temperature",
&state.target_temperature.to_string(),
"TemperatureSensor",
),
]
}
}
#[derive(Debug)]
struct HeaterStateSensor {
world_state: Arc<Mutex<WorldState>>,
}
impl HeaterStateSensor {
fn new(world_state: Arc<Mutex<WorldState>>) -> Self {
Self { world_state }
}
}
#[async_trait::async_trait]
impl SensorFn for HeaterStateSensor {
async fn exec(&self, _world_state: &HashMap<String, Fact>) -> Vec<Fact> {
let state = self.world_state.lock().unwrap();
vec![Fact::new(
"heater_on",
&state.heater_on.to_string(),
"HeaterStateSensor",
)]
}
}
struct TurnHeaterOnAction {
world_state: Arc<Mutex<WorldState>>,
}
impl TurnHeaterOnAction {
fn new(world_state: Arc<Mutex<WorldState>>) -> Self {
Self { world_state }
}
}
#[async_trait::async_trait]
impl ActionFn for TurnHeaterOnAction {
async fn exec(&self, _world_state: &HashMap<String, Fact>) -> bool {
println!("Executing: Turn heater ON");
let mut state = self.world_state.lock().unwrap();
state.heater_on = true;
state.temperature += 1;
true
}
}
struct TurnHeaterOffAction {
world_state: Arc<Mutex<WorldState>>,
}
impl TurnHeaterOffAction {
fn new(world_state: Arc<Mutex<WorldState>>) -> Self {
Self { world_state }
}
}
#[async_trait::async_trait]
impl ActionFn for TurnHeaterOffAction {
async fn exec(&self, _world_state: &HashMap<String, Fact>) -> bool {
println!("Executing: Turn heater OFF");
let mut state = self.world_state.lock().unwrap();
state.heater_on = false;
true
}
}
struct IncreaseTemperatureAction {
world_state: Arc<Mutex<WorldState>>,
}
impl IncreaseTemperatureAction {
fn new(world_state: Arc<Mutex<WorldState>>) -> Self {
Self { world_state }
}
}
#[async_trait::async_trait]
impl ActionFn for IncreaseTemperatureAction {
async fn exec(&self, _world_state: &HashMap<String, Fact>) -> bool {
println!("Executing: Increase temperature");
let mut state = self.world_state.lock().unwrap();
if state.heater_on {
state.temperature += 2;
println!(" Temperature increased to {}", state.temperature);
true
} else {
println!(" Cannot increase temperature - heater is off!");
false
}
}
}
struct ActionWrapper {
id: &'static str,
action: Arc<dyn ActionFn>,
}
impl ActionWrapper {
fn new(id: &'static str, action: Arc<dyn ActionFn>) -> Self {
Self { id, action }
}
}
struct TemperatureControlPlanner;
#[async_trait::async_trait]
impl PlannerFn for TemperatureControlPlanner {
async fn plan(
&self,
world_state: &HashMap<String, Fact>,
goal: &HashMap<String, Fact>,
available_actions: &[Arc<dyn ActionFn>],
) -> Vec<Arc<dyn ActionFn>> {
println!("Planning with world state: {:?}", world_state.keys());
let action_wrappers = vec![
ActionWrapper::new("TurnHeaterOnAction", available_actions[0].clone()),
ActionWrapper::new("TurnHeaterOffAction", available_actions[1].clone()),
ActionWrapper::new("IncreaseTemperatureAction", available_actions[2].clone()),
];
let mut plan = Vec::new();
if goal.contains_key("target_temperature_reached") {
if let (Some(temp_fact), Some(target_fact)) = (
world_state.get("temperature"),
world_state.get("target_temperature"),
) {
let current_temp: i32 = temp_fact.data().parse().unwrap_or(0);
let target_temp: i32 = target_fact.data().parse().unwrap_or(0);
println!(
"Current temp: {}, Target temp: {}",
current_temp, target_temp
);
let heater_on = world_state
.get("heater_on")
.map(|f| f.data() == "true")
.unwrap_or(false);
if current_temp < target_temp {
if !heater_on {
if let Some(turn_on_wrapper) = action_wrappers
.iter()
.find(|a| a.id == "TurnHeaterOnAction")
{
plan.push(turn_on_wrapper.action.clone());
}
}
if let Some(increase_wrapper) = action_wrappers
.iter()
.find(|a| a.id == "IncreaseTemperatureAction")
{
plan.push(increase_wrapper.action.clone());
}
} else if current_temp > target_temp && heater_on {
if let Some(turn_off_wrapper) = action_wrappers
.iter()
.find(|a| a.id == "TurnHeaterOffAction")
{
plan.push(turn_off_wrapper.action.clone());
}
}
}
}
println!("Plan created with {} steps", plan.len());
plan
}
}
#[tokio::main]
async fn main() {
println!("Starting Simplified Actor-based Temperature Control Agent");
let local = tokio::task::LocalSet::new();
local
.run_until(async move {
let world_state = Arc::new(Mutex::new(WorldState::new()));
let sensors = vec![
Arc::new(TemperatureSensor::new(Arc::clone(&world_state))) as Arc<dyn SensorFn>,
Arc::new(HeaterStateSensor::new(Arc::clone(&world_state))) as Arc<dyn SensorFn>,
];
let actions = vec![
Arc::new(TurnHeaterOnAction::new(Arc::clone(&world_state))) as Arc<dyn ActionFn>,
Arc::new(TurnHeaterOffAction::new(Arc::clone(&world_state))) as Arc<dyn ActionFn>,
Arc::new(IncreaseTemperatureAction::new(Arc::clone(&world_state)))
as Arc<dyn ActionFn>,
];
let planner = Arc::new(TemperatureControlPlanner) as Arc<dyn PlannerFn>;
let controller =
ActorAutomatonController::new("TemperatureController", sensors, actions, planner);
let mut goal = HashMap::new();
goal.insert(
"target_temperature_reached".to_string(),
Fact::new("target_temperature_reached", "true", "UserGoal"),
);
controller.set_goal(goal).await;
controller.start().await;
for i in 1..=10 {
println!("\n--- Cycle {} ---", i);
{
let state = world_state.lock().unwrap();
println!(
"Current temperature: {}, Target: {}, Heater: {}",
state.temperature,
state.target_temperature,
if state.heater_on { "ON" } else { "OFF" }
);
if state.temperature >= state.target_temperature {
println!("Target temperature reached!");
drop(state);
if i < 5 {
println!("Setting new target temperature to 26°C");
let mut state = world_state.lock().unwrap();
state.target_temperature = 26;
}
}
}
sleep(Duration::from_secs(1)).await;
}
controller.stop().await;
println!("Shutting down Temperature Control Agent");
})
.await;
}