# autocore-std
Standard library for AutoCore control programs. Provides shared memory communication, IPC, logging, and IEC 61131-3 inspired function blocks.
## Overview
`autocore-std` is the runtime library for control programs running alongside an AutoCore server. It handles:
- **Shared Memory Communication** - Zero-copy data exchange with the server
- **Synchronization** - Tick-based cyclic execution with proper memory barriers
- **UDP Logging** - Non-blocking log delivery to the server console
- **Function Blocks** - IEC 61131-3 inspired timers and edge triggers
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
autocore-std = "3.3"
log = "0.4"
anyhow = "1"
```
## Quick Start
```rust
use autocore_std::{ControlProgram, ControlRunner, RunnerConfig};
// Import the generated global memory module
mod gm;
use gm::GlobalMemory;
struct MyProgram {
// Your program state here
}
impl MyProgram {
pub fn new() -> Self {
Self {}
}
}
impl ControlProgram for MyProgram {
type Memory = GlobalMemory;
fn initialize(&mut self, mem: &mut Self::Memory) {
// Called once at startup
log::info!("Control program initialized");
}
fn process_tick(&mut self, mem: &mut Self::Memory, cycle: u64) {
// Called every scan cycle
// Read inputs from mem, compute, write outputs to mem
}
}
fn main() -> anyhow::Result<()> {
let config = RunnerConfig::default();
ControlRunner::new(MyProgram::new())
.config(config)
.run()
}
```
Or use the convenience macro:
```rust
autocore_std::autocore_main!(MyProgram, "autocore_shm", "tick");
```
## Function Blocks
### RTrig - Rising Edge Trigger
Detects false-to-true transitions.
```rust
use autocore_std::RTrig;
let mut trigger = RTrig::new();
trigger.call(false); // returns false
trigger.call(true); // returns true (rising edge!)
trigger.call(true); // returns false (no edge)
trigger.call(false); // returns false
trigger.call(true); // returns true (rising edge!)
```
### FTrig - Falling Edge Trigger
Detects true-to-false transitions.
```rust
use autocore_std::FTrig;
let mut trigger = FTrig::new();
trigger.call(true); // returns false
trigger.call(false); // returns true (falling edge!)
trigger.call(false); // returns false (no edge)
```
### TON - Timer On Delay
Output becomes true after input has been true for the preset duration.
```rust
use autocore_std::Ton;
use std::time::Duration;
let mut timer = Ton::new();
// In your process_tick:
let done = timer.call(start_condition, Duration::from_secs(5));
// Access elapsed time
println!("Elapsed: {:?}", timer.et);
```
## Logging
Logs are sent via UDP to the AutoCore server for display in the console.
```rust
log::trace!("Detailed debug info");
log::debug!("Debug message");
log::info!("Informational message");
log::warn!("Warning message");
log::error!("Error message");
```
View logs with:
```bash
acctl logs --follow
```
## Configuration
```rust
use autocore_std::{RunnerConfig, logger};
use log::LevelFilter;
let config = RunnerConfig {
ipc_address: "127.0.0.1:9100".to_string(),
module_name: "control".to_string(),
shm_name: "autocore_cyclic".to_string(),
tick_signal_name: "tick".to_string(),
busy_signal_name: None,
log_level: LevelFilter::Debug,
log_udp_port: logger::DEFAULT_LOG_UDP_PORT,
};
```
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ AutoCore Server │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Shared Mem │ │ IPC Server │ │ UDP Log Listener │ │
│ │ (cyclic) │ │ (setup) │ │ (port 39101) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │
└─────────┼────────────────┼────────────────────┼─────────────┘
│ │ │
│ memory map │ layout query │ UDP logs
│ │ │
┌─────────┼────────────────┼────────────────────┼─────────────┐
│ ▼ ▼ ▲ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Local Copy │ │ IPC Client │ │ UDP Logger │ │
│ │ of Memory │ │ (init only) │ │ (background thread) │ │
│ └──────┬──────┘ └─────────────┘ └─────────────────────┘ │
│ │ ▲ │
│ ▼ │ │
│ ┌────────────────────────────────────────────┴──────────┐ │
│ │ ControlRunner + Your Program │ │
│ │ 1. Wait for tick signal │ │
│ │ 2. Read shared memory → local copy (acquire) │ │
│ │ 3. Execute process_tick() │ │
│ │ 4. Write local copy → shared memory (release) │ │
│ │ 5. Signal completion (optional) │ │
│ └───────────────────────────────────────────────────────┘ │
│ Control Program │
└─────────────────────────────────────────────────────────────┘
```
## Memory Synchronization
The `ControlRunner` handles proper memory synchronization:
1. **Input Phase**: Reads shared memory into a local buffer with an acquire fence
2. **Execute Phase**: Your `process_tick()` runs on the local copy
3. **Output Phase**: Writes local buffer back to shared memory with a release fence
This ensures:
- Consistent snapshot of inputs at cycle start
- Atomic commit of outputs at cycle end
- Proper visibility across process boundaries
## License
**Proprietary - Licensed AutoCore Users Only**
This software is licensed exclusively for use with validly licensed AutoCore Server installations. This library is used to control industrial equipment - improper use may result in equipment damage, personal injury, or death.
See the [LICENSE](LICENSE) file for complete terms.
For licensing inquiries: support@automateddesign.com