Simulation device provider for anolis runtime, implementing the Anolis Device Provider Protocol (ADPP) v1.
Provider-sim provides a dry-run machine with 5 simulated devices covering a variety of signal types and control patterns. This enables comprehensive validation of the anolis runtime before integrating real hardware.
| Device ID | Type | Signals | Functions |
|---|---|---|---|
tempctl0 |
Temperature Control | tc1_temp, tc2_temp, relay1_state, relay2_state, control_mode, setpoint |
set_mode, set_setpoint, set_relay |
motorctl0 |
Motor Control | motor1_speed, motor2_speed, motor1_duty, motor2_duty |
set_motor_duty |
relayio0 |
Relay/IO Module | relay_ch1_state, relay_ch2_state, gpio_input_1, gpio_input_2 |
set_relay_ch1, set_relay_ch2 |
analogsensor0 |
Analog Sensor Module | voltage_ch1, voltage_ch2, sensor_quality |
calibrate_channel, inject_noise |
chaos_control |
Fault Injection | (none) | See Fault Injection API |
Physical basis documentation for each device is available in docs/.
Build/dependency/CI governance: docs/dependencies.md.
Provider-sim includes a configurable physics engine for realistic multi-device scenarios using the Signal Registry pattern for clean separation between simulation and protocol layers.
inert- No simulation engine, devices return static/default valuesnon_interacting- Local physics engine with first-order device modelssim- External simulation via FluxGraph adapter (requiresENABLE_FLUXGRAPH=ON)
- 8 transform primitives - FirstOrderLag, Noise, Saturation, Linear, Deadband, RateLimiter, Delay, MovingAverage
- ThermalMassModel - First-principles thermal physics (lumped capacitance)
- Rule engine - Automated actions based on signal conditions
- Declarative routing - Full device→model→device signal flow in YAML config
Chamber FluxGraph Integration (config/provider-chamber.yaml + config/multi-provider-extrusion.yaml):
- Single-provider chamber simulation against FluxGraph
- Demonstrates external simulation wiring, ambient injection, and wait-ready flow
- See docs/demo-chamber.md
Multi-Provider Extrusion Coupling (config/provider-chamber.yaml + config/provider-extruder.yaml):
- Two provider instances coupled through one shared FluxGraph graph
- Demonstrates cross-provider thermal interaction and synchronization behavior
- See docs/demo-reactor.md
Provider-sim is organized into explicit layers:
src/core/: ADPP protocol handling and transportsrc/devices/: device modules and shared device infrastructuresrc/simulation/: simulation engine + external sim adapterssrc/chaos/: runtime fault injection and control device
Physics execution uses the SignalRegistry pattern to coordinate between:
- Physics ticker: reads actuators, evaluates graph, writes sensors
- Device handlers: check registry for physics-driven signals
See docs/architecture-signal-registry.md for details.
Provider-sim supports device configuration via YAML files. This allows operators to customize device topology without code changes.
# Run with configuration file (required)
./anolis-provider-sim --config config/provider-sim.yamlProvider-sim includes several example configurations:
config/provider-sim.yaml- Default configuration matching hardcoded devicesconfig/multi-tempctl.yaml- Multiple temperature controllers (demonstrates same-type devices)config/minimal.yaml- Single device for lightweight testing
See docs/configuration.md for complete schema documentation and hardware provider guidance.
devices:
- id: tempctl0
type: tempctl
initial_temp: 25.0
- id: tempctl1
type: tempctl
initial_temp: 30.0
simulation:
mode: non_interacting
tick_rate_hz: 10.0Provider-sim includes a special control device (chaos_control) with functions for injecting deterministic failures into the simulation. This enables testing of fault handling, recovery workflows, and edge cases.
Makes a device appear unavailable for a specified duration.
Parameters:
device_id(string): Target device IDduration_ms(int64): Unavailability duration in milliseconds
Behavior:
ListDevicesomits the unavailable device while fault is activeDescribeDevicereturnsNOT_FOUND(unknown/unavailable target)ReadSignalsfor explicit signal requests returnsNOT_FOUNDCallFunctionreturnsINVALID_ARGUMENTwith injected fault message- Rejects invalid requests where
duration_ms <= 0 - Automatically clears after duration expires
Forces a signal to report FAULT quality for a specified duration.
Parameters:
device_id(string): Target device IDsignal_id(string): Target signal IDduration_ms(int64): Fault duration in milliseconds
Behavior:
- Signal quality becomes
FAULT - Signal value freezes at current value
- Rejects invalid requests where
duration_ms <= 0 - Automatically clears after duration expires
Adds artificial latency to all function calls on a device.
Parameters:
device_id(string): Target device IDlatency_ms(int64): Added latency in milliseconds
Behavior:
- All CallFunction requests delayed by specified amount
- Rejects invalid requests where
latency_ms < 0 - Useful for testing timeout handling and responsiveness under load
Causes a specific function to fail probabilistically.
Parameters:
device_id(string): Target device IDfunction_id(string): Target function ID (numeric ID as string, e.g., "1" for first function)failure_rate(double): Failure probability (0.0 = never fail, 1.0 = always fail)
Behavior:
- Function returns
INVALID_ARGUMENTat specified rate - Uses uniform random distribution for probabilistic failures
- Requires numeric string
function_id(for example,"1") - Requires
failure_ratein[0.0, 1.0]
Clears all active fault injections.
Parameters: (none)
Behavior:
- Removes all device unavailability faults
- Removes all signal faults
- Removes all latency injections
- Removes all failure rate injections
import requests
BASE_URL = "http://localhost:8080"
# Inject device unavailable for 5 seconds
requests.post(f"{BASE_URL}/v0/call/sim0/chaos_control/inject_device_unavailable", json={
"args": {
"device_id": "tempctl0",
"duration_ms": 5000
}
})
# Inject 50% failure rate on set_setpoint (function_id=2)
requests.post(f"{BASE_URL}/v0/call/sim0/chaos_control/inject_call_failure", json={
"args": {
"device_id": "tempctl0",
"function_id": "2",
"failure_rate": 0.5
}
})
# Clear all faults
requests.post(f"{BASE_URL}/v0/call/sim0/chaos_control/clear_faults", json={"args": {}})Comprehensive working examples for each simulation mode are available in the examples/ directory:
- inert_mode - Protocol testing without physics (fast, deterministic)
- non_interacting_mode - Built-in first-order physics (standalone)
- sim_mode - FluxGraph external simulation (advanced coupling)
Each example includes:
- Complete YAML configurations
- Python test scripts with expected output
- README with usage instructions
See examples/README.md for the full index and quick start guide.
# Recommended wrappers
bash ./scripts/build.sh --preset dev-release
# PowerShell: .\scripts\build.ps1 -Preset dev-windows-releaseList available presets:
cmake --list-presets
ctest --list-presetsENABLE_FLUXGRAPH (default: OFF)
- Controls FluxGraph adapter for external simulation support
- When
ON: Enablessimmode with FluxGraph protocol adapter - When
OFF: Standalone build supporting onlyinertandnon_interactingmodes
# Standalone build (no FluxGraph dependencies, default)
bash ./scripts/build.sh --preset dev-release --clean
# PowerShell: .\scripts\build.ps1 -Preset dev-windows-release -Clean
# FluxGraph-enabled build (sim mode support)
bash ./scripts/build.sh --preset ci-linux-release-fluxgraph -- -DFLUXGRAPH_DIR=../fluxgraph
# PowerShell: .\scripts\build.ps1 -Preset dev-windows-release-fluxgraph -- -DFLUXGRAPH_DIR=..\fluxgraph
# sim mode will fail with: "mode=sim requires FluxGraph support. Rebuild with -DENABLE_FLUXGRAPH=ON"# Requires --config argument
bash ./scripts/run_local.sh --preset dev-release -- --config config/provider-sim.yaml
# PowerShell: .\scripts\run_local.ps1 -Preset dev-windows-release -- --config config/provider-sim.yamlProvider uses stdio+uint32_le transport for ADPP v1 communication with anolis-runtime.
Provider baseline suites are CTest-registered:
# Linux/macOS
ctest --preset dev-release -L provider
# Windows (PowerShell)
ctest --preset dev-windows-release -L providerscripts/test.* remain supported as convenience wrappers around the same suites:
# Recommended wrappers
bash ./scripts/test.sh --preset dev-release --suite all
# PowerShell: .\scripts\test.ps1 -Preset dev-windows-release -Suite all
# FluxGraph-only integration suite (requires FluxGraph-enabled build)
bash ./scripts/test.sh --preset ci-linux-release-fluxgraph --suite fluxgraph
# PowerShell: .\scripts\test.ps1 -Preset dev-windows-release-fluxgraph -Suite fluxgraphBasic Protocol Tests:
python tests/test_hello.py # ADPP Hello handshake validation
python tests/test_adpp_integration.py # Full ADPP protocol compliance
python tests/test_multi_instance.py # Multiple provider instancesFault Injection Tests:
# Run all fault injection tests
python tests/test_fault_injection.py --test all
# Run individual fault injection tests
python tests/test_fault_injection.py --test device_unavailable
python tests/test_fault_injection.py --test signal_fault
python tests/test_fault_injection.py --test call_latency
python tests/test_fault_injection.py --test call_failure
python tests/test_fault_injection.py --test clear_faults
python tests/test_fault_injection.py --test multiple_devicesAll tests use the stdio+uint32_le transport and validate correct ADPP v1 behavior.
Provider-sim implements ADPP v1 using stdio+uint32_le transport. Key components:
- device_manager: Routes ADPP calls to device implementations
- Device implementations: Simulate realistic device behaviors with state machines
- Fault injection: Global state tracking for injected faults
- Transport: stdio with uint32_le message framing (ADPP v1 standard)
Physical device documentation and operational context available in docs/.
Provider-sim demonstrates compliance with the Anolis Provider Safe Initialization Contract. All devices initialize in safe, inactive states on startup, ensuring physical safety and predictable behavior.
| Device | Safe State | Rationale |
|---|---|---|
| tempctl0 | Relays OFF, Open-loop mode | No heating actuation; monitoring only |
| motorctl0 | Duty cycle = 0 (both motors) | No motion; PWM outputs disabled |
| relayio0 | Both relays OPEN (de-energized) | Fail-safe state; external equipment unaffected |
| analogsensor0 | No output control (read-only device) | Sensor readings available; no actuation capability |
| chaos_control | No faults injected on startup | Clean slate for fault injection testing |
Device state structures use C++ member initializers to guarantee safe defaults:
tempctl0 (src/devices/tempctl/tempctl_device.cpp):
struct State {
double tc1_c = 25.0; // Ambient temperature
double tc2_c = 25.0;
bool relay1 = false; // ✅ Safe: OFF
bool relay2 = false; // ✅ Safe: OFF
std::string mode = "open"; // ✅ Safe: No control action
double setpoint_c = 60.0; // Not active in open-loop
};motorctl0 (src/devices/motorctl/motorctl_device.cpp):
struct State {
double duty1 = 0.0; // ✅ Safe: No PWM output
double duty2 = 0.0; // ✅ Safe: No PWM output
double speed1 = 0.0; // Stationary
double speed2 = 0.0;
};relayio0 (src/devices/relayio/relayio_device.cpp):
struct State {
bool relay_ch1 = false; // ✅ Safe: Open/de-energized
bool relay_ch2 = false; // ✅ Safe: Open/de-energized
bool gpio_input_1 = false;
bool gpio_input_2 = false;
};Provider-sim meets all safe initialization requirements:
- ✅ No Actuation: Relays open, motors stopped, no outputs active
- ✅ No Heating/Cooling: Temperature controller in open-loop (monitoring only)
- ✅ No Motion: Motor duty cycles = 0
- ✅ No State Assumptions: Static initializers ensure safe state regardless of prior execution
- ✅ Hardware Verification: N/A (simulation); real providers must query hardware state
Validate safe defaults:
# Start provider-sim
./build/anolis-provider-sim --config config/provider-sim.yaml
# In separate terminal, use anolis runtime
# - Runtime starts in IDLE mode (control operations blocked)
# - Devices discovered with capabilities advertised
# - Query device signals to verify safe states:
curl http://localhost:8080/v0/state/sim0/tempctl0
# Expected: relay1_state=false, relay2_state=false, control_mode="open"
curl http://localhost:8080/v0/state/sim0/motorctl0
# Expected: motor1_duty=0.0, motor2_duty=0.0, motor1_speed=0.0, motor2_speed=0.0
curl http://localhost:8080/v0/state/sim0/relayio0
# Expected: relay_ch1_state=false, relay_ch2_state=falseWhen building providers for real hardware, use this as a reference:
- Read hardware state first: Query current actuator positions/states
- Command safe state explicitly: Don't assume power-on-reset is safe
- Log verification: Record confirmation that safe state was achieved
- Use hardware disable lines: Prefer hardware interlocks over software-only disable
Example pattern for hardware providers:
void initialize_device() {
// 1. Query current hardware state
HardwareState current = read_hardware_state();
// 2. Log current state for diagnostics
LOG_INFO("Device startup state: " << current.to_string());
// 3. Command safe state explicitly
command_relay_open();
command_motor_disable();
command_heater_off();
// 4. Verify safe state achieved
HardwareState verified = read_hardware_state();
if (!verified.is_safe()) {
LOG_ERROR("Failed to achieve safe state: " << verified.to_string());
// Provider should exit or mark device unavailable
}
LOG_INFO("Device initialized in safe state");
}See Anolis Provider Safe Initialization Contract for complete requirements.