demo D295
A network operations centre
2,000 devices and 10,000 alarms on one feed: two grids, two charts and a KPI strip, with a live incident to watch the whole screen react
createDataRouter · router.link · router.attach(rollup)
One mixed feed of devices and alarms, routed to two grids, two charts and a KPI strip at once. Click a device and the alarm grid narrows to it; run the incident and watch the whole screen react to the same feed.
The configuration
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.50.0/lattice-grid.min.css">
<script src="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.50.0/lattice-grid.min.js"></script>
<div id="devices" style="height: 320px"></div>
<div id="alarms" style="height: 320px"></div>
<div id="severity-chart" style="height: 220px"></div>
<script type="module">
import { createDataRouter } from '@toclocoinc/lattice-grid/modules/data-router';
import { createChart } from '@toclocoinc/lattice-grid/modules/charts';
const devices = LatticeGrid.createGrid(document.getElementById('devices'), {
rowKey: 'id', selection: { mode: 'single' },
state: { sort: [{ col: 'trend', dir: 'asc' }] },
columns: [
{ field: 'site', title: 'Site' },
{ field: 'hostname', title: 'Device' },
{ field: 'status', title: 'Status' },
{ field: 'latency', title: 'Latency', type: 'number' },
// A shadow column: each device's health against the value it held when
// this grid first attached, falling sharply the moment a device degrades.
{ id: 'trend', title: 'Trend', shadow: { of: 'health', kind: 'delta' }, type: 'number' },
],
});
const alarms = LatticeGrid.createGrid(document.getElementById('alarms'), {
rowKey: 'id',
columns: [
{ field: 'severity', title: 'Severity' },
{ field: 'device', title: 'Device' },
{ field: 'description', title: 'Description' },
{ field: 'timestamp', title: 'Started', type: 'datetime' },
],
});
const severityMetrics = LatticeGrid.createHeadlessGrid({
rowKey: 'severity', columns: [{ field: 'severity' }, { field: 'count', type: 'number' }],
});
// One partition key, three routes. overlap: true because the severity chart
// reads the same 'alarm' value the alarms grid already claims - without it
// only the first-attached route on a value would receive it. Only open
// alarms reach the alarms grid and the chart's rollup.
const router = createDataRouter({ key: 'type', rowKey: 'id', overlap: true });
router.attach(devices, 'device');
router.attach(alarms, 'alarm', { filter: (r) => r.status === 'open' });
router.attach(severityMetrics, 'alarm', {
filter: (r) => r.status === 'open',
rollup: { groupBy: 'severity', aggregate: { count: { op: 'count' } } },
});
// Click a device and the alarms grid narrows to its own alarms, on the
// deviceId both partitions share.
router.link(devices, alarms, { from: 'deviceId', to: 'deviceId' });
createChart({ grid: severityMetrics, container: document.getElementById('severity-chart'), type: 'bar', x: 'severity', y: 'count', title: 'Open alarms by severity' });
router.load([...deviceRows, ...alarmRows]);
// The live feed: a mixed batch of device telemetry and new alarms, delivered
// to only the views each record belongs to.
socket.onmessage = (event) => router.apply(JSON.parse(event.data));
</script>
A whole operations screen from one feed
A network operations centre is the shape a live desk usually takes to its most demanding extreme: thousands of devices, a stream of alarms, a handful of headline figures and a couple of trend lines, all supposed to agree with each other from one moment to the next. Here two thousand devices and ten thousand alarm records are one feed into one Data Router, partitioned by what each record is. Device records land in the Devices grid; alarm records land in the Active Alarms grid, held to the ones still open. A KPI strip and two charts read the very same partitioned stream, so a number on a tile and a bar on a chart are never a stale read of a screen that has already moved on.
The devices and alarms are not just two grids sharing a page - they are related. Every alarm carries the deviceId of the device it belongs to, and the router’s link ties the two grids on that field. Click a device and the alarms grid narrows to its own alarms on the spot; clear the selection and it opens back up. That is the whole trick behind drilling from a summary into the record behind it, and here it costs one line of configuration rather than a query written by hand.
Watching a real incident, live
The SIMULATE NETWORK INCIDENT button degrades twenty devices at one site over a few seconds: their latency and packet loss climb, and a shadow column - Trend, tracking each device’s health against where it stood when the page opened - falls sharply. New alarms open against those devices as they degrade, the KPI strip’s alarm and SLA figures move, both charts move, and because the Devices grid is sorted on that same Trend column, the affected devices climb straight to the top on their own. Nothing here is a separate demo mode: the incident pushes rows through the identical feed the ambient traffic already uses, the same way a real network’s fault stream is just more of the same feed, not a different one.
How do I split one feed into a grid, a linked grid, a chart and a KPI strip?
Create one router with createDataRouter({ key: 'type', rowKey: 'id', overlap: true }) and call router.attach(view, value, opts) once per view: a grid for 'device', a grid for 'alarm' (with a filter to keep only the open ones), a headless grid feeding a chart, and a KPI panel. overlap: true matters as soon as more than one view reads the same value - the KPI strip and the severity chart both read 'device'/'alarm' alongside the grids that already claim them, and without it only the first-attached view on a value would ever see it. router.link(devices, alarms, { from: 'deviceId', to: 'deviceId' }) makes a pick in the devices grid narrow the alarms grid to the same device. For the alarms-by-severity chart, attach a headless grid with rollup: { groupBy: 'severity', aggregate: { count: { op: 'count' } } } and the router keeps one summary row per severity, updated by keyed diff as alarms open and close.