HMI Stack Demo#
This project demonstrates a modern tech stack for building a Human-Machine Interface (HMI) that can be deployed as a web application.
Overview#
The stack consists of three main components:
- HMI Server - A C++ service using Apache Thrift for RPC, running on TCP
- Directory Agent - A C++ WebSocket proxy that enables web and firewall-friendly access
- HMI Client - A Rust/egui application compiled to WebAssembly
Architecture#
The server communicates with clients via Apache Thrift. For web deployment and firewall accessibility, a WebSocket proxy (directory_agent) forwards client connections to the backend services over plain TCP.
┌─────────────┐ ┌─────────────────┐ ┌─────────────┐
│ Web │ WS │ Directory │ TCP │ HMI │
│ Browser │────────▶│ Agent │────────▶│ Server │
│ (WASM) │ (8080) │ (proxy) │ (9090) │ │
└─────────────┘ └─────────────────┘ └─────────────┘
│
▼
┌────────────────────┐
│ Vite Dev Server │
│ + WASM + Thrift │
└────────────────────┘
Client Architecture (Rust/egui + WASM)#
The HMI Client uses Rust with egui for the UI, compiled to WebAssembly:
- Rust/egui: UI framework compiled to WASM
- wasm-bindgen: Rust-JavaScript interop
- Thrift (JavaScript): Handles Thrift networking via WebSocket in the browser
- Vite: Build tool and dev server
Getting Started#
Prerequisites#
- C++ compiler with C++23 support
- CMake 3.28+
- Conan 2.x
- Node.js 18+ with npm
justcommand runner- Apache Thrift CLI (optional - only needed if modifying
hmi.thrift) - Rust toolchain with
wasm32-unknown-unknowntarget wasm-bindgenCLI
Build#
Full build (server + client):
# Install all dependencies
just install-deps
# Configure C++ build
just configure
# Build everything (server + embedded web client)
just build
Note: Run just codegen only after modifying hmi.thrift to regenerate the Thrift bindings.
Quick rebuild (after code changes):
just build
Server only:
just conan
just configure
just build
Client only:
just client-deps
just client-wasm
just client-build
Run#
# Terminal 1: Start the Thrift server
./server/build/Release/hmi-server
# Terminal 2: Start the directory agent (serves both WebSocket proxy and embedded web client)
./server/build/Release/directory_agent
# Open browser: http://localhost:8080/hmi/9090
The directory agent serves the embedded web client at:
http://localhost:8080/hmi/9090- serves index.html with WASM clienthttp://localhost:8080/assets/*- serves static assets (JS, WASM)
The client connects to the WebSocket proxy at ws://localhost:8080/hmi/9090.
Production Build#
The production build embeds the web client assets (HTML, JS, WASM) directly into the directory_agent executable, so no separate server is needed for the web frontend.
# Build - this automatically builds the client and embeds it
just build
# Run - directory_agent serves both the API proxy and the web UI
./server/build/Release/directory_agent
Clean#
# Remove all build artifacts (keeps generated Thrift code)
just clean
Key Technologies#
- Apache Thrift: Interface definition language and RPC framework
- Vite: Next-generation frontend build tool
- Rust/egui: UI framework compiled to WebAssembly
- wasm-bindgen: Rust-JavaScript interoperability
- Boost.Beast: C++ WebSocket library (Boost.Asio)
- C++23: Modern C++ with coroutines