Logic Mesh
A reactive, async dataflow engine in Rust — wire blocks together, run programs natively or in the browser via WebAssembly, watch values propagate as inputs change.

Why Logic Mesh
- One engine, two targets. The same Rust crate runs as a native library and as a
wasm32build. The bundled web editor and a server-side controller speak to identical block semantics. - Built for control, not just dataflow. First-class blocks for PID, setpoint reset, deadband, schedules, lead/lag rotation, equipment staging, runtime accumulation, on/off delays, EMA filtering, change-of-value gating, sunrise/sunset, psychrometrics — the vocabulary you reach for in HVAC, lighting, energy, and process control. ASHRAE Guideline 36 patterns map directly to the catalog.
- Unit-aware numbers. Inputs accept any compatible unit (
°F,°C,K,Pa,kPa,s,min,h, …) and convert internally — courtesy of libhaystack. Blocks likeReset,Deadband,Clamp,EMA, andTrimRespondpropagate units to their outputs so downstream consumers see the right quantity. - Extensible from either side of the WASM boundary. Define new blocks in Rust with the
#[block]attribute macro, or in JavaScript/TypeScript withdefineBlock(...)+ Zod schemas when running in a browser. - Async by construction. Every block is a
Future; the scheduler drives them on Tokio (orwasm-bindgen-futuresin a browser) and only resumes blocks whose inputs have actually changed.
Block catalog
| Category | Blocks |
|---|---|
| Control | Pid, Reset, Deadband, Clamp, Sequencer, LeadLag, TrimRespond, Economizer, PriorityArray |
| Timers | OnDelay, OffDelay, OneShot, RateLimit, Runtime, CycleCount |
| Time | Now, Schedule, Calendar, Sun |
| Logic | And, Or, Not, Xor, Equal, NotEqual, GreaterThan, GreaterThanEq, LessThan, LessThanEq, FlipFlop, Latch, Trigger |
| Math | Add, Sub, Mul, Div, Modulus, Neg, Abs, Pow, Sqrt, Exp, Log10, LogN, Sin/Cos/Tan (+ inverses), Min, Max, Average, Median, Even, Odd |
| Misc | Ema, MovingAverage, Derivative, Integrator, ChangeOfValue, SampleHold, Random, SineWave, HasValue, ParseBool, ParseNumber |
| Bitwise | BitwiseAnd, BitwiseOr, BitwiseXor, BitwiseNot |
| Psychrometrics | Enthalpy, Dewpoint, WetBulb |
| Collections / Strings | Dict, List, Get, Keys, Values, Len, Concat, Replace |
Web editor & demos
A live SvelteKit editor is hosted at https://rracariu.github.io/logic-mesh/. Drag blocks, wire pins, watch the engine react.
It bundles a UI block set (Slider, Gauge, Bar, Display, Led, Chart, MultiChart, Button, Checkbox, ComboBox, Table, Input, Label) and five worked example programs you can switch between:
- DAT Temperature Reset — ASHRAE G36-style reset of supply-air SP from outdoor temperature, driving a PID loop.
- Cooling Tower Stage + Lead/Lag — demand →
Sequencer→LeadLag→ fan LEDs with on/off delays and rotation. - Air-Side Economizer (Enthalpy) —
Enthalpyof OA vs RA →LessThan→ free-cooling LED, with both enthalpies on aMultiChart. - Anti-Short-Cycle Compressor —
OnDelaywarmup +OffDelaycool-down lockout. - Outdoor Lighting (dusk-to-cutoff) —
Sun(sunrise/sunset) +Schedule+ boolean composition driving a streetlight.
Getting started
Rust
[dependencies]
logic-mesh = "1.0"
Wire two sine waves into an adder and run them:
use logic_mesh::{
base::{block::Block, block::connect::connect_output},
blocks::{math::Add, misc::SineWave},
SingleThreadedEngine,
};
#[tokio::main]
async fn main() {
let mut add1 = Add::new();
let mut sine1 = SineWave::new();
sine1.amplitude.val = Some(3.into());
sine1.freq.val = Some(200.into());
connect_output(&mut sine1.out, add1.inputs_mut()[0]).expect("connected");
let mut sine2 = SineWave::new();
sine2.amplitude.val = Some(7.into());
sine2.freq.val = Some(400.into());
sine2.connect_output("out", add1.inputs_mut()[1]).expect("connected");
let mut engine = SingleThreadedEngine::new();
engine.schedule(add1);
engine.schedule(sine1);
engine.schedule(sine2);
engine.run().await;
}
A multi-threaded engine is available behind the multi-threaded Cargo feature.
Browser / Node.js
npm install logic-mesh
The npm package wraps the WASM build. Define a UI block in TypeScript:
import { defineBlock, initEngine } from 'logic-mesh';
import { z } from 'zod';
const Gauge = defineBlock({
desc: { name: 'Gauge', dis: 'Gauge', lib: 'ui', ver: '0.0.1', category: 'UI', doc: 'Round gauge' },
inputs: [['in', z.number()]] as const,
outputs: [['out', z.number()]] as const,
execute: async ([input]) => [input],
});
const engine = initEngine();
Gauge.register(engine);
// then wire blocks via engine.engineCommand() and engine.run()
Possible applications
- Building automation systems (BAS). AHU/VAV/chiller sequences, schedules and overrides, energy logic, equipment runtime tracking. The block vocabulary maps directly to ASHRAE G36 patterns.
- Edge / IoT controls. Logic Mesh runs anywhere Rust runs, and reaches the browser through WASM for hand-held HMIs.
- Reactive dashboards. Use the engine as the live computation backbone behind charts, KPIs, or rule alerts.
- Process simulations and digital twins. Build models out of the same primitives used in production controls.
- Custom low-code platforms. The engine and the included editor are independent — keep the engine and ship your own UX.
Architecture notes
- Block trait — every unit of work implements
async fn execute(&mut self). The#[block]attribute macro generates the boilerplate (description, registration, default impl). - Reactive scheduler — blocks suspend on input pins via
read_inputs_until_ready(event-driven) orwait_on_inputs(timeout)(event + periodic throttle). The engine only resumes blocks whose data has actually changed. - Type-checked pins — pin kinds (
Number,Bool,Str,Dict,List,Null) are validated at link time; mismatched values fault the receiving block instead of silently corrupting state. - Threading models — single-threaded (default) and multi-threaded (
features = ["multi-threaded"]) engines on native; WASM uses single-threaded with the browser event loop. - Auto-discovered registry —
build.rswalkssrc/blocks/<category>/and assembles the static block registry, so adding a new block is one file plus amod.rsre-export. - Save/load format — programs serialize to a stable JSON shape (blocks, links, positions, optional labels and per-program description) understood by both the Rust API and the web editor.
Project layout
src/
base/ core traits, engine, link/pin model
blocks/ block implementations, organized by category
tokio_impl/ native (Tokio) reader/output/engine impls
wasm/ wasm-bindgen entry points + JS-facing types
block_macro/ #[block] proc-macro
web/
packages/logic-mesh/ TypeScript wrapper around the WASM build
app/ SvelteKit web editor (the demo at the link above)
Status & contributing
- License: BSD-3-Clause.
- Stability: APIs are stable enough for application use; expect occasional additive changes as new blocks land.
- Issues and PRs welcome — especially new blocks for under-served domains (lighting, irrigation, industrial process), additional UI primitives, and worked example programs.