Skip to main content

des_sim/modeling/
agent.rs

1//! The `agent` module provides structures for defining and managing autonomous agents
2//! within a discrete event simulation.
3//!
4//! It introduces `AgentStep` for individual actions and `AgentContinuation` for
5//! chaining these actions into complex workflows, enabling agents to dynamically
6//! interact with the simulation environment.
7
8use crate::context::{EventContext, UserContext};
9use crate::modeling::event::EventPriority;
10use crate::modeling::model::Model;
11use crate::primitive::time::Duration;
12use std::cell::RefCell;
13use std::collections::VecDeque;
14use std::fmt;
15use std::rc::Rc;
16
17/// Represents a single step in an agent's workflow.
18///
19/// This structure holds the logic and scheduling parameters for a specific
20/// action within the simulation.
21pub struct AgentStep<E, M: Model<E>> {
22    /// A descriptive tag for the step, used for logging and debugging purposes.
23    pub tag: &'static str,
24    /// The time delay before this step is executed.
25    pub delay: Duration,
26    /// The event priority, determining the order of execution within the same simulation time.
27    pub priority: EventPriority,
28    /// The logic to be executed for this step.
29    ///
30    /// The closure is provided with the current event context, the model instance,
31    /// and a mutable reference to the queue of subsequent steps, allowing for
32    /// dynamic modification of the remaining workflow.
33    #[allow(clippy::type_complexity)]
34    pub logic: Box<dyn FnOnce(&mut EventContext<E, M>, &mut M, &mut VecDeque<AgentStep<E, M>>)>,
35}
36
37impl<E, M: Model<E>> fmt::Debug for AgentStep<E, M> {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        f.debug_struct("AgentStep")
40            .field("tag", &self.tag)
41            .field("delay", &self.delay)
42            .field("priority", &self.priority)
43            // Since trait objects are opaque, output the type name and the function pointer address.
44            .field("logic", &format_args!("Box<dyn FnOnce>({:p})", self.logic))
45            .finish()
46    }
47}
48
49pub struct AgentContinuation<E, M: Model<E>> {
50    future_steps: VecDeque<AgentStep<E, M>>,
51    to_event_payload: Rc<dyn Fn(AgentContinuation<E, M>) -> E>,
52}
53
54impl<E, M: Model<E>> fmt::Debug for AgentContinuation<E, M> {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        f.debug_struct("AgentContinuation")
57            .field("future_steps", &self.future_steps)
58            // Output the internal Rc pointer address for identification.
59            .field(
60                "to_event_payload",
61                &format_args!("Rc<dyn Fn>({:p})", Rc::as_ptr(&self.to_event_payload)),
62            )
63            .finish()
64    }
65}
66
67impl<E: 'static, M: Model<E> + 'static> AgentContinuation<E, M> {
68    /// Creates a new `AgentContinuation` instance.
69    ///
70    /// # Arguments
71    ///
72    /// * `to_event_payload` - A closure that defines how to transform the continuation
73    ///   into an event payload for scheduling the next step.
74    pub fn new<F>(to_event_payload: F) -> Self
75    where
76        F: Fn(AgentContinuation<E, M>) -> E + 'static,
77    {
78        Self {
79            future_steps: VecDeque::new(),
80            to_event_payload: Rc::new(to_event_payload),
81        }
82    }
83
84    /// Appends a new step to be executed after the specified delay.
85    ///
86    /// # Arguments
87    ///
88    /// * `tag` - A descriptive label for the step, useful for debugging and tracking.
89    /// * `delay` - The time duration to wait before executing this step.
90    /// * `priority` - The priority of the event within the event scheduler.
91    /// * `logic` - The closure containing the logic to be executed for this step.
92    pub fn then_after<F>(
93        mut self,
94        tag: &'static str,
95        delay: Duration,
96        priority: EventPriority,
97        logic: F,
98    ) -> Self
99    where
100        F: FnOnce(&mut EventContext<E, M>, &mut M, &mut VecDeque<AgentStep<E, M>>) + 'static,
101    {
102        self.future_steps.push_back(AgentStep {
103            tag,
104            delay,
105            priority,
106            logic: Box::new(logic),
107        });
108
109        self
110    }
111}
112
113impl<E, M: Model<E>> AgentContinuation<E, M> {
114    /// Returns a reference to the next step scheduled to be executed,
115    /// without consuming it.
116    pub fn peek_next_step(&self) -> Option<&AgentStep<E, M>> {
117        self.future_steps.front()
118    }
119
120    /// Returns the tag of the next scheduled step, if available.
121    pub fn peek_next_step_tag(&self) -> Option<&'static str> {
122        self.peek_next_step().map(|step| step.tag)
123    }
124
125    /// Returns the total number of steps remaining in the continuation.
126    pub fn get_remain_step_count(&self) -> usize {
127        self.future_steps.len()
128    }
129
130    /// Consumes and executes the current step, then schedules the next step if one exists.
131    /// If no further steps remain, the continuation concludes gracefully.
132    pub fn execute_and_schedule(mut self, context: &mut EventContext<E, M>, model: &mut M) {
133        // Extract the current step.
134        if let Some(current_step) = self.future_steps.pop_front() {
135            // Process the event logic.
136            // Note: Steps may be added/modified by the logic itself.
137            (current_step.logic)(context, model, &mut self.future_steps);
138
139            // Check if there is a subsequent step and schedule it for the future.
140            let next_info = self.future_steps.front().map(|s| (s.delay, s.priority));
141            if let Some((next_delay, next_priority)) = next_info {
142                // Clone the reference to the event builder and schedule the remainder.
143                let to_event_payload = Rc::clone(&self.to_event_payload);
144                let next_payload = to_event_payload(self);
145
146                context.schedule_event(next_delay, next_priority, next_payload);
147            }
148        }
149    }
150}
151
152pub struct AgentActionTicket<E, M: Model<E>> {
153    action: RefCell<Option<AgentContinuation<E, M>>>,
154}
155
156impl<E, M: Model<E>> fmt::Debug for AgentActionTicket<E, M> {
157    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158        match self.action.borrow().as_ref() {
159            Some(continuation) => fmt::Debug::fmt(continuation, f),
160            None => write!(f, "ExecutedAction"),
161        }
162    }
163}
164
165impl<E, M: Model<E>> AgentActionTicket<E, M> {
166    pub fn issue(continuation: AgentContinuation<E, M>) -> Self {
167        Self {
168            action: RefCell::new(Some(continuation)),
169        }
170    }
171
172    pub fn execute(&self) -> Option<AgentContinuation<E, M>> {
173        self.action.borrow_mut().take()
174    }
175
176    pub fn inspect<R, F>(&self, f: F) -> Option<R>
177    where
178        F: FnOnce(&AgentContinuation<E, M>) -> R,
179    {
180        self.action.borrow().as_ref().map(f)
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187    use crate::context::EventContext;
188    use crate::event_scheduler::EventScheduler;
189    use crate::modeling::event::{Event, EventPriority};
190    use crate::modeling::hook::instance::HookDelegate;
191    use crate::modeling::model::Model;
192    use crate::primitive::time::{Duration, MicroStepStatus, SimTime, TickStatus};
193    use crate::source_handler::SourceHandler;
194    use std::assert_matches;
195    use std::cell::RefCell;
196    use std::rc::Rc;
197
198    // Dummy Event for testing
199    #[derive(Debug)]
200    enum TestEvent {
201        AgentContinuationEvent(AgentActionTicket<TestEvent, TestModel>),
202        SimpleEvent(u32),
203    }
204
205    // Dummy Model for testing
206    #[derive(Debug, Default)]
207    struct TestModel {
208        pub counter: Rc<RefCell<u32>>,
209    }
210
211    impl Model<TestEvent> for TestModel {
212        fn handle_event(
213            &mut self,
214            context: &mut EventContext<TestEvent, Self>,
215            event: &Event<TestEvent>,
216        ) {
217            match &event.payload {
218                TestEvent::AgentContinuationEvent(ticket) => {
219                    if let Some(continuation) = ticket.execute() {
220                        continuation.execute_and_schedule(context, self);
221                    }
222                }
223                TestEvent::SimpleEvent(val) => {
224                    *self.counter.borrow_mut() += val;
225                }
226            }
227        }
228    }
229
230    fn create_mock_event_context() -> EventContext<TestEvent, TestModel> {
231        EventContext {
232            current_tick_status: TickStatus::initialize(),
233            current_micro_step_status: MicroStepStatus::initialize(),
234            hook_delegate: HookDelegate::new(),
235            source_handler: SourceHandler::new(),
236            event_scheduler: EventScheduler::new(),
237        }
238    }
239
240    #[test]
241    fn test_agent_step_creation() {
242        let step = AgentStep {
243            tag: "test_step",
244            delay: Duration::ticks(1),
245            priority: EventPriority::new(5),
246            logic: Box::new(|_, _: &mut TestModel, _| {}),
247        };
248
249        assert_eq!(step.tag, "test_step");
250        assert_eq!(step.delay, Duration::ticks(1));
251        assert_eq!(step.priority, EventPriority::new(5));
252    }
253
254    #[test]
255    fn test_agent_continuation_new() {
256        let continuation: AgentContinuation<TestEvent, TestModel> =
257            AgentContinuation::new(|_| TestEvent::SimpleEvent(0));
258        assert_eq!(continuation.get_remain_step_count(), 0);
259        assert!(continuation.future_steps.is_empty());
260    }
261
262    #[test]
263    fn test_agent_continuation_then_after() {
264        let continuation: AgentContinuation<TestEvent, TestModel> =
265            AgentContinuation::new(|_| TestEvent::SimpleEvent(0))
266                .then_after(
267                    "step1",
268                    Duration::ticks(1),
269                    EventPriority::new(5),
270                    |_, _, _| {},
271                )
272                .then_after(
273                    "step2",
274                    Duration::ticks(2),
275                    EventPriority::new(10),
276                    |_, _, _| {},
277                );
278
279        assert_eq!(continuation.get_remain_step_count(), 2);
280
281        let step1 = continuation.future_steps.front().unwrap();
282        assert_eq!(step1.tag, "step1");
283        assert_eq!(step1.delay, Duration::ticks(1));
284        assert_eq!(step1.priority, EventPriority::new(5));
285
286        let step2 = continuation.future_steps.get(1).unwrap();
287        assert_eq!(step2.tag, "step2");
288        assert_eq!(step2.delay, Duration::ticks(2));
289        assert_eq!(step2.priority, EventPriority::new(10));
290    }
291
292    #[test]
293    fn test_agent_continuation_peek_next_step() {
294        let continuation: AgentContinuation<TestEvent, TestModel> =
295            AgentContinuation::new(|_| TestEvent::SimpleEvent(0))
296                .then_after(
297                    "first",
298                    Duration::ticks(1),
299                    EventPriority::new(5),
300                    |_, _, _| {},
301                )
302                .then_after(
303                    "second",
304                    Duration::ticks(2),
305                    EventPriority::new(10),
306                    |_, _, _| {},
307                );
308
309        assert_eq!(continuation.peek_next_step_tag(), Some("first"));
310        assert_eq!(
311            continuation.peek_next_step().unwrap().delay,
312            Duration::ticks(1)
313        );
314    }
315
316    #[test]
317    fn test_agent_continuation_get_remain_step_count() {
318        let continuation: AgentContinuation<TestEvent, TestModel> =
319            AgentContinuation::new(|_| TestEvent::SimpleEvent(0))
320                .then_after(
321                    "s1",
322                    Duration::ticks(1),
323                    EventPriority::new(5),
324                    |_, _, _| {},
325                )
326                .then_after(
327                    "s2",
328                    Duration::ticks(2),
329                    EventPriority::new(10),
330                    |_, _, _| {},
331                )
332                .then_after(
333                    "s3",
334                    Duration::ticks(3),
335                    EventPriority::new(0),
336                    |_, _, _| {},
337                );
338
339        assert_eq!(continuation.get_remain_step_count(), 3);
340    }
341
342    #[test]
343    fn test_agent_continuation_execute_and_schedule_single_step() {
344        let counter = Rc::new(RefCell::new(0));
345        let mut context = create_mock_event_context();
346        let mut model = TestModel {
347            counter: Rc::clone(&counter),
348        };
349
350        let continuation: AgentContinuation<TestEvent, TestModel> =
351            AgentContinuation::new(|_| TestEvent::SimpleEvent(0)).then_after(
352                "single_step",
353                Duration::ticks(1),
354                EventPriority::new(5),
355                |_, model: &mut TestModel, _| {
356                    *model.counter.borrow_mut() += 10;
357                },
358            );
359
360        continuation.execute_and_schedule(&mut context, &mut model);
361
362        assert_eq!(*counter.borrow(), 10);
363    }
364
365    #[test]
366    fn test_agent_continuation_execute_and_schedule_multiple_steps() {
367        let counter = Rc::new(RefCell::new(0));
368        let mut context = create_mock_event_context();
369        let mut model = TestModel {
370            counter: Rc::clone(&counter),
371        };
372
373        let continuation: AgentContinuation<TestEvent, TestModel> = AgentContinuation::new(|c| {
374            TestEvent::AgentContinuationEvent(AgentActionTicket::issue(c))
375        })
376        .then_after(
377            "step1",
378            Duration::ticks(1),
379            EventPriority::new(5),
380            |_, model, _| {
381                *model.counter.borrow_mut() += 1;
382            },
383        )
384        .then_after(
385            "step2",
386            Duration::ticks(2),
387            EventPriority::new(10),
388            |_, model, _| {
389                *model.counter.borrow_mut() += 10;
390            },
391        );
392
393        context.schedule_event(
394            Duration::one(),
395            EventPriority::minimum(),
396            TestEvent::AgentContinuationEvent(AgentActionTicket::issue(continuation)),
397        );
398        context.event_scheduler.flush_pending();
399        let events = context.event_scheduler.drain_ready(SimTime::from_ticks(1));
400        for event in events {
401            model.handle_event(&mut context, &event);
402        }
403
404        assert_eq!(*counter.borrow(), 1); // Only step1 logic should have run
405
406        context.event_scheduler.flush_pending();
407        let mut scheduled_events = context.event_scheduler.drain_ready(SimTime::from_ticks(2));
408
409        assert_eq!(scheduled_events.len(), 1);
410        let event = scheduled_events.pop_front().unwrap();
411        assert_eq!(event.priority, EventPriority::new(10));
412        assert_matches!(event.payload, TestEvent::AgentContinuationEvent(_));
413
414        if let TestEvent::AgentContinuationEvent(ticket) = event.payload {
415            assert_eq!(
416                ticket.inspect(|c| c.peek_next_step_tag()).unwrap(),
417                Some("step2")
418            );
419        } else {
420            panic!("Expected AgentContinuationEvent payload");
421        }
422    }
423
424    #[test]
425    fn test_agent_continuation_execute_and_schedule_no_steps() {
426        let counter = Rc::new(RefCell::new(0));
427        let mut context = create_mock_event_context();
428        let mut model = TestModel {
429            counter: Rc::clone(&counter),
430        };
431
432        let continuation: AgentContinuation<TestEvent, TestModel> =
433            AgentContinuation::new(|_| TestEvent::SimpleEvent(0));
434
435        continuation.execute_and_schedule(&mut context, &mut model);
436        context.event_scheduler.flush_pending();
437
438        assert_eq!(*counter.borrow(), 0);
439        assert_eq!(context.event_scheduler.ready_queue_len(), 0);
440    }
441
442    #[test]
443    fn test_agent_action_ticket_issue_and_execute() {
444        let continuation: AgentContinuation<TestEvent, TestModel> =
445            AgentContinuation::new(|_| TestEvent::SimpleEvent(0));
446        let ticket = AgentActionTicket::issue(continuation);
447
448        let executed_continuation = ticket.execute().unwrap();
449        assert_eq!(executed_continuation.get_remain_step_count(), 0);
450
451        assert!(ticket.execute().is_none()); // Should be None after first execution
452    }
453
454    #[test]
455    fn test_agent_action_ticket_inspect() {
456        let continuation: AgentContinuation<TestEvent, TestModel> =
457            AgentContinuation::new(|_| TestEvent::SimpleEvent(0)).then_after(
458                "inspect_step",
459                Duration::ticks(1),
460                EventPriority::new(5),
461                |_, _, _| {},
462            );
463        let ticket = AgentActionTicket::issue(continuation);
464
465        let tag = ticket.inspect(|c| c.peek_next_step_tag().unwrap()).unwrap();
466        assert_eq!(tag, "inspect_step");
467
468        let _ = ticket.execute(); // Consume the continuation
469        assert!(ticket.inspect(|c| c.peek_next_step_tag()).is_none()); // Should be None after execution
470    }
471}