Skip to content

rossonet/waldot

Repository files navigation

WaldOT

A Digital Twin Engine Bridging OPC UA and Apache TinkerPop Graphs

WaldOT logo

WaldOT is an innovative open-source project that seamlessly integrates industrial automation (OT - Operational Technology) with modern data analysis through graph databases. Built on Apache TinkerPop and Eclipse Milo OPC UA, it transforms OPC UA address spaces into queryable, reactive graph structures.

Why WaldOT?

Unified OT/IT Data Model

Traditional industrial systems expose data through OPC UA - a powerful but hierarchical protocol. WaldOT revolutionizes this by representing the entire OPC UA address space as a living graph database, where:

  • OPC UA Objects become TinkerPop Vertices
  • OPC UA References become Graph Edges
  • OPC UA Variables become Vertex Properties
  • Changes propagate in real-time in both directions

This enables industrial engineers and data scientists to work with the same data using their preferred tools: OPC UA clients for configuration, Gremlin queries for analysis.

Powerful Graph Queries with TinkerPop

WaldOT unlocks the full potential of Apache TinkerPop's Gremlin query language for industrial data. Instead of navigating rigid hierarchies, you can traverse complex relationships with expressive queries:

// Find all motors in alarm state connected to Line 1
g.V().has('name', 'Line1')
  .out('contains')
  .has('type', 'motor')
  .has('status', 'alarm')
  .values('name')

// Calculate average temperature across all sensors in a zone
g.V().has('zone', 'production')
  .has('type', 'temperature_sensor')
  .values('value')
  .mean()

// Find correlation patterns between equipment failures
g.V().has('event', 'failure')
  .as('failed')
  .in('connectedTo')
  .where(out('causedBy').as('failed'))
  .path()

These queries run directly on the OPC UA server, without external databases or ETL pipelines.

Reactive Rules Engine

WaldOT includes a sophisticated rules engine that enables edge computing logic without external systems. Define rules using simple IF-THEN patterns with full access to graph traversal:

Example: Temperature Monitoring

// Condition: Check if any sensor exceeds threshold
g.V().has('type', 'sensor')
  .has('temperature', gt(80))
  .hasNext()

// Action: Create alarm and notify
alarmTemp = g.V().has('type','sensor').has('temperature',gt(80)).values('temperature').next();
graph.addVertex('type', 'alarm', 'temperature', alarmTemp, 'timestamp', System.currentTimeMillis());
log.error('High temperature alert: ' + alarmTemp + '°C');

Rules can:

  • Query the graph using Gremlin in conditions
  • Modify the graph creating/updating vertices in actions
  • React to events with configurable hysteresis (debouncing)
  • Execute in parallel with priority-based scheduling
  • Integrate with external systems via HTTP, message buses, etc.

Edge-First Architecture

WaldOT is designed to run on-device - directly on RTUs (Remote Terminal Units), industrial PCs, or edge gateways:

  • Low latency: No cloud round-trips for rule evaluation
  • Offline capable: Operates without internet connectivity
  • Bandwidth efficient: Only sends aggregated/filtered data upstream
  • Secure: Data processing happens within the OT network perimeter

Standards-Based & Extensible

Built on proven open standards:

  • OPC UA for industrial connectivity (Eclipse Milo)
  • Apache TinkerPop 3.x for graph operations
  • Gremlin for query language
  • JEXL for rule expressions
  • Plugin architecture for custom extensions

Quick Start

Docker

docker pull rossonet/waldot:latest
docker run -p 4840:4840 -p 8182:8182 -p 8080:8080 rossonet/waldot

Access:

  • OPC UA Server: opc.tcp://localhost:4840
  • Gremlin Console: ws://localhost:8182/gremlin
  • REST API: http://localhost:8080/api

Build from Source

git clone https://github.com/rossonet/waldot.git
cd waldot
./gradlew clean build
java -jar waldot-app/build/libs/waldot-app-*.jar

Maven/Gradle Dependency

<!-- Maven -->
<dependency>
    <groupId>net.rossonet.waldot</groupId>
    <artifactId>waldot-api</artifactId>
    <version>0.6.1</version>
</dependency>
// Gradle
implementation 'net.rossonet.waldot:waldot-api:0.6.1'

Use Cases

1. Predictive Maintenance

Query historical patterns and correlations:

// Find equipment that failed within 24h after temperature spike
g.V().has('type', 'equipment')
  .where(
    out('hasEvent').has('type', 'failure').as('failure')
    .V().has('type', 'temperature_sensor')
      .has('value', gt(90))
      .has('timestamp', within(failure.timestamp - 86400000, failure.timestamp))
  )

2. Energy Optimization

Aggregate and analyze consumption:

// Total energy consumption per production line
g.V().has('type', 'production_line')
  .group()
    .by('name')
    .by(out('contains').values('energy_kwh').sum())

3. Quality Control

Trace product genealogy:

// Find all batches that used a specific raw material lot
g.V().has('lot_number', 'LOT12345')
  .in('usedIn')
  .in('producedBy')
  .values('batch_id')

4. Real-Time Alerting

React to complex conditions:

// Rule: Detect anomalous pump behavior
// Condition:
g.V().has('id', 'pump1').next().property('vibration').value() > threshold &&
g.V().has('id', 'pump1').next().property('flow').value() < minFlow

