Time API
The horus:: time functions are THE standard way to get time, timestep, and random numbers in HORUS nodes — same pattern as hlog!() for logging. The scheduler sets the ambient context before each tick() call; these functions read from it.
Quick Reference
| Function | Returns | Normal Mode | Deterministic Mode |
|---|---|---|---|
horus::now() | TimeStamp | Wall clock | Virtual SimClock |
horus::since(ts) | Duration | Elapsed since ts | Elapsed (virtual) |
horus::dt() | Duration | Real elapsed since last tick | Fixed 1/rate |
horus::elapsed() | Duration | Wall time since scheduler start | Accumulated virtual time |
horus::tick() | u64 | Current tick number | Current tick number |
horus::rng(f) | R | System entropy | Tick-seeded (deterministic) |
horus::budget_remaining() | Duration | Time left in budget | Time left (SimClock) |
All functions are safe to call outside tick() — they return sensible fallback values.
horus::now() — Current Time
Returns a TimeStamp representing the current framework time.
fn tick(&mut self) {
let start = horus::now();
self.do_expensive_work();
let elapsed = horus::since(start);
hlog!(debug, "work took {:?}", elapsed);
}
TimeStamp Type
TimeStamp is an opaque wrapper with nanosecond precision:
let a = horus::now();
let b = horus::now();
// Subtraction produces Duration
let diff: Duration = b - a;
// Elapsed since a timestamp
let elapsed: Duration = a.elapsed();
// Comparison
assert!(b > a);
// Display
println!("{}", a); // "1.500000s"
// Serialization
let nanos: u64 = a.as_nanos();
let restored = TimeStamp::from_nanos(nanos);
horus::dt() — Timestep
Returns the timestep for the current tick. Use this for physics integration:
fn tick(&mut self) {
let dt = horus::dt();
self.position += self.velocity * dt.as_secs_f64();
self.velocity += self.acceleration * dt.as_secs_f64();
}
In normal mode, dt() returns the actual elapsed time since the last tick. In deterministic mode, it returns a fixed value of 1/rate (e.g., 10ms for 100Hz, 1ms for 1kHz).
horus::elapsed() — Time Since Start
Total time since the scheduler started:
fn tick(&mut self) {
if horus::elapsed() > Duration::from_secs(30) {
hlog!(info, "Running for 30 seconds, switching to cruise mode");
self.mode = Mode::Cruise;
}
}
horus::tick() — Tick Number
Zero-based tick counter:
fn tick(&mut self) {
if horus::tick() % 100 == 0 {
hlog!(info, "Checkpoint at tick {}", horus::tick());
}
}
horus::rng() — Deterministic Random Numbers
Returns random values via a closure. In deterministic mode, the RNG is seeded from the tick number and node name — same sequence every run.
fn tick(&mut self) {
// Random float in range
let noise: f64 = horus::rng(|r| {
use rand::Rng;
r.gen_range(-0.01..0.01)
});
self.measurement += noise;
// Random bool
let should_explore: bool = horus::rng(|r| {
use rand::Rng;
r.gen_bool(0.1) // 10% chance
});
// Random integer
let index: usize = horus::rng(|r| {
use rand::Rng;
r.gen_range(0..self.candidates.len())
});
}
horus::budget_remaining() — Anytime Algorithms
Returns the time remaining in the current tick's budget. Use this for anytime algorithms that improve their result until time runs out:
fn tick(&mut self) {
let mut plan = self.current_plan.clone();
loop {
plan = self.improve_plan(plan);
if horus::budget_remaining() < 50_u64.us() {
break; // stop before budget runs out
}
}
self.path_topic.send(plan);
}
Returns Duration::MAX if no budget is configured or outside tick().
Fallback Behavior
All functions are safe to call outside tick():
| Function | Outside tick() |
|---|---|
horus::now() | Wall clock (fallback) |
horus::dt() | Duration::ZERO |
horus::elapsed() | Duration::ZERO |
horus::tick() | 0 |
horus::rng(f) | System entropy (non-deterministic) |
horus::budget_remaining() | Duration::MAX |
Python API
The time functions are available as module-level functions in Python:
import horus
# In a node's tick():
now = horus.time_now() # float (seconds)
dt = horus.time_dt() # float (seconds)
elapsed = horus.time_elapsed() # float (seconds)
tick = horus.time_tick() # int
budget = horus.time_budget_remaining() # float (seconds, inf if no budget)
rng_val = horus.time_rng_float() # float in [0, 1)