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