Skip to main content

autocore_std/
lib.rs

1//! # AutoCore Standard Library
2//!
3//! The standard library for writing AutoCore control programs. This crate provides
4//! everything you need to build real-time control applications that integrate with
5//! the AutoCore server ecosystem.
6//!
7//! ## Overview
8//!
9//! AutoCore control programs run as separate processes that communicate with the
10//! autocore-server via shared memory and IPC. This library handles all the low-level
11//! details, allowing you to focus on your control logic.
12//!
13//! ```text
14//! ┌─────────────────────────┐     ┌─────────────────────────┐
15//! │   autocore-server       │     │   Your Control Program  │
16//! │                         │     │                         │
17//! │  ┌─────────────────┐    │     │  ┌─────────────────┐    │
18//! │  │ Shared Memory   │◄───┼─────┼──│ ControlRunner   │    │
19//! │  │ (GlobalMemory)  │    │     │  │                 │    │
20//! │  └─────────────────┘    │     │  │ ┌─────────────┐ │    │
21//! │                         │     │  │ │ Your Logic  │ │    │
22//! │  ┌─────────────────┐    │     │  │ └─────────────┘ │    │
23//! │  │ Tick Signal     │────┼─────┼──│                 │    │
24//! │  └─────────────────┘    │     │  └─────────────────┘    │
25//! └─────────────────────────┘     └─────────────────────────┘
26//! ```
27//!
28//! ## Quick Start
29//!
30//! 1. Create a new control project using `acctl`:
31//!    ```bash
32//!    acctl clone <server-ip> <project-name>
33//!    ```
34//!
35//! 2. Implement the [`ControlProgram`] trait:
36//!    ```ignore
37//!    use autocore_std::{ControlProgram, RTrig};
38//!
39//!    // GlobalMemory is generated from your project.json
40//!    mod gm;
41//!    use gm::GlobalMemory;
42//!
43//!    pub struct MyProgram {
44//!        start_button: RTrig,
45//!    }
46//!
47//!    impl MyProgram {
48//!        pub fn new() -> Self {
49//!            Self {
50//!                start_button: RTrig::new(),
51//!            }
52//!        }
53//!    }
54//!
55//!    impl ControlProgram for MyProgram {
56//!        type Memory = GlobalMemory;
57//!
58//!        fn process_tick(&mut self, mem: &mut GlobalMemory, _cycle: u64) {
59//!            // Detect rising edge on start button
60//!            if self.start_button.call(mem.inputs.start_button) {
61//!                mem.outputs.motor_running = true;
62//!                autocore_std::log::info!("Motor started!");
63//!            }
64//!        }
65//!    }
66//!    ```
67//!
68//! 3. Use the [`autocore_main!`] macro for the entry point:
69//!    ```ignore
70//!    autocore_std::autocore_main!(MyProgram, "my_project_shm", "tick");
71//!    ```
72//!
73//! ## Function Blocks (IEC 61131-3 Inspired)
74//!
75//! This library includes standard function blocks commonly used in PLC programming:
76//!
77//! - [`RTrig`] - Rising edge detector (false→true transition)
78//! - [`FTrig`] - Falling edge detector (true→false transition)
79//! - [`Ton`] - Timer On Delay (output after delay)
80//! - [`SimpleTimer`] - Simple one-shot timer (NOT IEC 61131-3, for imperative use)
81//! - [`StateMachine`] - State machine helper with automatic timer management
82//!
83//! ### Example: Edge Detection
84//!
85//! ```
86//! use autocore_std::RTrig;
87//!
88//! let mut trigger = RTrig::new();
89//!
90//! // First call with false - no edge
91//! assert_eq!(trigger.call(false), false);
92//!
93//! // Rising edge detected!
94//! assert_eq!(trigger.call(true), true);
95//!
96//! // Still true, but no edge (already high)
97//! assert_eq!(trigger.call(true), false);
98//!
99//! // Back to false
100//! assert_eq!(trigger.call(false), false);
101//!
102//! // Another rising edge
103//! assert_eq!(trigger.call(true), true);
104//! ```
105//!
106//! ### Example: Timer
107//!
108//! ```
109//! use autocore_std::Ton;
110//! use std::time::Duration;
111//!
112//! let mut timer = Ton::new();
113//! let delay = Duration::from_millis(100);
114//!
115//! // Timer not enabled - output is false
116//! assert_eq!(timer.call(false, delay), false);
117//!
118//! // Enable timer - starts counting
119//! assert_eq!(timer.call(true, delay), false);
120//!
121//! // Still counting...
122//! std::thread::sleep(Duration::from_millis(50));
123//! assert_eq!(timer.call(true, delay), false);
124//! assert!(timer.et < delay); // Elapsed time < preset
125//!
126//! // After delay elapsed
127//! std::thread::sleep(Duration::from_millis(60));
128//! assert_eq!(timer.call(true, delay), true); // Output is now true!
129//! ```
130//!
131//! ## Logging
132//!
133//! Control programs can send log messages to the autocore-server for display in the
134//! web console. Logging is handled automatically when using [`ControlRunner`].
135//!
136//! ```ignore
137//! use autocore_std::log;
138//!
139//! log::trace!("Detailed trace message");
140//! log::debug!("Debug information");
141//! log::info!("Normal operation message");
142//! log::warn!("Warning condition detected");
143//! log::error!("Error occurred!");
144//! ```
145//!
146//! See the [`logger`] module for advanced configuration.
147//!
148//! ## Memory Synchronization
149//!
150//! The [`ControlRunner`] handles all shared memory synchronization automatically:
151//!
152//! 1. **Wait for tick** - Blocks until the server signals a new cycle
153//! 2. **Read inputs** - Copies shared memory to local buffer (atomic snapshot)
154//! 3. **Execute logic** - Your `process_tick` runs on the local buffer
155//! 4. **Write outputs** - Copies local buffer back to shared memory
156//!
157//! This ensures your control logic always sees a consistent view of the data,
158//! even when other processes are modifying shared memory.
159
160#![warn(missing_docs)]
161#![warn(rustdoc::missing_crate_level_docs)]
162#![doc(html_root_url = "https://docs.rs/autocore-std/3.3.0")]
163
164use anyhow::{anyhow, Result};
165use async_trait::async_trait;
166use log::LevelFilter;
167use mechutil::ipc::{CommandMessage, IpcClient, ModuleHandler};
168use raw_sync::events::{Event, EventInit, EventState};
169use raw_sync::Timeout;
170use shared_memory::ShmemConf;
171use std::collections::HashMap;
172use std::sync::atomic::{fence, Ordering};
173use std::time::{Duration, Instant};
174
175/// UDP logger for sending log messages to autocore-server.
176///
177/// This module provides a non-blocking logger implementation that sends log messages
178/// via UDP to the autocore-server. Messages are batched and sent asynchronously to
179/// avoid impacting control loop timing.
180///
181/// # Example
182///
183/// ```ignore
184/// use autocore_std::logger;
185/// use log::LevelFilter;
186///
187/// // Initialize the logger (done automatically by ControlRunner)
188/// logger::init_udp_logger("127.0.0.1", 39101, LevelFilter::Info, "control")?;
189///
190/// // Now you can use the log macros
191/// log::info!("System initialized");
192/// ```
193pub mod logger;
194
195// Re-export log crate for convenience - control programs can use autocore_std::log::info!() etc.
196pub use log;
197
198// ============================================================================
199// Standard Library Function Blocks (IEC 61131-3 Inspired)
200// ============================================================================
201
202/// Rising Edge Trigger (R_TRIG)
203///
204/// Detects a rising edge (false → true transition) on the input signal.
205/// The output `q` is `true` for exactly one cycle when the input `clk`
206/// transitions from `false` to `true`.
207///
208/// This is equivalent to the IEC 61131-3 R_TRIG function block.
209///
210/// # Example
211///
212/// ```
213/// use autocore_std::RTrig;
214///
215/// let mut trigger = RTrig::new();
216///
217/// // No edge yet
218/// assert_eq!(trigger.call(false), false);
219///
220/// // Rising edge detected!
221/// assert_eq!(trigger.call(true), true);
222///
223/// // Signal still high, but edge already passed
224/// assert_eq!(trigger.call(true), false);
225/// assert_eq!(trigger.call(true), false);
226///
227/// // Signal goes low
228/// assert_eq!(trigger.call(false), false);
229///
230/// // Another rising edge
231/// assert_eq!(trigger.call(true), true);
232/// ```
233///
234/// # Timing Diagram
235///
236/// ```text
237/// clk: _____|‾‾‾‾‾‾‾‾‾|_____|‾‾‾‾‾
238///   q: _____|‾|_____________|‾|____
239/// ```
240///
241/// # Use Cases
242///
243/// - Detecting button presses (trigger on press, not hold)
244/// - Counting events (increment counter on each rising edge)
245/// - State machine transitions
246#[derive(Debug, Clone)]
247pub struct RTrig {
248    /// Current input value
249    pub clk: bool,
250    /// Output: true for one cycle on rising edge
251    pub q: bool,
252    /// Internal memory of previous input state
253    m: bool,
254}
255
256impl RTrig {
257    /// Creates a new rising edge trigger with all values initialized to `false`.
258    ///
259    /// # Example
260    ///
261    /// ```
262    /// use autocore_std::RTrig;
263    ///
264    /// let trigger = RTrig::new();
265    /// assert_eq!(trigger.q, false);
266    /// ```
267    pub fn new() -> Self {
268        Self {
269            clk: false,
270            q: false,
271            m: false,
272        }
273    }
274
275    /// Executes the rising edge detection logic.
276    ///
277    /// Call this method once per control cycle with the current input value.
278    /// Returns `true` for exactly one cycle when a rising edge is detected.
279    ///
280    /// # Arguments
281    ///
282    /// * `clk` - The current state of the input signal
283    ///
284    /// # Returns
285    ///
286    /// `true` if a rising edge (false → true transition) was detected, `false` otherwise.
287    ///
288    /// # Example
289    ///
290    /// ```
291    /// use autocore_std::RTrig;
292    ///
293    /// let mut trigger = RTrig::new();
294    ///
295    /// let button_pressed = true;
296    /// if trigger.call(button_pressed) {
297    ///     println!("Button was just pressed!");
298    /// }
299    /// ```
300    pub fn call(&mut self, clk: bool) -> bool {
301        self.clk = clk;
302        self.q = self.clk && !self.m;
303        self.m = self.clk;
304        self.q
305    }
306}
307
308impl Default for RTrig {
309    fn default() -> Self {
310        Self::new()
311    }
312}
313
314
315/// Falling Edge Trigger (F_TRIG)
316///
317/// Detects a falling edge (true → false transition) on the input signal.
318/// The output `q` is `true` for exactly one cycle when the input `clk`
319/// transitions from `true` to `false`.
320///
321/// This is equivalent to the IEC 61131-3 F_TRIG function block.
322///
323/// # Example
324///
325/// ```
326/// use autocore_std::FTrig;
327///
328/// let mut trigger = FTrig::new();
329///
330/// // Signal starts low
331/// assert_eq!(trigger.call(false), false);
332///
333/// // Signal goes high
334/// assert_eq!(trigger.call(true), false);
335///
336/// // Falling edge detected!
337/// assert_eq!(trigger.call(false), true);
338///
339/// // Signal still low, edge already passed
340/// assert_eq!(trigger.call(false), false);
341/// ```
342///
343/// # Timing Diagram
344///
345/// ```text
346/// clk: _____|‾‾‾‾‾‾‾‾‾|_____|‾‾‾‾‾
347///   q: _______________|‾|________
348/// ```
349///
350/// # Use Cases
351///
352/// - Detecting button releases
353/// - Detecting signal loss
354/// - Triggering actions when a condition ends
355#[derive(Debug, Clone)]
356pub struct FTrig {
357    /// Current input value
358    pub clk: bool,
359    /// Output: true for one cycle on falling edge
360    pub q: bool,
361    /// Internal memory of previous input state
362    m: bool,
363}
364
365impl FTrig {
366    /// Creates a new falling edge trigger with all values initialized to `false`.
367    ///
368    /// # Example
369    ///
370    /// ```
371    /// use autocore_std::FTrig;
372    ///
373    /// let trigger = FTrig::new();
374    /// assert_eq!(trigger.q, false);
375    /// ```
376    pub fn new() -> Self {
377        Self {
378            clk: false,
379            q: false,
380            m: false,
381        }
382    }
383
384    /// Executes the falling edge detection logic.
385    ///
386    /// Call this method once per control cycle with the current input value.
387    /// Returns `true` for exactly one cycle when a falling edge is detected.
388    ///
389    /// # Arguments
390    ///
391    /// * `clk` - The current state of the input signal
392    ///
393    /// # Returns
394    ///
395    /// `true` if a falling edge (true → false transition) was detected, `false` otherwise.
396    ///
397    /// # Example
398    ///
399    /// ```
400    /// use autocore_std::FTrig;
401    ///
402    /// let mut trigger = FTrig::new();
403    ///
404    /// // Simulate button release
405    /// trigger.call(true);  // Button held
406    /// if trigger.call(false) {  // Button released
407    ///     println!("Button was just released!");
408    /// }
409    /// ```
410    pub fn call(&mut self, clk: bool) -> bool {
411        self.clk = clk;
412        self.q = !self.clk && self.m;
413        self.m = self.clk;
414        self.q
415    }
416}
417
418impl Default for FTrig {
419    fn default() -> Self {
420        Self::new()
421    }
422}
423
424
425/// Timer On Delay (TON)
426///
427/// A timer that delays turning on the output. The output `q` becomes `true`
428/// after the enable input `en` has been continuously `true` for the preset
429/// time `pt`. The elapsed time is available in `et`.
430///
431/// This is equivalent to the IEC 61131-3 TON function block.
432///
433/// # Behavior
434///
435/// - When `en` becomes `true`, the timer starts counting from zero
436/// - While counting, `et` shows the elapsed time and `q` is `false`
437/// - When `et` reaches `pt`, `q` becomes `true` and `et` is clamped to `pt`
438/// - When `en` becomes `false`, the timer resets: `q` = `false`, `et` = 0
439///
440/// # Example
441///
442/// ```
443/// use autocore_std::Ton;
444/// use std::time::Duration;
445///
446/// let mut timer = Ton::new();
447/// let delay = Duration::from_secs(5);
448///
449/// // Timer disabled - output is false
450/// assert_eq!(timer.call(false, delay), false);
451/// assert_eq!(timer.et, Duration::ZERO);
452///
453/// // Enable timer - starts counting
454/// timer.call(true, delay);
455/// assert_eq!(timer.q, false);  // Not done yet
456/// // timer.et is now counting up...
457///
458/// // Disable resets the timer
459/// timer.call(false, delay);
460/// assert_eq!(timer.et, Duration::ZERO);
461/// ```
462///
463/// # Timing Diagram
464///
465/// ```text
466///  en: _____|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾|_____
467///   q: _____________|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾|_____
468///  et: 0---|++++++++|PT----------------|0----
469///          ^        ^                  ^
470///          |        |                  |
471///      en rises   et=pt             en falls
472/// ```
473///
474/// # Use Cases
475///
476/// - Motor start delay (allow contactors to engage)
477/// - Debouncing switches (ignore brief transitions)
478/// - Timeout detection (alarm if condition persists too long)
479#[derive(Debug, Clone)]
480pub struct Ton {
481    /// Input: Enable the timer (true = counting, false = reset)
482    pub en: bool,
483    /// Input: Preset time (duration before output activates)
484    pub pt: Duration,
485    /// Output: Timer done (true when elapsed time >= preset time)
486    pub q: bool,
487    /// Output: Elapsed time since timer was enabled
488    pub et: Duration,
489
490    start_time: Option<Instant>,
491    active: bool,
492}
493
494impl Ton {
495    /// Creates a new timer with default values.
496    ///
497    /// The timer starts in the disabled state with zero elapsed time.
498    ///
499    /// # Example
500    ///
501    /// ```
502    /// use autocore_std::Ton;
503    ///
504    /// let timer = Ton::new();
505    /// assert_eq!(timer.q, false);
506    /// assert_eq!(timer.et, std::time::Duration::ZERO);
507    /// ```
508    pub fn new() -> Self {
509        Self {
510            en: false,
511            pt: Duration::default(),
512            q: false,
513            et: Duration::default(),
514            start_time: None,
515            active: false,
516        }
517    }
518
519    /// Executes the timer logic.
520    ///
521    /// Call this method once per control cycle. The timer counts real elapsed
522    /// time (not cycles), so the output timing is independent of scan rate.
523    ///
524    /// # Arguments
525    ///
526    /// * `en` - Enable input: `true` to run timer, `false` to reset
527    /// * `pt` - Preset time: duration before output activates
528    ///
529    /// # Returns
530    ///
531    /// The current state of the output `q` (true if timer has elapsed).
532    ///
533    /// # Example
534    ///
535    /// ```
536    /// use autocore_std::Ton;
537    /// use std::time::Duration;
538    ///
539    /// let mut timer = Ton::new();
540    ///
541    /// // Use in a control loop
542    /// let motor_request = true;
543    /// let start_delay = Duration::from_millis(500);
544    ///
545    /// let motor_enabled = timer.call(motor_request, start_delay);
546    /// // motor_enabled will be true after 500ms of motor_request being true
547    /// ```
548    pub fn call(&mut self, en: bool, pt: Duration) -> bool {
549        self.en = en;
550        self.pt = pt;
551
552        if !self.en {
553            // Reset
554            self.q = false;
555            self.et = Duration::ZERO;
556            self.start_time = None;
557            self.active = false;
558        } else {
559            if !self.active {
560                // Rising edge of EN - start timing
561                self.start_time = Some(Instant::now());
562                self.active = true;
563                self.et = Duration::ZERO;
564                self.q = false;
565            } else {
566                // Timer running
567                if let Some(start) = self.start_time {
568                    self.et = start.elapsed();
569                    if self.et >= self.pt {
570                        self.et = self.pt; // Clamp ET to PT
571                        self.q = true;
572                    }
573                }
574            }
575        }
576        self.q
577    }
578
579    /// Resets the timer to its initial state.
580    ///
581    /// This is equivalent to calling `call(false, ...)`.
582    ///
583    /// # Example
584    ///
585    /// ```
586    /// use autocore_std::Ton;
587    /// use std::time::Duration;
588    ///
589    /// let mut timer = Ton::new();
590    /// timer.call(true, Duration::from_secs(1));
591    /// // ... timer is running ...
592    ///
593    /// timer.reset();
594    /// assert_eq!(timer.q, false);
595    /// assert_eq!(timer.et, Duration::ZERO);
596    /// ```
597    pub fn reset(&mut self) {
598        self.q = false;
599        self.et = Duration::ZERO;
600        self.start_time = None;
601        self.active = false;
602    }
603}
604
605impl Default for Ton {
606    fn default() -> Self {
607        Self::new()
608    }
609}
610
611
612/// Simple One-Shot Timer
613///
614/// A simple timer for one-shot timing operations. Unlike [`Ton`], this timer
615/// does NOT follow the IEC 61131-3 standard and is designed for imperative
616/// "start then check" patterns rather than cyclic function block calls.
617///
618/// **Note:** For standard PLC timer patterns (delay while condition is true),
619/// use [`Ton`] instead. `SimpleTimer` is intended for one-shot use cases
620/// like "do X, wait, then do Y".
621///
622/// # Safety
623///
624/// This timer uses [`Instant`] (monotonic clock) internally, making it immune
625/// to system clock adjustments (NTP sync, manual changes, DST). This is
626/// critical for reliable timing in industrial control applications.
627///
628/// # Example
629///
630/// ```
631/// use autocore_std::SimpleTimer;
632/// use std::time::Duration;
633///
634/// let mut timer = SimpleTimer::new(Duration::from_millis(100));
635///
636/// // Timer hasn't started yet
637/// assert_eq!(timer.is_done(), false);
638/// assert_eq!(timer.elapsed(), Duration::ZERO);
639///
640/// // Start the timer
641/// timer.start();
642///
643/// // Check if done (will be false initially)
644/// assert_eq!(timer.is_done(), false);
645///
646/// // After waiting...
647/// std::thread::sleep(Duration::from_millis(110));
648/// assert_eq!(timer.is_done(), true);
649///
650/// // Reset for reuse
651/// timer.reset();
652/// assert_eq!(timer.is_done(), false);
653/// ```
654///
655/// # Use Cases
656///
657/// - One-shot delays ("wait 500ms then continue")
658/// - Tracking time since an event occurred
659/// - Non-cyclic async contexts
660/// - Simple timeout checks
661///
662/// # Comparison with Ton
663///
664/// | Aspect | `Ton` | `SimpleTimer` |
665/// |--------|-------|---------------|
666/// | IEC 61131-3 | Yes | No |
667/// | Pattern | Cyclic `call()` | Imperative `start()`/`is_done()` |
668/// | Auto-reset on disable | Yes | No (explicit `reset()`) |
669/// | Best for | PLC-style control | One-shot operations |
670#[derive(Debug, Clone)]
671pub struct SimpleTimer {
672    start_time: Option<Instant>,
673    preset: Duration,
674}
675
676impl SimpleTimer {
677    /// Creates a new simple timer with the specified preset duration.
678    ///
679    /// The timer starts in the stopped state and must be explicitly started
680    /// with [`start()`](Self::start).
681    ///
682    /// # Arguments
683    ///
684    /// * `preset` - The duration after which [`is_done()`](Self::is_done) returns `true`
685    ///
686    /// # Example
687    ///
688    /// ```
689    /// use autocore_std::SimpleTimer;
690    /// use std::time::Duration;
691    ///
692    /// let timer = SimpleTimer::new(Duration::from_secs(5));
693    /// assert_eq!(timer.is_done(), false);
694    /// ```
695    pub fn new(preset: Duration) -> Self {
696        Self {
697            start_time: None,
698            preset,
699        }
700    }
701
702    /// Starts (or restarts) the timer.
703    ///
704    /// Records the current time as the start time. If the timer was already
705    /// running, this restarts it from zero.
706    ///
707    /// # Example
708    ///
709    /// ```
710    /// use autocore_std::SimpleTimer;
711    /// use std::time::Duration;
712    ///
713    /// let mut timer = SimpleTimer::new(Duration::from_millis(100));
714    /// timer.start();
715    /// // Timer is now counting...
716    /// ```
717    pub fn start(&mut self) {
718        self.start_time = Some(Instant::now());
719    }
720
721    /// Checks if the preset time has elapsed since the timer was started.
722    ///
723    /// Returns `false` if the timer hasn't been started yet.
724    ///
725    /// # Returns
726    ///
727    /// `true` if the timer was started and the preset duration has elapsed,
728    /// `false` otherwise.
729    ///
730    /// # Example
731    ///
732    /// ```
733    /// use autocore_std::SimpleTimer;
734    /// use std::time::Duration;
735    ///
736    /// let mut timer = SimpleTimer::new(Duration::from_millis(50));
737    ///
738    /// // Not started yet
739    /// assert_eq!(timer.is_done(), false);
740    ///
741    /// timer.start();
742    /// std::thread::sleep(Duration::from_millis(60));
743    /// assert_eq!(timer.is_done(), true);
744    /// ```
745    pub fn is_done(&self) -> bool {
746        self.start_time
747            .map(|t| t.elapsed() >= self.preset)
748            .unwrap_or(false)
749    }
750
751    /// Returns the elapsed time since the timer was started.
752    ///
753    /// Returns [`Duration::ZERO`] if the timer hasn't been started yet.
754    ///
755    /// # Returns
756    ///
757    /// The elapsed time since [`start()`](Self::start) was called, or
758    /// `Duration::ZERO` if not started.
759    ///
760    /// # Example
761    ///
762    /// ```
763    /// use autocore_std::SimpleTimer;
764    /// use std::time::Duration;
765    ///
766    /// let mut timer = SimpleTimer::new(Duration::from_secs(10));
767    ///
768    /// // Not started
769    /// assert_eq!(timer.elapsed(), Duration::ZERO);
770    ///
771    /// timer.start();
772    /// std::thread::sleep(Duration::from_millis(50));
773    /// assert!(timer.elapsed() >= Duration::from_millis(50));
774    /// ```
775    pub fn elapsed(&self) -> Duration {
776        self.start_time
777            .map(|t| t.elapsed())
778            .unwrap_or(Duration::ZERO)
779    }
780
781    /// Resets the timer to its initial (stopped) state.
782    ///
783    /// After calling this, [`is_done()`](Self::is_done) will return `false`
784    /// and [`elapsed()`](Self::elapsed) will return zero until
785    /// [`start()`](Self::start) is called again.
786    ///
787    /// # Example
788    ///
789    /// ```
790    /// use autocore_std::SimpleTimer;
791    /// use std::time::Duration;
792    ///
793    /// let mut timer = SimpleTimer::new(Duration::from_millis(50));
794    /// timer.start();
795    /// std::thread::sleep(Duration::from_millis(60));
796    /// assert_eq!(timer.is_done(), true);
797    ///
798    /// timer.reset();
799    /// assert_eq!(timer.is_done(), false);
800    /// assert_eq!(timer.elapsed(), Duration::ZERO);
801    /// ```
802    pub fn reset(&mut self) {
803        self.start_time = None;
804    }
805
806    /// Returns the preset duration for this timer.
807    ///
808    /// # Example
809    ///
810    /// ```
811    /// use autocore_std::SimpleTimer;
812    /// use std::time::Duration;
813    ///
814    /// let timer = SimpleTimer::new(Duration::from_secs(5));
815    /// assert_eq!(timer.preset(), Duration::from_secs(5));
816    /// ```
817    pub fn preset(&self) -> Duration {
818        self.preset
819    }
820
821    /// Sets a new preset duration.
822    ///
823    /// This does not reset or restart the timer. If the timer is running,
824    /// the new preset takes effect immediately for [`is_done()`](Self::is_done)
825    /// checks.
826    ///
827    /// # Arguments
828    ///
829    /// * `preset` - The new duration after which `is_done()` returns `true`
830    ///
831    /// # Example
832    ///
833    /// ```
834    /// use autocore_std::SimpleTimer;
835    /// use std::time::Duration;
836    ///
837    /// let mut timer = SimpleTimer::new(Duration::from_secs(5));
838    /// timer.set_preset(Duration::from_secs(10));
839    /// assert_eq!(timer.preset(), Duration::from_secs(10));
840    /// ```
841    pub fn set_preset(&mut self, preset: Duration) {
842        self.preset = preset;
843    }
844
845    /// Returns whether the timer is currently running (has been started but not reset).
846    ///
847    /// # Example
848    ///
849    /// ```
850    /// use autocore_std::SimpleTimer;
851    /// use std::time::Duration;
852    ///
853    /// let mut timer = SimpleTimer::new(Duration::from_secs(5));
854    /// assert_eq!(timer.is_running(), false);
855    ///
856    /// timer.start();
857    /// assert_eq!(timer.is_running(), true);
858    ///
859    /// timer.reset();
860    /// assert_eq!(timer.is_running(), false);
861    /// ```
862    pub fn is_running(&self) -> bool {
863        self.start_time.is_some()
864    }
865}
866
867impl Default for SimpleTimer {
868    /// Creates a timer with a preset of zero (will be immediately done when started).
869    fn default() -> Self {
870        Self::new(Duration::ZERO)
871    }
872}
873
874
875/// State Machine Helper (FB_StateMachine)
876///
877/// A state machine helper with automatic timer management and error tracking.
878/// Provides two timers that automatically reset when the state index changes:
879///
880/// - **Timer** (`timer_done()`) - General purpose timer for delays and debouncing
881/// - **Timeout** (`timed_out()`) - For detecting stuck states
882///
883/// This is equivalent to the IEC 61131-3 FB_StateMachine function block.
884///
885/// # Automatic Timer Reset
886///
887/// Both timers automatically reset when `index` changes. This eliminates a
888/// common source of bugs in state machines where timers are not properly reset.
889///
890/// The pattern is:
891/// 1. Set `timer_preset` (and optionally `timeout_preset`) in state N
892/// 2. Change `index` to state N+1 (timers reset and start counting)
893/// 3. In state N+1, check `timer_done()` or `timed_out()`
894///
895/// # Example
896///
897/// ```
898/// use autocore_std::StateMachine;
899/// use std::time::Duration;
900///
901/// let mut state = StateMachine::new();
902///
903/// // Simulate a control loop
904/// loop {
905///     match state.index {
906///         0 => { // Reset
907///             state.clear_error();
908///             state.index = 10;
909///         }
910///         10 => { // Idle - wait for start signal
911///             // For demo, just proceed
912///             state.timer_preset = Duration::from_millis(100);
913///             state.index = 20;
914///         }
915///         20 => { // Debounce
916///             if state.timer_done() {
917///                 state.timeout_preset = Duration::from_secs(10);
918///                 state.index = 30;
919///             }
920///         }
921///         30 => { // Wait for operation (simulated)
922///             // In real code: check operation_complete
923///             // For demo, check timeout
924///             if state.timed_out() {
925///                 state.set_error(30, "Operation timeout");
926///                 state.index = 0;
927///             }
928///             // Exit demo loop
929///             break;
930///         }
931///         _ => { state.index = 0; }
932///     }
933///
934///     state.call(); // Call at end of each scan cycle
935///     # break; // Exit for doctest
936/// }
937/// ```
938///
939/// # Timer Presets Persist
940///
941/// Timer presets persist until you change them. This allows setting a preset
942/// once and using it across multiple states:
943///
944/// ```ignore
945/// 100 => {
946///     state.timer_preset = Duration::from_millis(300);
947///     state.index = 110;
948/// }
949/// 110 => {
950///     // Uses 300ms preset set in state 100
951///     if some_condition && state.timer_done() {
952///         state.index = 120;
953///     }
954/// }
955/// 120 => {
956///     // Still uses 300ms preset (timer reset on state change)
957///     if state.timer_done() {
958///         state.index = 10;
959///     }
960/// }
961/// ```
962///
963/// # Error Handling Pattern
964///
965/// ```ignore
966/// 200 => {
967///     state.timeout_preset = Duration::from_secs(7);
968///     start_operation();
969///     state.index = 210;
970/// }
971/// 210 => {
972///     if operation_complete {
973///         state.index = 1000; // Success
974///     } else if state.timed_out() {
975///         state.set_error(210, "Operation timed out");
976///         state.index = 5000; // Error handler
977///     }
978/// }
979/// 5000 => {
980///     // Error recovery
981///     state.index = 0;
982/// }
983/// ```
984#[derive(Debug, Clone)]
985pub struct StateMachine {
986    /// Current state index.
987    pub index: i32,
988
989    /// Timer preset. `timer_done()` returns true when time in current state >= this value.
990    /// Defaults to `Duration::MAX` (timer never triggers unless you set a preset).
991    pub timer_preset: Duration,
992
993    /// Timeout preset. `timed_out()` returns true when time in current state >= this value.
994    /// Defaults to `Duration::MAX` (timeout never triggers unless you set a preset).
995    pub timeout_preset: Duration,
996
997    /// Error code. A value of 0 indicates no error.
998    /// When non-zero, `is_error()` returns true.
999    pub error_code: i32,
1000
1001    /// Status message for UI display. Content does not indicate an error.
1002    pub message: String,
1003
1004    /// Error message for UI display. Should only have content when `error_code != 0`.
1005    pub error_message: String,
1006
1007    // Internal state
1008    last_index: Option<i32>,
1009    state_entered_at: Option<Instant>,
1010}
1011
1012impl StateMachine {
1013    /// Creates a new state machine starting at state 0.
1014    ///
1015    /// Timer presets default to `Duration::MAX`, meaning timers won't trigger
1016    /// until you explicitly set a preset.
1017    ///
1018    /// # Example
1019    ///
1020    /// ```
1021    /// use autocore_std::StateMachine;
1022    ///
1023    /// let state = StateMachine::new();
1024    /// assert_eq!(state.index, 0);
1025    /// assert_eq!(state.error_code, 0);
1026    /// assert!(!state.is_error());
1027    /// ```
1028    pub fn new() -> Self {
1029        Self {
1030            index: 0,
1031            timer_preset: Duration::MAX,
1032            timeout_preset: Duration::MAX,
1033            error_code: 0,
1034            message: String::new(),
1035            error_message: String::new(),
1036            last_index: None,
1037            state_entered_at: None,
1038        }
1039    }
1040
1041    /// Call once per scan cycle at the END of your state machine logic.
1042    ///
1043    /// This method:
1044    /// - Detects state changes (when `index` differs from the previous call)
1045    /// - Resets internal timers on state change
1046    /// - Updates internal tracking for `elapsed()`, `timer_done()`, and `timed_out()`
1047    ///
1048    /// # Example
1049    ///
1050    /// ```
1051    /// use autocore_std::StateMachine;
1052    ///
1053    /// let mut state = StateMachine::new();
1054    ///
1055    /// // Your state machine logic here...
1056    /// match state.index {
1057    ///     0 => { state.index = 10; }
1058    ///     _ => {}
1059    /// }
1060    ///
1061    /// state.call(); // Always call at the end
1062    /// ```
1063    pub fn call(&mut self) {
1064        if self.last_index != Some(self.index) {
1065            self.state_entered_at = Some(Instant::now());
1066        }
1067        self.last_index = Some(self.index);
1068    }
1069
1070    /// Returns true when time in current state >= `timer_preset`.
1071    ///
1072    /// The timer automatically resets when the state index changes.
1073    ///
1074    /// # Example
1075    ///
1076    /// ```
1077    /// use autocore_std::StateMachine;
1078    /// use std::time::Duration;
1079    ///
1080    /// let mut state = StateMachine::new();
1081    /// state.timer_preset = Duration::from_millis(50);
1082    /// state.call(); // Start tracking
1083    ///
1084    /// assert!(!state.timer_done()); // Not enough time elapsed
1085    ///
1086    /// std::thread::sleep(Duration::from_millis(60));
1087    /// assert!(state.timer_done()); // Now it's done
1088    /// ```
1089    pub fn timer_done(&self) -> bool {
1090        self.elapsed() >= self.timer_preset
1091    }
1092
1093    /// Returns true when time in current state >= `timeout_preset`.
1094    ///
1095    /// Use this for detecting stuck states. The timeout automatically
1096    /// resets when the state index changes.
1097    ///
1098    /// # Example
1099    ///
1100    /// ```
1101    /// use autocore_std::StateMachine;
1102    /// use std::time::Duration;
1103    ///
1104    /// let mut state = StateMachine::new();
1105    /// state.timeout_preset = Duration::from_millis(50);
1106    /// state.call();
1107    ///
1108    /// assert!(!state.timed_out());
1109    ///
1110    /// std::thread::sleep(Duration::from_millis(60));
1111    /// assert!(state.timed_out());
1112    /// ```
1113    pub fn timed_out(&self) -> bool {
1114        self.elapsed() >= self.timeout_preset
1115    }
1116
1117    /// Returns elapsed time since entering the current state.
1118    ///
1119    /// Returns `Duration::ZERO` if `call()` has never been called.
1120    ///
1121    /// # Example
1122    ///
1123    /// ```
1124    /// use autocore_std::StateMachine;
1125    /// use std::time::Duration;
1126    ///
1127    /// let mut state = StateMachine::new();
1128    /// state.call();
1129    ///
1130    /// std::thread::sleep(Duration::from_millis(10));
1131    /// assert!(state.elapsed() >= Duration::from_millis(10));
1132    /// ```
1133    pub fn elapsed(&self) -> Duration {
1134        self.state_entered_at
1135            .map(|t| t.elapsed())
1136            .unwrap_or(Duration::ZERO)
1137    }
1138
1139    /// Returns true if `error_code != 0`.
1140    ///
1141    /// # Example
1142    ///
1143    /// ```
1144    /// use autocore_std::StateMachine;
1145    ///
1146    /// let mut state = StateMachine::new();
1147    /// assert!(!state.is_error());
1148    ///
1149    /// state.error_code = 100;
1150    /// assert!(state.is_error());
1151    /// ```
1152    pub fn is_error(&self) -> bool {
1153        self.error_code != 0
1154    }
1155
1156    /// Set error state with code and message.
1157    ///
1158    /// This is a convenience method equivalent to setting `error_code`
1159    /// and `error_message` directly.
1160    ///
1161    /// # Example
1162    ///
1163    /// ```
1164    /// use autocore_std::StateMachine;
1165    ///
1166    /// let mut state = StateMachine::new();
1167    /// state.set_error(110, "Failed to home X axis");
1168    ///
1169    /// assert_eq!(state.error_code, 110);
1170    /// assert_eq!(state.error_message, "Failed to home X axis");
1171    /// assert!(state.is_error());
1172    /// ```
1173    pub fn set_error(&mut self, code: i32, message: impl Into<String>) {
1174        self.error_code = code;
1175        self.error_message = message.into();
1176    }
1177
1178    /// Clear error state.
1179    ///
1180    /// Sets `error_code` to 0 and clears `error_message`.
1181    ///
1182    /// # Example
1183    ///
1184    /// ```
1185    /// use autocore_std::StateMachine;
1186    ///
1187    /// let mut state = StateMachine::new();
1188    /// state.set_error(100, "Some error");
1189    /// assert!(state.is_error());
1190    ///
1191    /// state.clear_error();
1192    /// assert!(!state.is_error());
1193    /// assert_eq!(state.error_code, 0);
1194    /// assert!(state.error_message.is_empty());
1195    /// ```
1196    pub fn clear_error(&mut self) {
1197        self.error_code = 0;
1198        self.error_message.clear();
1199    }
1200
1201    /// Returns the current state index.
1202    ///
1203    /// This is equivalent to reading `state.index` directly but provided
1204    /// for API consistency.
1205    pub fn state(&self) -> i32 {
1206        self.index
1207    }
1208}
1209
1210impl Default for StateMachine {
1211    fn default() -> Self {
1212        Self::new()
1213    }
1214}
1215
1216
1217// ============================================================================
1218// Core Framework
1219// ============================================================================
1220
1221/// Marker trait for generated GlobalMemory structs.
1222///
1223/// This trait is implemented by the auto-generated `GlobalMemory` struct
1224/// that represents the shared memory layout. It serves as a marker for
1225/// type safety in the control framework.
1226///
1227/// You don't need to implement this trait yourself - it's automatically
1228/// implemented by the code generator.
1229pub trait AutoCoreMemory {}
1230
1231/// The trait that defines a control program's logic.
1232///
1233/// Implement this trait to create your control program. The associated `Memory`
1234/// type should be the generated `GlobalMemory` struct from your project.
1235///
1236/// # Memory Type Requirements
1237///
1238/// The `Memory` type must implement `Copy` to allow efficient synchronization
1239/// between shared memory and local buffers. This is automatically satisfied
1240/// by the generated `GlobalMemory` struct.
1241///
1242/// # Lifecycle
1243///
1244/// 1. `initialize` is called once at startup
1245/// 2. `process_tick` is called repeatedly in the control loop
1246///
1247/// # Example
1248///
1249/// ```ignore
1250/// use autocore_std::ControlProgram;
1251///
1252/// mod gm;
1253/// use gm::GlobalMemory;
1254///
1255/// pub struct MyController {
1256///     cycle_counter: u64,
1257/// }
1258///
1259/// impl MyController {
1260///     pub fn new() -> Self {
1261///         Self { cycle_counter: 0 }
1262///     }
1263/// }
1264///
1265/// impl ControlProgram for MyController {
1266///     type Memory = GlobalMemory;
1267///
1268///     fn initialize(&mut self, mem: &mut GlobalMemory) {
1269///         // Set initial output states
1270///         mem.outputs.ready = true;
1271///         log::info!("Controller initialized");
1272///     }
1273///
1274///     fn process_tick(&mut self, mem: &mut GlobalMemory, cycle: u64) {
1275///         self.cycle_counter = cycle;
1276///
1277///         // Your control logic here
1278///         if mem.inputs.start && !mem.inputs.estop {
1279///             mem.outputs.running = true;
1280///         }
1281///     }
1282/// }
1283/// ```
1284pub trait ControlProgram {
1285    /// The shared memory structure type (usually the generated `GlobalMemory`).
1286    ///
1287    /// Must implement `Copy` to allow efficient memory synchronization.
1288    type Memory: Copy;
1289
1290    /// Called once when the control program starts.
1291    ///
1292    /// Use this to initialize output states, reset counters, or perform
1293    /// any one-time setup. The default implementation does nothing.
1294    ///
1295    /// # Arguments
1296    ///
1297    /// * `mem` - Mutable reference to the shared memory. Changes are written
1298    ///           back to shared memory after this method returns.
1299    fn initialize(&mut self, _mem: &mut Self::Memory) {}
1300
1301    /// The main control loop - called once per scan cycle.
1302    ///
1303    /// This is where your control logic lives. Read inputs from `mem`,
1304    /// perform calculations, and write outputs back to `mem`.
1305    ///
1306    /// # Arguments
1307    ///
1308    /// * `mem` - Mutable reference to a local copy of the shared memory.
1309    ///           Changes made here are written back to shared memory after
1310    ///           this method returns.
1311    /// * `cycle` - The current cycle number (increments each tick, starting at 1).
1312    ///
1313    /// # Timing
1314    ///
1315    /// This method should complete within the scan cycle time. Long-running
1316    /// operations will cause cycle overruns.
1317    fn process_tick(&mut self, mem: &mut Self::Memory, cycle: u64);
1318}
1319
1320/// Configuration for the [`ControlRunner`].
1321///
1322/// Specifies connection parameters, shared memory names, and logging settings.
1323/// Use [`Default::default()`] for typical configurations.
1324///
1325/// # Example
1326///
1327/// ```
1328/// use autocore_std::RunnerConfig;
1329/// use log::LevelFilter;
1330///
1331/// let config = RunnerConfig {
1332///     ipc_address: "192.168.1.100:9100".to_string(),
1333///     module_name: "my_controller".to_string(),
1334///     shm_name: "my_project_shm".to_string(),
1335///     tick_signal_name: "tick".to_string(),
1336///     busy_signal_name: Some("busy".to_string()),
1337///     log_level: LevelFilter::Debug,
1338///     ..Default::default()
1339/// };
1340/// ```
1341#[derive(Debug, Clone)]
1342pub struct RunnerConfig {
1343    /// IPC server address in "host:port" format (default: "127.0.0.1:9100")
1344    pub ipc_address: String,
1345    /// Module name for registration with the server (default: "control")
1346    pub module_name: String,
1347    /// Shared memory segment name (must match server configuration)
1348    pub shm_name: String,
1349    /// Name of the tick signal in shared memory (triggers each scan cycle)
1350    pub tick_signal_name: String,
1351    /// Optional name of the busy signal (set when cycle completes)
1352    pub busy_signal_name: Option<String>,
1353    /// Minimum log level to send to the server (default: Info)
1354    pub log_level: LevelFilter,
1355    /// UDP port for sending logs to the server (default: 39101)
1356    pub log_udp_port: u16,
1357}
1358
1359impl Default for RunnerConfig {
1360    fn default() -> Self {
1361        Self {
1362            ipc_address: "127.0.0.1:9100".to_string(),
1363            module_name: "control".to_string(),
1364            shm_name: "autocore_cyclic".to_string(),
1365            tick_signal_name: "tick".to_string(),
1366            busy_signal_name: None,
1367            log_level: LevelFilter::Info,
1368            log_udp_port: logger::DEFAULT_LOG_UDP_PORT,
1369        }
1370    }
1371}
1372
1373/// Internal handler for IPC setup phase.
1374/// The control program only uses IPC during initialization to get the memory layout.
1375struct ControlSetupHandler {
1376    domain: String,
1377}
1378
1379impl ControlSetupHandler {
1380    fn new(domain: &str) -> Self {
1381        Self {
1382            domain: domain.to_string(),
1383        }
1384    }
1385}
1386
1387#[async_trait]
1388impl ModuleHandler for ControlSetupHandler {
1389    async fn handle_message(&mut self, msg: CommandMessage) -> CommandMessage {
1390        // We don't expect to handle any messages during setup
1391        msg.into_error_response("Control module does not handle external messages")
1392    }
1393
1394    async fn on_initialize(&mut self) -> Result<(), anyhow::Error> {
1395        Ok(())
1396    }
1397
1398    async fn on_finalize(&mut self) -> Result<(), anyhow::Error> {
1399        Ok(())
1400    }
1401
1402    fn domain(&self) -> &str {
1403        &self.domain
1404    }
1405
1406    fn version(&self) -> &str {
1407        "1.0.0"
1408    }
1409}
1410
1411/// The main execution engine for control programs.
1412///
1413/// `ControlRunner` handles all the infrastructure required to run a control program:
1414///
1415/// - Connecting to the autocore-server via IPC
1416/// - Opening and mapping shared memory
1417/// - Setting up synchronization signals
1418/// - Running the real-time control loop
1419/// - Sending log messages to the server
1420///
1421/// # Usage
1422///
1423/// ```ignore
1424/// use autocore_std::{ControlRunner, RunnerConfig};
1425///
1426/// let config = RunnerConfig {
1427///     shm_name: "my_project_shm".to_string(),
1428///     tick_signal_name: "tick".to_string(),
1429///     ..Default::default()
1430/// };
1431///
1432/// ControlRunner::new(MyProgram::new())
1433///     .config(config)
1434///     .run()?;  // Blocks forever
1435/// ```
1436///
1437/// # Control Loop
1438///
1439/// The runner executes a synchronous control loop:
1440///
1441/// 1. **Wait** - Blocks until the tick signal is set by the server
1442/// 2. **Read** - Copies shared memory to a local buffer (acquire barrier)
1443/// 3. **Execute** - Calls your `process_tick` method
1444/// 4. **Write** - Copies local buffer back to shared memory (release barrier)
1445/// 5. **Signal** - Sets the busy signal (if configured) to indicate completion
1446///
1447/// This ensures your code always sees a consistent snapshot of the data
1448/// and that your writes are atomically visible to other processes.
1449pub struct ControlRunner<P: ControlProgram> {
1450    config: RunnerConfig,
1451    program: P,
1452}
1453
1454impl<P: ControlProgram> ControlRunner<P> {
1455    /// Creates a new runner for the given control program.
1456    ///
1457    /// Uses default configuration. Call [`.config()`](Self::config) to customize.
1458    ///
1459    /// # Arguments
1460    ///
1461    /// * `program` - Your control program instance
1462    ///
1463    /// # Example
1464    ///
1465    /// ```ignore
1466    /// let runner = ControlRunner::new(MyProgram::new());
1467    /// ```
1468    pub fn new(program: P) -> Self {
1469        Self {
1470            config: RunnerConfig::default(),
1471            program,
1472        }
1473    }
1474
1475    /// Sets the configuration for this runner.
1476    ///
1477    /// # Arguments
1478    ///
1479    /// * `config` - The configuration to use
1480    ///
1481    /// # Example
1482    ///
1483    /// ```ignore
1484    /// ControlRunner::new(MyProgram::new())
1485    ///     .config(RunnerConfig {
1486    ///         shm_name: "custom_shm".to_string(),
1487    ///         ..Default::default()
1488    ///     })
1489    ///     .run()?;
1490    /// ```
1491    pub fn config(mut self, config: RunnerConfig) -> Self {
1492        self.config = config;
1493        self
1494    }
1495
1496    /// Starts the control loop.
1497    ///
1498    /// This method blocks indefinitely, running the control loop until
1499    /// an error occurs or the process is terminated.
1500    ///
1501    /// # Returns
1502    ///
1503    /// Returns `Ok(())` only if the loop exits cleanly (which typically
1504    /// doesn't happen). Returns an error if:
1505    ///
1506    /// - IPC connection fails
1507    /// - Shared memory cannot be opened
1508    /// - Signal offsets cannot be found
1509    /// - A critical error occurs during execution
1510    ///
1511    /// # Example
1512    ///
1513    /// ```ignore
1514    /// fn main() -> anyhow::Result<()> {
1515    ///     ControlRunner::new(MyProgram::new())
1516    ///         .config(config)
1517    ///         .run()
1518    /// }
1519    /// ```
1520    pub fn run(mut self) -> Result<()> {
1521        // Initialize UDP logger FIRST (before any log statements)
1522        // Extract host from ipc_address (format: "host:port")
1523        let log_host = self.config.ipc_address
1524            .split(':')
1525            .next()
1526            .unwrap_or("127.0.0.1");
1527
1528        if let Err(e) = logger::init_udp_logger(
1529            log_host,
1530            self.config.log_udp_port,
1531            self.config.log_level,
1532            "control",
1533        ) {
1534            eprintln!("Warning: Failed to initialize UDP logger: {}", e);
1535            // Continue anyway - logging will just go nowhere
1536        }
1537
1538        // We use a dedicated runtime for the setup phase
1539        let rt = tokio::runtime::Builder::new_current_thread()
1540            .enable_all()
1541            .build()?;
1542
1543        rt.block_on(async {
1544            log::info!("AutoCore Control Runner Starting...");
1545
1546            // 1. Connect IPC using the new ModuleHandler API
1547            let handler = ControlSetupHandler::new(&self.config.module_name);
1548            let client = IpcClient::connect(&self.config.ipc_address, handler).await
1549                .map_err(|e| anyhow!("Failed to connect to server: {}", e))?;
1550            log::info!("IPC Connected.");
1551
1552            // 2. Get Layout to find signal offsets
1553            // Use the request method with a timeout
1554            let response = client.request("gm.get_layout", serde_json::Value::Null, Duration::from_secs(5)).await
1555                .map_err(|e| anyhow!("Failed to get layout: {}", e))?;
1556
1557            if !response.success {
1558                return Err(anyhow!("Failed to get layout: {}", response.error_message));
1559            }
1560
1561            let layout: HashMap<String, serde_json::Value> = serde_json::from_value(response.data)?;
1562
1563            // 3. Find Signal Offsets
1564            let tick_offset = self.find_offset(&layout, &self.config.tick_signal_name)?;
1565            let busy_offset = if let Some(name) = &self.config.busy_signal_name {
1566                Some(self.find_offset(&layout, name)?)
1567            } else {
1568                None
1569            };
1570
1571            // Shutdown the IPC client - we don't need it anymore
1572            client.shutdown();
1573
1574            // 4. Open Shared Memory
1575            let shmem = ShmemConf::new().os_id(&self.config.shm_name).open()?;
1576            let base_ptr = shmem.as_ptr();
1577            log::info!("Shared Memory '{}' mapped.", self.config.shm_name);
1578
1579            // 5. Setup Pointers
1580            // SAFETY: We trust the server's layout matches the generated GlobalMemory struct.
1581            let gm = unsafe { &mut *(base_ptr as *mut P::Memory) };
1582
1583            // EventInit::from_existing returns (Box<dyn EventImpl>, usize)
1584            let (tick_event, _) = unsafe {
1585                Event::from_existing(base_ptr.add(tick_offset))
1586                    .map_err(|e| anyhow!("Failed to open tick event: {}", e))?
1587            };
1588
1589            let busy_event = if let Some(offset) = busy_offset {
1590                let (event, _) = unsafe {
1591                    Event::from_existing(base_ptr.add(offset))
1592                        .map_err(|e| anyhow!("Failed to open busy event: {}", e))?
1593                };
1594                Some(event)
1595            } else {
1596                None
1597            };
1598
1599            // 6. Initialize local memory buffer and user program
1600            // We use a local copy for the control loop to ensure:
1601            // - Consistent snapshot of inputs at start of cycle
1602            // - Atomic commit of outputs at end of cycle
1603            // - Proper memory barriers for cross-process visibility
1604            let mut local_mem: P::Memory = unsafe { std::ptr::read_volatile(gm) };
1605            fence(Ordering::Acquire); // Ensure we see all prior writes from other processes
1606
1607            self.program.initialize(&mut local_mem);
1608
1609            // Write back any changes from initialize
1610            fence(Ordering::Release);
1611            unsafe { std::ptr::write_volatile(gm, local_mem) };
1612
1613            log::info!("Entering Control Loop...");
1614            let mut cycle_count: u64 = 0;
1615
1616            loop {
1617                // Wait for Tick
1618                if let Err(e) = tick_event.wait(Timeout::Infinite) {
1619                    eprintln!("Tick wait error: {}", e);
1620                    break;
1621                }
1622
1623                cycle_count += 1;
1624
1625                // === INPUT PHASE ===
1626                // Read all variables from shared memory into local buffer.
1627                // This gives us a consistent snapshot of inputs for this cycle.
1628                // Acquire fence ensures we see all writes from other processes (server, modules).
1629                local_mem = unsafe { std::ptr::read_volatile(gm) };
1630                fence(Ordering::Acquire);
1631
1632                // === EXECUTE PHASE ===
1633                // Execute user logic on the local copy.
1634                // All reads/writes during process_tick operate on local_mem.
1635                self.program.process_tick(&mut local_mem, cycle_count);
1636
1637                // === OUTPUT PHASE ===
1638                // Write all variables from local buffer back to shared memory.
1639                // Release fence ensures our writes are visible to other processes.
1640                fence(Ordering::Release);
1641                unsafe { std::ptr::write_volatile(gm, local_mem) };
1642
1643                // Signal Busy/Done
1644                if let Some(ref busy) = busy_event {
1645                    let _ = busy.set(EventState::Signaled);
1646                }
1647            }
1648
1649            Ok(())
1650        })
1651    }
1652
1653    fn find_offset(&self, layout: &HashMap<String, serde_json::Value>, name: &str) -> Result<usize> {
1654        let info = layout.get(name).ok_or_else(|| anyhow!("Signal '{}' not found in layout", name))?;
1655        info.get("offset")
1656            .and_then(|v| v.as_u64())
1657            .map(|v| v as usize)
1658            .ok_or_else(|| anyhow!("Invalid offset for '{}'", name))
1659    }
1660}
1661
1662/// Generates the standard `main` function for a control program.
1663///
1664/// This macro reduces boilerplate by creating a properly configured `main`
1665/// function that initializes and runs your control program.
1666///
1667/// # Arguments
1668///
1669/// * `$prog_type` - The type of your control program (must implement [`ControlProgram`])
1670/// * `$shm_name` - The shared memory segment name (string literal)
1671/// * `$tick_signal` - The tick signal name in shared memory (string literal)
1672///
1673/// # Example
1674///
1675/// ```ignore
1676/// mod gm;
1677/// use gm::GlobalMemory;
1678///
1679/// pub struct MyProgram;
1680///
1681/// impl MyProgram {
1682///     pub fn new() -> Self { Self }
1683/// }
1684///
1685/// impl autocore_std::ControlProgram for MyProgram {
1686///     type Memory = GlobalMemory;
1687///
1688///     fn process_tick(&mut self, mem: &mut GlobalMemory, _cycle: u64) {
1689///         // Your logic here
1690///     }
1691/// }
1692///
1693/// // This generates the main function
1694/// autocore_std::autocore_main!(MyProgram, "my_project_shm", "tick");
1695/// ```
1696///
1697/// # Generated Code
1698///
1699/// The macro expands to:
1700///
1701/// ```ignore
1702/// fn main() -> anyhow::Result<()> {
1703///     let config = autocore_std::RunnerConfig {
1704///         ipc_address: "127.0.0.1:9100".to_string(),
1705///         module_name: "control".to_string(),
1706///         shm_name: "my_project_shm".to_string(),
1707///         tick_signal_name: "tick".to_string(),
1708///         busy_signal_name: None,
1709///         log_level: log::LevelFilter::Info,
1710///         log_udp_port: autocore_std::logger::DEFAULT_LOG_UDP_PORT,
1711///     };
1712///
1713///     autocore_std::ControlRunner::new(MyProgram::new())
1714///         .config(config)
1715///         .run()
1716/// }
1717/// ```
1718#[macro_export]
1719macro_rules! autocore_main {
1720    ($prog_type:ty, $shm_name:expr, $tick_signal:expr) => {
1721        fn main() -> anyhow::Result<()> {
1722            let config = autocore_std::RunnerConfig {
1723                ipc_address: "127.0.0.1:9100".to_string(),
1724                module_name: "control".to_string(),
1725                shm_name: $shm_name.to_string(),
1726                tick_signal_name: $tick_signal.to_string(),
1727                busy_signal_name: None,
1728                log_level: log::LevelFilter::Info,
1729                log_udp_port: autocore_std::logger::DEFAULT_LOG_UDP_PORT,
1730            };
1731
1732            autocore_std::ControlRunner::new(<$prog_type>::new())
1733                .config(config)
1734                .run()
1735        }
1736    };
1737}
1738
1739#[cfg(test)]
1740mod tests {
1741    use super::*;
1742
1743    #[test]
1744    fn test_rtrig_rising_edge() {
1745        let mut trigger = RTrig::new();
1746
1747        // No edge initially
1748        assert_eq!(trigger.call(false), false);
1749
1750        // Rising edge
1751        assert_eq!(trigger.call(true), true);
1752
1753        // No edge while high
1754        assert_eq!(trigger.call(true), false);
1755        assert_eq!(trigger.call(true), false);
1756
1757        // No edge on falling
1758        assert_eq!(trigger.call(false), false);
1759
1760        // Rising edge again
1761        assert_eq!(trigger.call(true), true);
1762    }
1763
1764    #[test]
1765    fn test_ftrig_falling_edge() {
1766        let mut trigger = FTrig::new();
1767
1768        // No edge initially
1769        assert_eq!(trigger.call(false), false);
1770
1771        // No edge on rising
1772        assert_eq!(trigger.call(true), false);
1773
1774        // Falling edge
1775        assert_eq!(trigger.call(false), true);
1776
1777        // No edge while low
1778        assert_eq!(trigger.call(false), false);
1779
1780        // Rising, then falling edge
1781        assert_eq!(trigger.call(true), false);
1782        assert_eq!(trigger.call(false), true);
1783    }
1784
1785    #[test]
1786    fn test_ton_basic() {
1787        let mut timer = Ton::new();
1788        let pt = Duration::from_millis(50);
1789
1790        // Disabled
1791        assert_eq!(timer.call(false, pt), false);
1792        assert_eq!(timer.et, Duration::ZERO);
1793
1794        // Enable
1795        assert_eq!(timer.call(true, pt), false);
1796        assert!(timer.et < pt);
1797
1798        // Wait for timer
1799        std::thread::sleep(Duration::from_millis(60));
1800        assert_eq!(timer.call(true, pt), true);
1801        assert_eq!(timer.et, pt);
1802
1803        // Reset
1804        timer.reset();
1805        assert_eq!(timer.q, false);
1806        assert_eq!(timer.et, Duration::ZERO);
1807    }
1808
1809    #[test]
1810    fn test_simple_timer_basic() {
1811        let mut timer = SimpleTimer::new(Duration::from_millis(50));
1812
1813        // Not started yet
1814        assert_eq!(timer.is_done(), false);
1815        assert_eq!(timer.is_running(), false);
1816        assert_eq!(timer.elapsed(), Duration::ZERO);
1817
1818        // Start
1819        timer.start();
1820        assert_eq!(timer.is_running(), true);
1821        assert_eq!(timer.is_done(), false);
1822
1823        // Wait for timer
1824        std::thread::sleep(Duration::from_millis(60));
1825        assert_eq!(timer.is_done(), true);
1826        assert!(timer.elapsed() >= Duration::from_millis(50));
1827
1828        // Reset
1829        timer.reset();
1830        assert_eq!(timer.is_done(), false);
1831        assert_eq!(timer.is_running(), false);
1832        assert_eq!(timer.elapsed(), Duration::ZERO);
1833    }
1834
1835    #[test]
1836    fn test_simple_timer_restart() {
1837        let mut timer = SimpleTimer::new(Duration::from_millis(100));
1838
1839        timer.start();
1840        std::thread::sleep(Duration::from_millis(30));
1841
1842        // Restart resets the timer
1843        timer.start();
1844        assert!(timer.elapsed() < Duration::from_millis(20));
1845    }
1846
1847    #[test]
1848    fn test_simple_timer_preset() {
1849        let mut timer = SimpleTimer::new(Duration::from_secs(5));
1850
1851        assert_eq!(timer.preset(), Duration::from_secs(5));
1852
1853        timer.set_preset(Duration::from_secs(10));
1854        assert_eq!(timer.preset(), Duration::from_secs(10));
1855    }
1856
1857    #[test]
1858    fn test_simple_timer_default() {
1859        let mut timer = SimpleTimer::default();
1860
1861        // Default preset is zero, so immediately done when started
1862        assert_eq!(timer.preset(), Duration::ZERO);
1863
1864        timer.start();
1865        assert_eq!(timer.is_done(), true);
1866    }
1867
1868    #[test]
1869    fn test_state_machine_basic() {
1870        let state = StateMachine::new();
1871
1872        assert_eq!(state.index, 0);
1873        assert_eq!(state.error_code, 0);
1874        assert!(!state.is_error());
1875        assert_eq!(state.timer_preset, Duration::MAX);
1876        assert_eq!(state.timeout_preset, Duration::MAX);
1877    }
1878
1879    #[test]
1880    fn test_state_machine_timer() {
1881        let mut state = StateMachine::new();
1882        state.timer_preset = Duration::from_millis(50);
1883        state.call();
1884
1885        // Timer shouldn't be done yet
1886        assert!(!state.timer_done());
1887
1888        // Wait for timer
1889        std::thread::sleep(Duration::from_millis(60));
1890        assert!(state.timer_done());
1891        assert!(state.elapsed() >= Duration::from_millis(50));
1892    }
1893
1894    #[test]
1895    fn test_state_machine_timeout() {
1896        let mut state = StateMachine::new();
1897        state.timeout_preset = Duration::from_millis(50);
1898        state.call();
1899
1900        assert!(!state.timed_out());
1901
1902        std::thread::sleep(Duration::from_millis(60));
1903        assert!(state.timed_out());
1904    }
1905
1906    #[test]
1907    fn test_state_machine_timer_reset_on_state_change() {
1908        let mut state = StateMachine::new();
1909        state.timer_preset = Duration::from_millis(50);
1910        state.call();
1911
1912        // Wait a bit
1913        std::thread::sleep(Duration::from_millis(30));
1914        let elapsed_before = state.elapsed();
1915        assert!(elapsed_before >= Duration::from_millis(30));
1916
1917        // Change state
1918        state.index = 10;
1919        state.call();
1920
1921        // Timer should have reset
1922        assert!(state.elapsed() < Duration::from_millis(20));
1923        assert!(!state.timer_done());
1924    }
1925
1926    #[test]
1927    fn test_state_machine_error_handling() {
1928        let mut state = StateMachine::new();
1929
1930        assert!(!state.is_error());
1931
1932        state.set_error(110, "Failed to home axis");
1933        assert!(state.is_error());
1934        assert_eq!(state.error_code, 110);
1935        assert_eq!(state.error_message, "Failed to home axis");
1936
1937        state.clear_error();
1938        assert!(!state.is_error());
1939        assert_eq!(state.error_code, 0);
1940        assert!(state.error_message.is_empty());
1941    }
1942
1943    #[test]
1944    fn test_state_machine_preset_persists() {
1945        let mut state = StateMachine::new();
1946
1947        // Set preset in state 0
1948        state.timer_preset = Duration::from_millis(50);
1949        state.index = 10;
1950        state.call();
1951
1952        // Preset should still be 50ms
1953        assert_eq!(state.timer_preset, Duration::from_millis(50));
1954
1955        // Change to state 20 without changing preset
1956        state.index = 20;
1957        state.call();
1958
1959        // Preset still 50ms
1960        assert_eq!(state.timer_preset, Duration::from_millis(50));
1961    }
1962
1963    #[test]
1964    fn test_state_machine_default_presets_never_trigger() {
1965        let mut state = StateMachine::new();
1966        state.call();
1967
1968        // Default presets are Duration::MAX, so timers should never trigger
1969        std::thread::sleep(Duration::from_millis(10));
1970        assert!(!state.timer_done());
1971        assert!(!state.timed_out());
1972    }
1973}