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