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