DetectorGraph  2.0
DetectorGraph: Formal C++ applications with logic/data separation, automatic dependency injection and data passing.

DetectorGraph is a framework for writing programs in a formal graph topology. This can be used to write applications with multiple interdependent algorithms, applications' data models, general business logic or all of that combined. The framework uses a formal distinction between data (Topics) and transformations/logic (Detectors). It natively provides dependency injection, strict type-safety and provides loose coupling between Detectors by formalizing the touch points as Topics. It forces an intuitive (albeit unusual) programming paradigm that results in highly readable, maintainable & testable code.

This is not an officially supported Google product.

Full online documentation.

Note that the cross-reference links in this page are only rendered in the Doxygen version of the documentation (see Building). You can also navigate the web version of the documentation hosted at https://google.github.io/detectorgraph/.

Getting Started

Usage

Applications are written as a combination of Detectors and Topics.

Topics carry a particular signal/data-type:

struct FooSensorData : public DetectorGraph::TopicState
{
    int x;
}

Detectors encode a logical unit that describes the transformation from any number of Topics to any number of other Topics:

class BarThresholdDetector : public DetectorGraph::Detector,
    public DetectorGraph::SubscriberInterface<FooSensorData>,
    public DetectorGraph::Publisher<BazThresholdCrossing>
{
    BarThresholdDetector(DetectorGraph::Graph* graph) : DetectorGraph::Detector(graph)
    {
        Subscribe<FooSensorData>(this);
        SetupPublishing<BazThresholdCrossing>(this);
    }
    virtual void Evaluate(const FooSensorData& data)
    {
        if (data.x > 100)
        {
            Publish(BazThresholdCrossing(data.x));
        }
    }
}

DetectorGraphs are created by adding any number of Detectors to a Graph. All necessary Topics are created on demand and supplied via Dependency Injection.

DetectorGraph::Graph graph;
BarThresholdDetector detector(&graph);

Detectors and Topics are kept sorted in topological order.

Graph Evaluations start after data is posted to a Topic. This causes all Detector's `Evaluate()` methods for that Topic to be called with the new piece of data which in turn may result in new data being posted to subsequent Topics. That may then trigger the Evaluate() of other Detectors. This process continues following the topological order until the end of the Graph is reached or until no more Topics with subscribers have new data.

digraph { FooSensorData -> ThresholdDetector -> ThresholdCrossing }

Graphs can be visualized with Graphviz

User Guide

Below are a number of examples showcasing different aspects & usage patterns of the framework (these are in the ./examples/ folder).

Porting

The library has no dependencies beyond C++ & STL (min C++0x) and was designed to be fully portable to any environment supporting those dependencies.

Platform Implementations

The library uses abstractions for basic things like asserts & logging to allow for project & platform specific customization. The library is shipped with a basic set of implementations for those basic functions in ./platform_standalone.

Timers / Time Integration

To enable time-aware functionality (e.g. `PublishOnTimeout`, `GetTime`, `SetupPeriodicPublishing`) you must provide a concrete implementation of `TimeoutPublisherService` and pass that to your Detectors upon construction.

Runtime Integration

There are multiple ways of integrating `Graph` into your application. A good place to start is sub-classing `ProcessorContainer`, adding:

A more powerful & flexible option is to implement your own container to hold the `Graph` and `Detector` objects and orchestrate calls to `PushData<T>()`, `EvaluateGraph()` and either inspect particular Topics (retrievable via `ResolveTopic<T>()`) or iterate through all modified Topics using `GetOutputList()`.

Building

Tests, Docs and Examples

The library is shipped with a bare-bones makefile that can be used to build & run all of the examples, unit tests, documentation and coverage report.

For your project

The library is mostly header-only (only 3 core compilation units) and has a trivial compilation process. So instead of providing a binary or a complete build system we recommend that users use their build system of choice to build the library in whatever way fits their needs better. For examples on how to do that, please check `makefile`.

Why another Graph compute framework?

DetectorGraph shares a lot of its core concepts with other frameworks based in computation graphs (e.g. ROS, TensorFlow etc) but has also many differences. Some of its most unique features are:

Style & Tips

For an article containing a set of guidelines & rules of thumb that we accumulated after 3+ years of using DetectorGraph visit Style Tips - Patterns, Anti-Patterns & Suggestions. These are aimed at keeping software design constrained in a way that best takes advantage of the DetectorGraph framework, its expressibility and modeling power.

Naming

The DetectorGraph library had a little naming problem growing up. From birth it came to replace nlDetectorGraph and so it pretended to be called that way. As an adolescent it decided it wanted to be called MarkII.. but no one cared - for years now the world continues to call it simply DetectorGraph. Now as it approaches adulthood it has finally accepted its popular name: DetectorGraph.

In-depth Docs & API Reference

For in depth documentation of the library, start here.