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