-
Notifications
You must be signed in to change notification settings - Fork 0
API Reference: Event Detection
The EventDetection library groups threshold violations into discrete events with statistics, provides console reporting, and includes a Gantt-style viewer UI with click-to-plot functionality. Features include severity escalation, persistent event storage with backups, and live auto-refresh from file.
Core detection engine. Groups consecutive threshold violation points into Event objects, applies debounce filtering, computes statistics, and invokes callbacks.
det = EventDetector();
det = EventDetector('MinDuration', 0.5);
det = EventDetector('MinDuration', 2, 'OnEventStart', @myCallback, 'MaxCallsPerEvent', 3);| Property | Type | Default | Description |
|---|---|---|---|
MinDuration |
double | 0 |
Debounce filter: discard events shorter than this (in time units matching your X data). Set to 0 to keep all events |
OnEventStart |
function_handle | [] |
Callback invoked as f(event) for each detected event |
MaxCallsPerEvent |
double | 1 |
Maximum callback invocations per event (prevents duplicate notifications) |
Detect events from raw time series data and a threshold.
events = det.detect(t, values, 4.5, 'high', 'High Limit', 'Pressure');
events = det.detect(t, values, -2.0, 'low', 'Low Limit', 'Temperature');| Parameter | Type | Description |
|---|---|---|
t |
1xN double | Time stamps |
values |
1xN double | Data values |
thresholdValue |
double | Threshold to check against |
direction |
char |
'high' (value > threshold) or 'low' (value < threshold) |
thresholdLabel |
char | Label for the threshold (e.g., 'Run HI') |
sensorName |
char | Name of the sensor/channel |
Returns: Array of Event objects. Returns [] if no events are found.
Algorithm:
- Call the private helper
groupViolations()to find all points where the value crosses the threshold in the given direction, and cluster consecutive violation points into groups (each group has astartIdxandendIdx) - For each group, compute
startTime,endTime, andduration - Filter out events shorter than
MinDuration(debounce) - Compute statistics for each event over the data window
values(startIdx:endIdx):-
PeakValue:
maxfor'high'direction,minfor'low'direction - NumPoints, MinValue, MaxValue, MeanValue, RmsValue, StdValue
-
PeakValue:
- Invoke
OnEventStartcallback for each event (respectingMaxCallsPerEvent)
% Simple detection
det = EventDetector('MinDuration', 0.5);
t = linspace(0, 100, 1e6);
y = randn(1, 1e6) * 10 + 50;
events = det.detect(t, y, 70, 'high', 'High', 'Pressure');
fprintf('Found %d events\n', numel(events));% With live logging callback
det = EventDetector('MinDuration', 1.0, 'OnEventStart', eventLogger());
events = det.detect(t, y, 70, 'high', 'High', 'Pressure');
% Console output: [EVENT] Pressure | High | HIGH | 12.50 -> 14.20 (dur=1.70) | peak=82.30Value class holding metadata and statistics for a single detected event.
| Constant | Value | Description |
|---|---|---|
DIRECTIONS |
{'high', 'low'} |
Valid direction values |
| Property | Type | Description |
|---|---|---|
StartTime |
double | Timestamp of first violation point |
EndTime |
double | Timestamp of last violation point |
Duration |
double | EndTime - StartTime |
SensorName |
char | Name of the sensor/channel |
ThresholdLabel |
char | Label of the threshold that was violated |
ThresholdValue |
double | The threshold value |
Direction |
char |
'high' or 'low'
|
PeakValue |
double | Worst violation value (furthest from threshold) |
NumPoints |
double | Number of data points within the event time window |
MinValue |
double | Minimum signal value during event |
MaxValue |
double | Maximum signal value during event |
MeanValue |
double | Mean signal value during event |
RmsValue |
double | RMS signal value during event |
StdValue |
double | Standard deviation during event |
e = Event(startTime, endTime, sensorName, thresholdLabel, thresholdValue, direction);| Parameter | Type | Description |
|---|---|---|
startTime |
double | First violation timestamp |
endTime |
double | Last violation timestamp (must be >= startTime) |
sensorName |
char | Sensor/channel name |
thresholdLabel |
char | Threshold label |
thresholdValue |
double | Threshold value |
direction |
char | Must be 'high' or 'low'
|
Errors:
-
Event:invalidDirectionif direction is not'high'or'low' -
Event:invalidTimeRangeifendTime < startTime
Note: Event objects are normally created by EventDetector.detect(), not directly.
Set the statistical fields. Called internally by EventDetector. Since Event is a value class, this returns a new object with the stats set.
e = e.setStats(peakVal, nPts, minVal, maxVal, meanVal, rmsVal, stdVal);Escalate an event to a higher severity threshold. Used by the severity escalation system. Returns a new Event with updated label and threshold value while preserving all other fields and statistics.
e = e.escalateTo('temp critical', 95);| Parameter | Type | Description |
|---|---|---|
newLabel |
char | New threshold label |
newThresholdValue |
double | New threshold value |
Configuration container that orchestrates event detection across multiple sensors. Bridges the SensorThreshold and EventDetection libraries. Supports severity escalation, persistent event storage with backups, and auto-open viewer.
cfg = EventConfig();| Property | Type | Default | Description |
|---|---|---|---|
Sensors |
cell array | {} |
Registered Sensor objects |
SensorData |
struct array | [] |
Sensor data for viewer (fields: name, t, y) |
MinDuration |
double | 0 |
Debounce threshold |
MaxCallsPerEvent |
double | 1 |
Callback limit |
OnEventStart |
function_handle | [] |
Callback f(event)
|
ThresholdColors |
containers.Map | (empty) | Maps threshold labels to RGB color vectors |
AutoOpenViewer |
logical | false |
Auto-open EventViewer after detection |
EscalateSeverity |
logical | true |
Escalate events whose peak exceeds a higher threshold on the same sensor/direction |
EventFile |
char | '' |
Path to .mat file for auto-saving events. Empty = disabled |
MaxBackups |
double | 5 |
Number of timestamped backup files to keep before pruning. 0 = no backups |
Register a sensor. Automatically calls sensor.resolve() and stores the sensor's data (X, Y, and display name) for the viewer's click-to-plot feature.
cfg.addSensor(sensor);Set the display color for a threshold label in the viewer.
cfg.setColor('Run HI', [1 0 0]);
cfg.setColor('Boost HI', [1 0.5 0]);Create a configured EventDetector from the config properties.
det = cfg.buildDetector();Detect events across all registered sensors, then:
-
Severity escalation (if
EscalateSeverityistrue): for each event, check if its peak exceeds a higher threshold on the same sensor and direction. If so, escalate the event's label and threshold value to the higher one. Remove duplicate events that are contained within an escalated event with the same label. -
Auto-save (if
EventFileis set): create a timestamped backup of the existing file, then save events, sensor data, threshold colors, and a timestamp to the.matfile. Old backups are pruned toMaxBackups. -
Auto-open viewer (if
AutoOpenVieweristrue).
events = cfg.runDetection();Returns: Combined Event array across all sensors, after escalation.
When nested thresholds exist (e.g., warning at 85, critical at 95), detection runs each threshold independently. A brief spike above 95 may get debounced, but the longer warning event (above 85) survives. Without escalation, the user sees a "temp warning" with peak=96 — misleading.
With EscalateSeverity = true (default), the post-detection step checks the warning event's peak against all higher thresholds for the same sensor and direction. Since 96 > 95, the event is escalated from "temp warning" to "temp critical".
cfg.EscalateSeverity = true; % default — escalate
cfg.EscalateSeverity = false; % disable — keep original labelsHow it works for each direction:
-
'high': thresholds sorted ascending. Peak must exceed (>=) the higher threshold value. -
'low': thresholds sorted descending. Peak must be at or below (<=) the lower threshold value.
Set EventFile to auto-save events to a .mat file after every runDetection() call.
cfg.EventFile = 'my_events.mat';
cfg.MaxBackups = 5; % keep 5 timestamped backups (default)
cfg.MaxBackups = 0; % disable backups
events = cfg.runDetection(); % saves automaticallyThe .mat file contains:
| Field | Type | Description |
|---|---|---|
events |
Event array | All detected events |
sensorData |
struct array | Sensor time series (name, t, y) |
thresholdColors |
struct | Color map serialized as struct (label + rgb per entry) |
timestamp |
datetime | When the file was saved |
Backup files are created with a timestamp suffix before each overwrite:
my_events.mat ← current
my_events_20260309_143025.mat ← backup 1
my_events_20260309_142512.mat ← backup 2
Oldest backups are pruned when the count exceeds MaxBackups.
cfg = EventConfig();
cfg.MinDuration = 0.5;
cfg.OnEventStart = eventLogger();
cfg.EventFile = 'events.mat';
cfg.MaxBackups = 3;
cfg.addSensor(pressureSensor);
cfg.addSensor(temperatureSensor);
cfg.setColor('Run HI', [1 0 0]);
cfg.setColor('Boost HI', [1 0.5 0]);
events = cfg.runDetection();
% Events saved to events.mat with backup of previous versionFigure-based UI with a Gantt-style timeline, filter dropdowns, and a filterable event table. Supports click-to-plot for zooming into individual events via FastPlot with violation markers and toolbar.
viewer = EventViewer(events);
viewer = EventViewer(events, sensorData);
viewer = EventViewer(events, sensorData, thresholdColors);| Parameter | Type | Description |
|---|---|---|
events |
Event array | Detected events to display |
sensorData |
struct array | Fields: name, t, y (enables click-to-plot). Optional |
thresholdColors |
containers.Map | Maps threshold labels to [R G B] color vectors. Optional |
| Property | Type | Description |
|---|---|---|
Events |
Event array | All events (unfiltered) |
SensorData |
struct array | Sensor time series data for click-to-plot |
ThresholdColors |
containers.Map | Color mapping for threshold labels |
hFigure |
figure handle | Handle to the viewer figure |
Refresh the viewer with new events. Re-applies current filters, redraws the timeline, and repopulates the table.
viewer.update(newEvents);Get unique sensor names or threshold labels from the current events.
names = viewer.getSensorNames();
labels = viewer.getThresholdLabels();Reload events, sensor data, and colors from the source .mat file. Only available when the viewer was opened via fromFile(). Updates the Gantt chart, table, and status bar.
viewer.refreshFromFile();Start polling the source .mat file at the given interval (in seconds).
viewer.startAutoRefresh(5); % refresh every 5 secondsStop the auto-refresh timer.
viewer.stopAutoRefresh();Open an EventViewer directly from a saved .mat event store file (created by EventConfig with EventFile set). No sensors or config needed — all data is loaded from the file.
viewer = EventViewer.fromFile('events.mat');The viewer includes a refresh toolbar (only when opened via fromFile):
| Control | Description |
|---|---|
| [Refresh] button | Manual one-shot reload from file |
| [Auto] checkbox | Enable/disable auto-refresh timer |
| Interval input | Refresh interval in seconds (default 5) |
| Status bar | Shows last refresh time, event count, and source file path |
The figure title displays the timestamp from the saved file.
The viewer opens a figure window (1200x700, dark theme) with three sections:
Top Panel — Gantt Timeline:
- One row per sensor on the Y-axis, bars fill the full lane height (no gaps)
- Colored horizontal bars for each event
- Bar color is determined by
ThresholdColorsif a mapping exists, otherwise auto-assigned - X and Y grid lines in subtle gray for visual alignment
- Hover tooltips: hovering over a bar shows sensor name, threshold, direction, start/end times, duration, peak value, and point count
- Click-to-select: clicking a Gantt bar highlights it with a white border and highlights the corresponding table row with a blue background
Filter Dropdowns (middle):
- Sensor dropdown: filter by sensor name (or "All")
- Threshold dropdown: filter by threshold label (or "All")
- Changing either filter immediately redraws the timeline and table
Refresh Toolbar (when opened via fromFile):
- Refresh button, Auto checkbox, interval input, status bar
Bottom Panel — Event Table:
| Column | Description |
|---|---|
| Start | Event start time |
| End | Event end time |
| Duration | Event duration |
| Sensor | Sensor name |
| Threshold | Threshold label |
| Dir | Direction (high or low) |
| Peak | Peak violation value |
| #Pts | Number of data points in event |
| Min | Minimum value during event |
| Max | Maximum value during event |
| Mean | Mean value during event |
| RMS | RMS value during event |
| Std | Standard deviation during event |
Click-to-Plot:
Clicking any row in the table opens a new FastPlot window with:
- The sensor's full signal via
addLine - The violated threshold via
addThresholdwithShowViolationsenabled -
FastPlotToolbarfor interactive exploration - View zoomed to the event's time range with 20% padding
Requires SensorData to be provided.
% Direct usage with events
viewer = EventViewer(events, sensorData, colors);
viewer.update(newEvents); % live update% Open from saved file (e.g., from background detection process)
viewer = EventViewer.fromFile('events.mat');
% Manual refresh
viewer.refreshFromFile();
% Auto-refresh every 5 seconds
viewer.startAutoRefresh(5);
% Stop auto-refresh
viewer.stopAutoRefresh();% Background process writes events, viewer polls them:
%
% Process A (background):
% cfg.EventFile = '/shared/events.mat';
% while running
% cfg.runDetection(); % auto-saves to file
% pause(10);
% end
%
% Process B (viewer):
% viewer = EventViewer.fromFile('/shared/events.mat');
% viewer.startAutoRefresh(5); % polls every 5sBridge function that connects the SensorThreshold and EventDetection libraries.
events = detectEventsFromSensor(sensor); % Default EventDetector
events = detectEventsFromSensor(sensor, detector); % Custom detectorBehavior:
- Determines the sensor display name (
sensor.Nameif set, otherwisesensor.Key) - Iterates over
sensor.ResolvedViolations - Maps SensorThreshold directions:
'upper'→'high','lower'→'low' - Looks up threshold value from
sensor.ResolvedThresholds - Calls
detector.detect()on the full sensor signal for each threshold
Print a formatted event summary table to the console.
printEventSummary(events);Output format:
Start End Duration Sensor Threshold Dir Peak #Pts Mean Std
------------------------------------------------------------------------------------------------------------------------
12.50 14.20 1.70 Pressure Run HI high 82.30 170 75.40 4.21
...
2 event(s) total.
Factory function returning a callback for live console logging of events.
det = EventDetector('OnEventStart', eventLogger());Output: [EVENT] Pressure | Run HI | HIGH | 12.50 -> 14.20 (dur=1.70) | peak=82.30
Clusters consecutive threshold violations into groups using diff([0, mask, 0]) transition detection.
Returns: Struct array with startIdx and endIdx per group. Returns [] if no violations.
%% 1. Create sensors with thresholds
sTemp = Sensor('temperature', 'Name', 'Temperature');
sTemp.X = t; sTemp.Y = temp;
sTemp.addThresholdRule(struct(), 85, 'Direction', 'upper', 'Label', 'temp warning');
sTemp.addThresholdRule(struct(), 95, 'Direction', 'upper', 'Label', 'temp critical');
sPres = Sensor('pressure', 'Name', 'Pressure');
sPres.X = t; sPres.Y = pressure;
sPres.addThresholdRule(struct(), 4, 'Direction', 'lower', 'Label', 'pressure low');
%% 2. Configure detection with auto-save and escalation
cfg = EventConfig();
cfg.MinDuration = 0.5;
cfg.OnEventStart = eventLogger();
cfg.EventFile = 'events.mat'; % persistent storage
cfg.MaxBackups = 3; % keep 3 backups
cfg.addSensor(sTemp);
cfg.addSensor(sPres);
cfg.setColor('temp warning', [1.0 0.8 0.0]);
cfg.setColor('temp critical', [1.0 0.2 0.0]);
cfg.setColor('pressure low', [0.2 0.5 1.0]);
%% 3. Detect — events are escalated and saved to file
events = cfg.runDetection();
printEventSummary(events);
%% 4. View from file (e.g., in another session or by another user)
viewer = EventViewer.fromFile('events.mat');
viewer.startAutoRefresh(5); % poll for updates every 5s- API Reference: Sensors — Sensor, StateChannel, ThresholdRule
- API Reference: FastPlot — addSensor(), addLine(), addThreshold() methods
- Live Mode Guide — Live event detection with streaming data
-
Examples —
example_event_detection_live,example_event_viewer_from_file
Wraps EventDetector with incremental state for streaming / chunk-based processing. Tracks the last-processed index per sensor and carries over open events across chunks.
Superclass: handle
Source: libs/EventDetection/IncrementalEventDetector.m
ied = IncrementalEventDetector();
ied = IncrementalEventDetector('MinDuration', 2, 'OnEventStart', @myCallback);| Property | Type | Default | Description |
|---|---|---|---|
MinDuration |
double | 0 |
Debounce filter: discard events shorter than this |
MaxCallsPerEvent |
double | 1 |
Maximum callback invocations per event |
OnEventStart |
function_handle | [] |
Callback invoked as f(event) for each completed event |
EscalateSeverity |
logical | true |
Escalate events whose peak exceeds a higher threshold |
Process a new chunk of data for a sensor. Internally accumulates data, builds a temporary Sensor on the relevant slice, runs EventDetector, handles open/merged events, and returns newly completed events.
newEvents = ied.process('temperature', sTemp, newX, newY, stateX, stateY);| Parameter | Type | Description |
|---|---|---|
sensorKey |
char | Unique sensor identifier |
sensor |
Sensor | Source sensor (for threshold rules and state channels) |
newX |
1xN double | New time stamps |
newY |
1xN double | New data values |
newStateX |
1xM double | New state transition times (can be empty) |
newStateY |
1xM | New state values (can be empty) |
Returns: Array of completed Event objects (may be empty if all events are still open).
Behavior:
- Appends new data to the accumulated buffer for the sensor
- Determines the slice start (open event start or new data start)
- Builds a temporary Sensor with threshold rules and state channels on the slice
- Runs EventDetector on the slice
- Merges events with any previously open event
- Carries over events that are still ongoing at the end of the chunk
- Fires
OnEventStartcallback for completed events
Check whether a sensor currently has an unfinished event that spans the end of the last processed chunk.
tf = ied.hasOpenEvent('temperature');Get the internal state struct for a sensor (accumulated data, open event, etc.).
st = ied.getSensorState('temperature');ied = IncrementalEventDetector('MinDuration', 1.0, 'OnEventStart', eventLogger());
% Process data in chunks (e.g., from a live pipeline)
while hasMoreData
[newX, newY] = readChunk();
events = ied.process('pressure', sPressure, newX, newY, [], []);
if ~isempty(events)
fprintf('Detected %d new events\n', numel(events));
end
endMaps sensor keys to DataSource instances. Used by live pipelines to manage multiple data sources.
Superclass: handle
Source: libs/EventDetection/DataSourceMap.m
dsm = DataSourceMap();| Method | Signature | Description |
|---|---|---|
add |
add(key, dataSource) |
Register a DataSource under a string key. Throws if not a DataSource subclass. |
get |
get(key) |
Retrieve the DataSource for a key. Throws DataSourceMap:unknownKey if not found. |
keys |
keys() |
Return cell array of all registered keys. |
has |
has(key) |
Return true if the key exists. |
remove |
remove(key) |
Remove a key (no-op if not found). |
dsm = DataSourceMap();
dsm.add('temperature', myTempSource);
dsm.add('pressure', myPressSource);
allKeys = dsm.keys();
ds = dsm.get('temperature');Configures notification settings for sensor/threshold events. Matched against events by sensor key and threshold label. Supports template-based subject/message formatting.
Superclass: handle
Source: libs/EventDetection/NotificationRule.m
rule = NotificationRule('SensorKey', 'temperature', 'ThresholdLabel', 'Hi Alarm', ...
'Recipients', {{'ops@example.com'}});| Property | Type | Default | Description |
|---|---|---|---|
SensorKey |
char | '' |
Filter by sensor key (empty = match all) |
ThresholdLabel |
char | '' |
Filter by threshold label (empty = match all) |
Recipients |
cell | {{}} |
Email recipients |
Subject |
char | 'Event: {sensor} - {threshold}' |
Email subject template |
Message |
char | (see source) | Email body template |
IncludeSnapshot |
logical | true |
Attach a FastPlot snapshot image |
ContextHours |
double | 2 |
Hours of context data around event in snapshot |
SnapshotPadding |
double | 0.1 |
Padding around event in snapshot |
SnapshotSize |
1x2 double | [800 400] |
Snapshot image size in pixels |
Templates support these placeholders: {sensor}, {threshold}, {direction}, {startTime}, {endTime}, {duration}, {peak}, {mean}, {rms}, {std}, {thresholdValue}.
Returns a match score: 3 = sensor + threshold match, 2 = sensor match, 1 = default (no filters), 0 = no match.
Replace placeholders in a template string with event data.
Rule-based email notification service with event snapshot generation.
Superclass: handle
Source: libs/EventDetection/NotificationService.m
ns = NotificationService('Enabled', true, 'DryRun', true);
ns = NotificationService('SmtpServer', 'smtp.example.com', 'FromAddress', 'alerts@example.com');| Property | Type | Default | Description |
|---|---|---|---|
Rules |
NotificationRule array | [] |
Registered notification rules |
DefaultRule |
NotificationRule | [] |
Fallback rule when no specific rule matches |
Enabled |
logical | true |
Master enable/disable |
DryRun |
logical | false |
Print notifications to console instead of sending email |
SnapshotDir |
char | (tempdir) | Directory for snapshot images |
SnapshotRetention |
double | 7 |
Days to keep snapshots before cleanup |
SmtpServer |
char | '' |
SMTP server address |
FromAddress |
char | 'fastplot@noreply.com' |
Sender email address |
NotificationCount |
double | 0 |
Running count of notifications sent |
| Method | Description |
|---|---|
addRule(rule) |
Register a NotificationRule |
setDefaultRule(rule) |
Set the fallback rule |
findBestRule(event) |
Find the highest-scoring matching rule for an event |
notify(event, sensorData) |
Send notification for an event (generates snapshot, sends email) |
cleanupSnapshots() |
Delete snapshot files older than SnapshotRetention days |
ns = NotificationService('DryRun', true);
rule = NotificationRule('SensorKey', 'temperature', ...
'Recipients', {{'ops@example.com'}});
ns.addRule(rule);
% Called from event detection callback:
ns.notify(event, sensorData);
% [NOTIFY DRY-RUN] To: ops@example.com | Subject: Event: temperature - Hi AlarmFastPlot Wiki
API Reference
Guides
Use Cases
Internals
Resources