Skip to main content

Correlate alarms with the graph

Two alarms fire within seconds of each other. Are they one incident or two? A flat lookup can't tell you — but the relationship graph can. If both alarmed sensors sit under the same subsystem, it's almost certainly one root cause, not two coincidences.

fetchRelated walks the graph outward from a node and returns the connected sub-graph. Walk the neighbourhood of each alarm and intersect the two: a shared ancestor — say a cooling_system both sensors are PART_OF — is the common cause.

Needs a sandbox

This guide reads data it does not create. Seed a sandbox writes it in one go.

Why a flat read isn't enough

byIds returns a resource and its direct edges only. A shared subsystem might be several hops away (sensor → pump → skid → cooling_system). Graph traversal follows the chain; a single-hop read stops at the first neighbour.

1. From alarms to resources​

An alarm is an event that references the resource it concerns (relatedResources). Start from the two resources the alarms point at — here sensor_a and sensor_b.

2. Walk each alarm's neighbourhood​

Fetch the sub-graph around each sensor. Bounding to PART_OF edges keeps the walk to the structural hierarchy (containment), ignoring incidental links.

RelatedResourcesForm a = new RelatedResourcesForm();
a.setExternalId("sensor_a");
a.setDepth(5);
a.setRelationshipTypes(List.of("PART_OF"));

RelatedResourcesForm b = new RelatedResourcesForm();
b.setExternalId("sensor_b");
b.setDepth(5);
b.setRelationshipTypes(List.of("PART_OF"));

ResourceNetwork na = client.resources().fetchRelated(a);
ResourceNetwork nb = client.resources().fetchRelated(b);

3. Find the shared subsystem​

Intersect the two node sets. Whatever remains (besides the sensors themselves) is a subsystem both alarms belong to — the likely common cause.

Set<String> aNodes = na.nodes().stream()
.map(Resource::getExternalId).collect(Collectors.toSet());

Set<String> shared = nb.nodes().stream()
.map(Resource::getExternalId)
.filter(aNodes::contains)
.collect(Collectors.toSet());
shared.remove("sensor_a");
shared.remove("sensor_b");

if (!shared.isEmpty()) {
System.out.println("Both alarms are part of: " + shared); // e.g. [cooling_system]
}
Deeper networks, smarter correlation

Raise depth to correlate across a deeper hierarchy — a whole plant rather than one skid. Drop the relationship-type filter to follow any connection (power feeds, data flows), not just containment, when a fault can propagate sideways.

The same pattern answers "what else might this failure affect?" — walk out from a failing component and the returned nodes are its blast radius.