Getting Started with Varpulis
This tutorial will get you up and running with Varpulis in under 5 minutes. You'll learn to write, validate, and run your first VPL program.
Prerequisites
- Rust 1.93+: Install via rustup
- Git: For cloning the repository
- Optional: MQTT broker (Mosquitto) for real-time streaming
Installation
From Source
# Clone the repository
git clone https://github.com/varpulis/varpulis.git
cd varpulis
# Build the CLI
cargo build --release
# Add to PATH (optional)
export PATH="$PATH:$(pwd)/target/release"
# Verify installation
varpulis --version
# Output: varpulis 0.6.0Quick Build Check
# Run tests to verify everything works
cargo test --workspaceYour First VPL Program
Let's create a simple temperature monitoring program.
Step 1: Create the Program File
Create a file called temperature_monitor.vpl:
// temperature_monitor.vpl
// Simple temperature monitoring with alerts
// Define what events we're listening for
stream TemperatureReadings = TemperatureReading
.where(temperature > 0) // Basic validation
// Create an alert stream for high temperatures
stream HighTempAlerts = TemperatureReading
.where(temperature > 100)
.emit(
alert_type: "HighTemperature",
message: "Temperature exceeded 100 degrees"
)Step 2: Validate the Syntax
Before running, check that your program has valid syntax:
varpulis check temperature_monitor.vplExpected output:
Syntax OK
Statements: 2If there's an error, you'll see helpful diagnostics:
Syntax error: unexpected token at line 5, column 10
Hint: Expected 'where', 'select', or 'emit'
|
| stream HighTempAlerts form TemperatureReading
| ^^^^Step 3: View the AST (Optional)
To understand how Varpulis parses your program:
varpulis parse temperature_monitor.vplThis shows the Abstract Syntax Tree, useful for debugging complex expressions.
Step 4: Run a Simulation
Create a test event file called test_events.evt:
# test_events.evt
# Format: @delay_ms EventType { field: value, ... }
@0 TemperatureReading { sensor_id: "sensor-1", temperature: 72.5, unit: "F" }
@100 TemperatureReading { sensor_id: "sensor-2", temperature: 68.0, unit: "F" }
@200 TemperatureReading { sensor_id: "sensor-1", temperature: 105.2, unit: "F" }
@300 TemperatureReading { sensor_id: "sensor-3", temperature: 71.8, unit: "F" }
@400 TemperatureReading { sensor_id: "sensor-1", temperature: 112.5, unit: "F" }Now simulate the events:
varpulis simulate \
--program temperature_monitor.vpl \
--events test_events.evt \
--verboseExpected output:
Varpulis Event Simulation
============================
Program: temperature_monitor.vpl
Events: test_events.evt
Mode: timed
Workers: 1
Program loaded: 2 streams
Starting simulation...
[ 1] @ 0ms TemperatureReading { ... }
[ 2] @ 100ms TemperatureReading { ... }
[ 3] @ 200ms TemperatureReading { ... }
ALERT: HighTemperature - Temperature exceeded 100 degrees
sensor_id: sensor-1
temperature: 105.2
[ 4] @ 300ms TemperatureReading { ... }
[ 5] @ 400ms TemperatureReading { ... }
ALERT: HighTemperature - Temperature exceeded 100 degrees
sensor_id: sensor-1
temperature: 112.5
Simulation Complete
======================
Duration: 0.402s
Events processed: 5
Workers used: 1
Alerts generated: 2
Event rate: 12.4 events/secFaster Processing with Parallel Workers
By default, simulate runs in fast mode -- it preloads all events into memory and processes them as fast as possible with no timing delays. To scale across multiple CPU cores:
varpulis simulate \
--program temperature_monitor.vpl \
--events test_events.evt \
--workers 8 \
--partition-by sensor_idIf you want to replay events with real-time timing delays (e.g., to observe how alerts trigger over time), use --timed:
varpulis simulate \
--program temperature_monitor.vpl \
--events test_events.evt \
--timedAdding Aggregations
Let's enhance our program with windowed aggregations:
// temperature_monitor_v2.vpl
// Raw temperature readings
stream Readings = TemperatureReading
// Calculate average temperature over 1-minute windows
stream AvgTemperature = TemperatureReading
.window(1m)
.aggregate(
avg_temp: avg(temperature),
max_temp: max(temperature),
min_temp: min(temperature),
reading_count: count()
)
.print("Minute summary: avg={avg_temp}, max={max_temp}, min={min_temp}")
// Alert on sustained high temperatures (average > 90 over window)
stream SustainedHighTemp = TemperatureReading
.window(1m)
.aggregate(avg_temp: avg(temperature))
.where(avg_temp > 90)
.emit(
alert_type: "SustainedHighTemp",
avg_temp: avg_temp,
message: "Average temperature exceeds threshold"
)Running the Built-in Demo
Varpulis includes a HVAC building monitoring demo:
# Run for 30 seconds with simulated anomalies
varpulis demo --duration 30 --anomalies
# With Prometheus metrics
varpulis demo --duration 60 --anomalies --metrics --metrics-port 9090Then open http://localhost:9090/metrics to see real-time metrics.
Next Steps
Now that you have Varpulis running:
Learn the Language: Read the VPL Language Tutorial for comprehensive coverage of streams, patterns, windows, and more.
Explore Windows: See the Windows & Aggregations Reference for tumbling, sliding, and count-based windows.
Pattern Matching: Learn about SASE+ patterns in the Pattern Guide for detecting complex event sequences.
Production Setup: Review the Configuration Guide for MQTT integration, TLS, and deployment options.
Quick Reference
| Command | Purpose |
|---|---|
varpulis check file.vpl | Validate syntax |
varpulis parse file.vpl | Show AST |
varpulis simulate -p file.vpl -e events.evt | Run simulation |
varpulis simulate ... --workers 8 | Parallel mode |
varpulis run --file file.vpl | Run with MQTT |
varpulis demo | Built-in HVAC demo |
varpulis server | Start WebSocket API |
Troubleshooting
"Parse error: unexpected token"
- Check for typos in keywords (
where,emit) - Ensure strings are quoted:
"value"notvalue - Verify brackets match:
{ }for blocks,( )for function calls
"No events processed"
- Check event file format matches expected event types
- Verify the stream declaration (
stream Name = EventType) matches the event type exactly
"Simulation runs slowly"
- The default mode already runs as fast as possible (no timing delays, events preloaded)
- Use
--workers Nfor parallel processing - If using
--timedor--streaming, switch to the default fast mode for maximum throughput
For more help, see the Troubleshooting Guide.