// Action:
graph.addVertex('type', 'maintenance_request', 'equipment', 'pump1', 'priority', 'high');
// Send notification via REST/MQTT/etc.

Resources

Documentation

Links

Example: Complete Digital Twin Application

Here's a minimal example showing WaldOT's key features:

// 1. Initialize WaldOT graph
WaldotGraph graph = OpcFactory.getOpcGraph("file:///tmp/waldot.db", new LoggerHistoryStrategy());

// 2. Create industrial equipment model
Vertex productionLine = graph.addVertex(
    "id", "line1", 
    "type", "production_line", 
    "name", "Assembly Line 1"
);

Vertex motor1 = graph.addVertex(
    "id", "motor1", 
    "type", "motor",
    "rpm", 1500,
    "temperature", 45.5,
    "status", "running"
);

Vertex sensor1 = graph.addVertex(
    "id", "temp_sensor_1",
    "type", "temperature_sensor", 
    "value", 45.5,
    "unit", "celsius"
);

// 3. Create relationships
productionLine.addEdge("contains", motor1);
motor1.addEdge("monitors", sensor1);

// 4. Define reactive rule
Vertex rule = graph.addVertex(
    "id", "overheat_rule",
    "type", "rule",
    "name", "Motor Overheat Detection",
    "condition", "g.V().has('id','temp_sensor_1').next().property('value').value() > 75",
    "action", """
        temp = g.V().has('id','temp_sensor_1').next().property('value').value();
        log.warn('Motor overheating detected: ' + temp + '°C');
        g.V().has('id','motor1').next().property('status', 'alarm');
        graph.addVertex('type','alert','equipment','motor1','temperature',temp,'timestamp',System.currentTimeMillis());
    """,
    "hysteresis", 5000  // 5 second debounce
);

// 5. Create compute manager
Vertex compute = graph.addVertex(
    "id", "compute_manager",
    "type", "compute",
    "threads", 2
);

// 6. Wire rule execution
sensor1.addEdge("fire", rule);              // Trigger on sensor change
compute.addEdge("execute", rule, "priority", 100);  // Execution priority

// 7. Now the system is live! Query from Gremlin or OPC UA client
// Gremlin query:
graph.traversal()
    .V().has('type', 'motor')
    .has('status', 'alarm')
    .values('name')
    .toList();

// OPC UA: Browse to opc.tcp://localhost:4840 and navigate the address space

Advanced Features

TinkerPop Graph Algorithms

WaldOT supports TinkerPop's graph algorithms for industrial analytics:

// Shortest path between equipment
g.V().has('name', 'Pump1')
  .repeat(out().simplePath())
  .until(has('name', 'Tank5'))
  .path()
  .by('name')

// PageRank to find most connected equipment
g.V().pageRank().by('rank').values('rank').order().desc()

// Community detection for grouping related assets
g.V().connectedComponent().by('component').group().by('component')

Extensibility via Plugins

Create custom vertex types and behaviors:

@WaldotPlugin
public class MyIndustrialPlugin implements PluginListener {
    @Override
    public void initialize(WaldotNamespace namespace) {
        // Register custom "Conveyor" vertex type in OPC UA
        createConveyorTypeNode(namespace);
    }
    
    @Override
    public WaldotVertex createVertex(NodeId typeNodeId, ...) {
        return new ConveyorVertex(...);  // Custom logic
    }
}

Integration Capabilities

  • REST API: HTTP endpoints for external systems
  • Gremlin Server: WebSocket protocol (port 8182)
  • OPC UA Client: Connect to other OPC UA servers
  • Message Bus: Zenoh pub/sub connector for edge-to-cloud
  • JDBC: Export graph data to SQL databases
  • GraphQL: Query via GraphQL over HTTP

Performance & Scalability

  • Edge-optimized: Runs on devices with 1GB+ RAM
  • Virtual threads: Java 21 for massive concurrency
  • Persistent storage: RocksDB backend for large graphs
  • Streaming: Process millions of data points without memory issues
  • Distributed: Cluster support via TinkerPop providers (Neo4j, JanusGraph, etc.)

Development Tools

Gitpod ready-to-code Codacy Badge

CI/CD Status

Test all subprojects with Gradle Build WaldOT shadowJar Build and publish WaldOT docker image to Docker Hub Publish Java artifacts to Maven Central

Contributing

We welcome contributions! Whether you're:

  • Adding new plugins
  • Improving documentation
  • Reporting bugs
  • Suggesting features

Please see AGENT.md for development guidelines.

Key contribution areas:

  • Additional storage connectors (Neo4j, JanusGraph, etc.)
  • Rule engine extensions (SQL-like DSL, visual editor)
  • Dashboard integrations (Grafana, real-time graph visualization)
  • Protocol bridges (MQTT, Modbus, BACnet)
  • AI/ML model integration

References

License

WaldOT is released under the Apache License 2.0.

See LICENSE file for details.

Project Sponsor

Rossonet s.c.a r.l.


Ready to bridge your OT and IT worlds? Get started with WaldOT today!

About

TinkerPop Gremlin backend OPC UA

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

 
 
 

Contributors

Languages