node-red-contrib-event-calc 3.3.3 → 3.3.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -55
- package/examples/alarm-simulation-example.json +288 -0
- package/examples/change-detection-example.json +448 -0
- package/examples/isa88-batch-example.json +278 -0
- package/examples/opcua-alarm-example.json +516 -0
- package/examples/test-flows.json +723 -0
- package/examples/throughput-test.json +293 -0
- package/nodes/event-alarm.html +252 -0
- package/nodes/event-alarm.js +292 -0
- package/nodes/event-cache.js +84 -33
- package/nodes/event-calc.js +132 -77
- package/nodes/event-flatten.html +53 -0
- package/nodes/event-flatten.js +25 -0
- package/nodes/event-frame.html +192 -0
- package/nodes/event-frame.js +246 -0
- package/nodes/simple-frame.html +125 -0
- package/nodes/simple-frame.js +126 -0
- package/package.json +6 -2
- package/node-red-contrib-event-calc-3.3.2.tgz +0 -0
- package/nul +0 -0
- package/playwright.config.js +0 -22
- package/tests/external-trigger.spec.js +0 -141
package/README.md
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
# node-red-contrib-event-calc
|
|
2
2
|
|
|
3
|
-
Node-RED nodes for event caching
|
|
3
|
+
Node-RED nodes for event caching, streaming calculations, alarm management, and event framing.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
7
|
This package provides a local in-memory event hub with topic-based publish/subscribe and latest-value caching for reactive data flows within Node-RED. Stream data from MQTT, OPC-UA, or any source, then perform calculations that trigger automatically when values update.
|
|
8
8
|
|
|
9
|
+
Also includes ISA-88 batch structure tracking, ISA-18.2 alarm lifecycle management, and simple event recording.
|
|
10
|
+
|
|
9
11
|
## Architecture
|
|
10
12
|
|
|
11
13
|
```
|
|
12
14
|
┌──────────────────────────────────────────────────────────────┐
|
|
13
15
|
│ event-cache (config node) │
|
|
14
|
-
│ • Stores: Map<topic, {value, ts, metadata}>
|
|
16
|
+
│ • Stores: Map<topic, {value, ts, metadata, previous}> │
|
|
15
17
|
│ • Event emitter for topic updates │
|
|
16
18
|
│ • LRU eviction, optional TTL │
|
|
17
19
|
└──────────────────────────────────────────────────────────────┘
|
|
@@ -34,14 +36,16 @@ Or install directly from the Node-RED palette manager.
|
|
|
34
36
|
|
|
35
37
|
## Nodes
|
|
36
38
|
|
|
37
|
-
###
|
|
39
|
+
### Core Nodes
|
|
40
|
+
|
|
41
|
+
#### event-cache (Config Node)
|
|
38
42
|
|
|
39
43
|
Central cache that stores topic values and manages subscriptions. Configure:
|
|
40
44
|
|
|
41
45
|
- **Max Entries**: Maximum topics to cache (default: 10000). Oldest entries removed when exceeded.
|
|
42
46
|
- **TTL**: Time-to-live in milliseconds. Set to 0 for no expiry.
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
#### event-in
|
|
45
49
|
|
|
46
50
|
Receives messages from any upstream node and pushes values to the cache.
|
|
47
51
|
|
|
@@ -51,7 +55,7 @@ Receives messages from any upstream node and pushes values to the cache.
|
|
|
51
55
|
|
|
52
56
|
The original message passes through, allowing insertion into existing flows.
|
|
53
57
|
|
|
54
|
-
|
|
58
|
+
#### event-topic
|
|
55
59
|
|
|
56
60
|
Subscribes to a topic and outputs when that topic updates.
|
|
57
61
|
|
|
@@ -66,7 +70,7 @@ Subscribes to a topic and outputs when that topic updates.
|
|
|
66
70
|
- `msg.topic`: Change subscription topic
|
|
67
71
|
- `msg.payload = 'refresh'`: Output current cached value
|
|
68
72
|
|
|
69
|
-
|
|
73
|
+
#### event-calc
|
|
70
74
|
|
|
71
75
|
Subscribes to multiple topics and evaluates an expression when values update.
|
|
72
76
|
|
|
@@ -92,71 +96,113 @@ Subscribes to multiple topics and evaluates an expression when values update.
|
|
|
92
96
|
}
|
|
93
97
|
```
|
|
94
98
|
|
|
95
|
-
###
|
|
99
|
+
### Event Frame Nodes
|
|
96
100
|
|
|
97
|
-
|
|
101
|
+
#### event-frame (ISA-88 Batch Structure)
|
|
98
102
|
|
|
99
|
-
|
|
100
|
-
- **Unwrap**: If payload is `{value, topic?, timestamp?}`, extracts to msg properties
|
|
101
|
-
- **Wrap**: If payload is any other value, wraps as `{timestamp, topic, value}`
|
|
103
|
+
Creates ISA-88 procedural model records with hierarchical parent-child linking.
|
|
102
104
|
|
|
103
|
-
**
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
**ISA-88 Levels:**
|
|
106
|
+
- **Procedure** — top-level batch recipe
|
|
107
|
+
- **Unit Procedure** — sequence within a unit
|
|
108
|
+
- **Operation** — major processing step
|
|
109
|
+
- **Phase** — lowest-level action
|
|
110
|
+
|
|
111
|
+
**Features:**
|
|
112
|
+
- Trigger-based: truthy starts, falsy ends
|
|
113
|
+
- Auto-generated UUIDs
|
|
114
|
+
- Parent-child linking via `parentLevel` config or `msg.frame_id` chaining
|
|
115
|
+
- Cascade end: ending a parent auto-ends all children recursively
|
|
116
|
+
- Shared global context tracker for cross-node coordination
|
|
117
|
+
|
|
118
|
+
**Output Record:**
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"id": "auto-generated UUID",
|
|
122
|
+
"starttime": "2024-01-15T10:30:00.000Z",
|
|
123
|
+
"endtime": "9999-12-31T23:59:59.000Z",
|
|
124
|
+
"name": "Mixing",
|
|
125
|
+
"parent_id": "parent-uuid-or-empty",
|
|
126
|
+
"level": "operation",
|
|
127
|
+
"state": "running",
|
|
128
|
+
"batch_id": "BATCH-001",
|
|
129
|
+
"unit": "Reactor-1",
|
|
130
|
+
"metadata": ""
|
|
131
|
+
}
|
|
107
132
|
```
|
|
108
133
|
|
|
109
|
-
|
|
134
|
+
#### simple-frame (Simple Event Recorder)
|
|
110
135
|
|
|
111
|
-
|
|
136
|
+
A simplified event frame for tracking any event with start/end time — no hierarchy, no cascade.
|
|
112
137
|
|
|
113
|
-
|
|
138
|
+
**Properties:**
|
|
139
|
+
- **Event Type**: Category label (str/msg/flow/global/env)
|
|
140
|
+
- **Event Name**: Descriptive name (str/msg/flow/global/env)
|
|
141
|
+
- **Metadata Field**: Optional msg property for extra data
|
|
142
|
+
- **Two outputs**: start and end
|
|
114
143
|
|
|
115
|
-
|
|
144
|
+
**Output Record:**
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"id": "auto-generated UUID",
|
|
148
|
+
"starttime": "2024-01-15T10:30:00.000Z",
|
|
149
|
+
"endtime": "9999-12-31T23:59:59.000Z",
|
|
150
|
+
"type": "maintenance",
|
|
151
|
+
"name": "Oil Change",
|
|
152
|
+
"state": "running",
|
|
153
|
+
"metadata": ""
|
|
154
|
+
}
|
|
155
|
+
```
|
|
116
156
|
|
|
117
|
-
|
|
157
|
+
### Alarm Management
|
|
118
158
|
|
|
119
|
-
|
|
159
|
+
#### event-alarm (ISA-18.2 Alarm Lifecycle)
|
|
120
160
|
|
|
121
|
-
|
|
122
|
-
[inject: room1/temp] → [event-in] → [cache]
|
|
123
|
-
[inject: room2/temp] → [event-in] → [cache]
|
|
124
|
-
|
|
125
|
-
[event-calc] → [debug]
|
|
126
|
-
inputs: a = sensors/room1/temp
|
|
127
|
-
b = sensors/room2/temp
|
|
128
|
-
expression: (a + b) / 2
|
|
129
|
-
trigger: all
|
|
130
|
-
```
|
|
161
|
+
Tracks alarms through the full ISA-18.2 lifecycle with four outputs.
|
|
131
162
|
|
|
132
|
-
|
|
163
|
+
**Alarm States:**
|
|
164
|
+
- **UNACK_ALM** — Active + Unacknowledged (just raised)
|
|
165
|
+
- **ACK_ALM** — Active + Acknowledged (operator acked, condition still true)
|
|
166
|
+
- **UNACK_RTN** — Inactive + Unacknowledged (condition cleared, not yet acked)
|
|
167
|
+
- **NORM** — Normal (fully resolved)
|
|
133
168
|
|
|
169
|
+
**Lifecycle Paths:**
|
|
134
170
|
```
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
b = sensors/voltage
|
|
138
|
-
expression: a * b
|
|
171
|
+
Path A: NORM → UNACK_ALM → ACK_ALM → NORM (ack first, then clear)
|
|
172
|
+
Path B: NORM → UNACK_ALM → UNACK_RTN → NORM (clear first, then ack)
|
|
139
173
|
```
|
|
140
174
|
|
|
141
|
-
|
|
175
|
+
**Properties:**
|
|
176
|
+
- **Condition ID/Name**: Alarm identifier and description
|
|
177
|
+
- **Input Mappings**: Map variable names to cache topics (like event-calc)
|
|
178
|
+
- **Condition**: Expression that evaluates to true/false (e.g. `active == true`)
|
|
179
|
+
- **Severity**: 0-1000 scale
|
|
180
|
+
- **4 Outputs**: raised, acknowledged, cleared, resolved
|
|
181
|
+
|
|
182
|
+
**Actions (via msg.payload):**
|
|
183
|
+
- `{ action: "ack", source: "topic" }` — Acknowledge specific alarm
|
|
184
|
+
- `{ action: "ack_all" }` — Acknowledge all active alarms
|
|
185
|
+
- `{ action: "list" }` — List all active alarms
|
|
142
186
|
|
|
143
|
-
|
|
144
|
-
[MQTT in] → [event-json] → [event-in] → [cache]
|
|
187
|
+
### Utility Nodes
|
|
145
188
|
|
|
146
|
-
|
|
147
|
-
```
|
|
189
|
+
#### event-flatten
|
|
148
190
|
|
|
149
|
-
|
|
191
|
+
Flattens nested objects into individual topic/value pairs for the cache.
|
|
150
192
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
193
|
+
#### event-preview
|
|
194
|
+
|
|
195
|
+
Preview cached values directly in the Node-RED editor.
|
|
196
|
+
|
|
197
|
+
#### event-simulator
|
|
198
|
+
|
|
199
|
+
Generates simulated data for testing. Supports sine waves, random values, and ramps.
|
|
158
200
|
|
|
159
|
-
|
|
201
|
+
#### event-chart
|
|
202
|
+
|
|
203
|
+
Real-time charting node for visualizing cached event data.
|
|
204
|
+
|
|
205
|
+
## Built-in Functions (event-calc)
|
|
160
206
|
|
|
161
207
|
### Math
|
|
162
208
|
| Function | Description |
|
|
@@ -190,6 +236,28 @@ Real-time charting node for visualizing cached event data.
|
|
|
190
236
|
| `delta(current, previous)` | Difference |
|
|
191
237
|
| `pctChange(current, previous)` | Percentage change |
|
|
192
238
|
|
|
239
|
+
### Change Detection
|
|
240
|
+
| Function | Description |
|
|
241
|
+
|----------|-------------|
|
|
242
|
+
| `now()` | Current timestamp in milliseconds |
|
|
243
|
+
| `prev('varName')` | Previous value of a variable |
|
|
244
|
+
| `hasChanged('varName')` | `true` if value differs from previous (false on first message) |
|
|
245
|
+
| `timeSinceLastChange('varName')` | Milliseconds since the value last changed |
|
|
246
|
+
|
|
247
|
+
### Date/Time
|
|
248
|
+
| Function | Description |
|
|
249
|
+
|----------|-------------|
|
|
250
|
+
| `hour()` | Current hour (0-23) |
|
|
251
|
+
| `minute()` | Current minute (0-59) |
|
|
252
|
+
| `second()` | Current second (0-59) |
|
|
253
|
+
| `day()` | Day of week (0=Sun, 1=Mon, ..., 6=Sat) |
|
|
254
|
+
| `dayOfMonth()` | Day of month (1-31) |
|
|
255
|
+
| `month()` | Month (1-12) |
|
|
256
|
+
| `year()` | Full year (e.g. 2026) |
|
|
257
|
+
| `isWeekday()` | `true` if Monday-Friday |
|
|
258
|
+
| `isWeekend()` | `true` if Saturday or Sunday |
|
|
259
|
+
| `hoursBetween(start, end)` | `true` if current hour is within range (wraps midnight) |
|
|
260
|
+
|
|
193
261
|
## Expression Examples
|
|
194
262
|
|
|
195
263
|
| Expression | Description |
|
|
@@ -202,7 +270,9 @@ Real-time charting node for visualizing cached event data.
|
|
|
202
270
|
| `clamp(a, 0, 100)` | Constrain 0-100 |
|
|
203
271
|
| `map(a, 0, 1023, 0, 100)` | Scale ADC to % |
|
|
204
272
|
| `ifelse(a > b, 'high', 'low')` | Conditional |
|
|
205
|
-
| `
|
|
273
|
+
| `hasChanged('temp')` | Did temperature just change? |
|
|
274
|
+
| `temp - prev('temp')` | Delta from previous value |
|
|
275
|
+
| `isWeekday() && hoursBetween(8, 18)` | During business hours? |
|
|
206
276
|
|
|
207
277
|
## API (for custom nodes)
|
|
208
278
|
|
|
@@ -216,7 +286,7 @@ cache.setValue('topic/path', 42, { source: 'sensor' });
|
|
|
216
286
|
|
|
217
287
|
// Get a value
|
|
218
288
|
const entry = cache.getValue('topic/path');
|
|
219
|
-
// { value: 42, ts: 1704000000000, metadata: { source: 'sensor' } }
|
|
289
|
+
// { value: 42, ts: 1704000000000, metadata: { source: 'sensor' }, previous: { value: 40, ts: ..., metadata: ... } }
|
|
220
290
|
|
|
221
291
|
// Subscribe to updates
|
|
222
292
|
const subId = cache.subscribe('sensors/room1/temp', (topic, entry) => {
|
|
@@ -236,9 +306,11 @@ cache.clear();
|
|
|
236
306
|
## HTTP Admin Endpoints
|
|
237
307
|
|
|
238
308
|
```
|
|
239
|
-
GET /event-cache/:id/stats
|
|
240
|
-
GET /event-cache/:id/topics
|
|
241
|
-
POST /event-cache/:id/clear
|
|
309
|
+
GET /event-cache/:id/stats - Cache statistics
|
|
310
|
+
GET /event-cache/:id/topics - List all topics
|
|
311
|
+
POST /event-cache/:id/clear - Clear cache
|
|
312
|
+
GET /event-frame/tracker - Current batch tracker state
|
|
313
|
+
GET /event-alarm/:id/alarms - Active alarms for a node
|
|
242
314
|
```
|
|
243
315
|
|
|
244
316
|
## License
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "alarm-sim-flow",
|
|
4
|
+
"type": "tab",
|
|
5
|
+
"label": "Alarm Lifecycle Simulation",
|
|
6
|
+
"disabled": false,
|
|
7
|
+
"info": "Self-running alarm lifecycle simulation.\n\nEvery few seconds a random alarm is raised. After a random delay it gets\nacknowledged, and after another random delay the condition clears.\nThe event-alarm nodes track the full ISA-18.2 lifecycle.\n\nBoth lifecycle paths are exercised:\n Path A: Raised > Acked > Cleared (Resolved)\n Path B: Raised > Cleared > Acked (Resolved)"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"id": "sim-cache",
|
|
11
|
+
"type": "event-cache",
|
|
12
|
+
"name": "Sim Cache",
|
|
13
|
+
"maxEntries": "10000",
|
|
14
|
+
"ttl": "0"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "comment-sim-title",
|
|
18
|
+
"type": "comment",
|
|
19
|
+
"z": "alarm-sim-flow",
|
|
20
|
+
"name": "Alarm Lifecycle Simulation (self-running)",
|
|
21
|
+
"info": "Raises random alarms every N seconds, then auto-acks and auto-clears after random delays.\nWatch the debug sidebar to see the full lifecycle play out.",
|
|
22
|
+
"x": 230,
|
|
23
|
+
"y": 40,
|
|
24
|
+
"wires": []
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "sim-trigger",
|
|
28
|
+
"type": "inject",
|
|
29
|
+
"z": "alarm-sim-flow",
|
|
30
|
+
"name": "Every 5s: raise random alarm",
|
|
31
|
+
"props": [
|
|
32
|
+
{ "p": "payload", "v": "", "vt": "date" }
|
|
33
|
+
],
|
|
34
|
+
"repeat": "5",
|
|
35
|
+
"crontab": "",
|
|
36
|
+
"once": true,
|
|
37
|
+
"onceDelay": "2",
|
|
38
|
+
"x": 200,
|
|
39
|
+
"y": 120,
|
|
40
|
+
"wires": [["sim-raise-alarm"]]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "sim-raise-alarm",
|
|
44
|
+
"type": "function",
|
|
45
|
+
"z": "alarm-sim-flow",
|
|
46
|
+
"name": "Raise Random Alarm",
|
|
47
|
+
"func": "const alarms = [\n { topic: 'sim/plant/PumpTrip_P1A/active', name: 'PumpTrip_P1A' },\n { topic: 'sim/plant/BreakerFault_MCC3/active', name: 'BreakerFault_MCC3' },\n { topic: 'sim/plant/HighLevel_Thickener/active', name: 'HighLevel_Thickener' },\n { topic: 'sim/plant/VibrationHigh_Screen2/active', name: 'VibrationHigh_Screen2' },\n { topic: 'sim/plant/EStop_Conveyor7/active', name: 'EStop_Conveyor7' },\n { topic: 'sim/plant/MagnetiteLevel_Low/active', name: 'MagnetiteLevel_Low' }\n];\n\n// Pick a random alarm\nconst alarm = alarms[Math.floor(Math.random() * alarms.length)];\n\n// Activate it\nconst activateMsg = {\n topic: alarm.topic,\n payload: true\n};\n\n// Schedule ack after 3-10s random delay\nconst ackDelay = 3000 + Math.floor(Math.random() * 7000);\n// Schedule clear after 5-15s random delay\nconst clearDelay = 5000 + Math.floor(Math.random() * 10000);\n\n// Randomly choose lifecycle path:\n// Path A (60%): ack first, then clear\n// Path B (40%): clear first, then ack\nconst ackFirst = Math.random() < 0.6;\n\nconst ackMsg = {\n payload: { action: 'ack', source: alarm.topic },\n _alarm: alarm.name,\n _delay: ackFirst ? ackDelay : clearDelay + ackDelay\n};\n\nconst clearMsg = {\n topic: alarm.topic,\n payload: false,\n _alarm: alarm.name,\n _delay: ackFirst ? clearDelay + ackDelay : clearDelay\n};\n\n// Store scheduled actions in context for the delay nodes\nnode.status({ text: `${alarm.name} (${ackFirst ? 'ack-first' : 'clear-first'})` });\n\n// Output 1: activate now\n// Output 2: ack (with delay metadata)\n// Output 3: clear (with delay metadata)\nreturn [activateMsg, ackMsg, clearMsg];",
|
|
48
|
+
"outputs": 3,
|
|
49
|
+
"outputLabels": ["activate", "ack (delayed)", "clear (delayed)"],
|
|
50
|
+
"x": 480,
|
|
51
|
+
"y": 120,
|
|
52
|
+
"wires": [["sim-event-in"], ["sim-delay-ack"], ["sim-delay-clear"]]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "sim-delay-ack",
|
|
56
|
+
"type": "function",
|
|
57
|
+
"z": "alarm-sim-flow",
|
|
58
|
+
"name": "Random Delay (ack)",
|
|
59
|
+
"func": "const delay = msg._delay || 5000;\nconst alarmName = msg._alarm || 'unknown';\n\nreturn new Promise(resolve => {\n const timerId = setTimeout(() => {\n node.status({ text: `ack ${alarmName} after ${(delay/1000).toFixed(1)}s` });\n delete msg._delay;\n delete msg._alarm;\n resolve(msg);\n }, delay);\n \n // Clean up on node close\n node.on('close', () => clearTimeout(timerId));\n});",
|
|
60
|
+
"outputs": 1,
|
|
61
|
+
"x": 480,
|
|
62
|
+
"y": 200,
|
|
63
|
+
"wires": [["sim-ack-router"]]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "sim-delay-clear",
|
|
67
|
+
"type": "function",
|
|
68
|
+
"z": "alarm-sim-flow",
|
|
69
|
+
"name": "Random Delay (clear)",
|
|
70
|
+
"func": "const delay = msg._delay || 8000;\nconst alarmName = msg._alarm || 'unknown';\n\nreturn new Promise(resolve => {\n const timerId = setTimeout(() => {\n node.status({ text: `clear ${alarmName} after ${(delay/1000).toFixed(1)}s` });\n delete msg._delay;\n delete msg._alarm;\n resolve(msg);\n }, delay);\n \n // Clean up on node close\n node.on('close', () => clearTimeout(timerId));\n});",
|
|
71
|
+
"outputs": 1,
|
|
72
|
+
"x": 490,
|
|
73
|
+
"y": 280,
|
|
74
|
+
"wires": [["sim-event-in"]]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"id": "sim-event-in",
|
|
78
|
+
"type": "event-in",
|
|
79
|
+
"z": "alarm-sim-flow",
|
|
80
|
+
"name": "Push to Cache",
|
|
81
|
+
"cache": "sim-cache",
|
|
82
|
+
"topicSource": "msg",
|
|
83
|
+
"topicPattern": "",
|
|
84
|
+
"x": 710,
|
|
85
|
+
"y": 120,
|
|
86
|
+
"wires": [[]]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"id": "sim-ack-router",
|
|
90
|
+
"type": "function",
|
|
91
|
+
"z": "alarm-sim-flow",
|
|
92
|
+
"name": "Route ACK to alarm node",
|
|
93
|
+
"func": "// Route the ack to the correct alarm node based on source topic\nconst source = msg.payload.source;\nconst routes = {\n 'sim/plant/PumpTrip_P1A/active': 0,\n 'sim/plant/BreakerFault_MCC3/active': 1,\n 'sim/plant/HighLevel_Thickener/active': 2,\n 'sim/plant/VibrationHigh_Screen2/active': 3,\n 'sim/plant/EStop_Conveyor7/active': 4,\n 'sim/plant/MagnetiteLevel_Low/active': 5\n};\n\nconst outputs = [null, null, null, null, null, null];\nconst idx = routes[source];\nif (idx !== undefined) {\n outputs[idx] = msg;\n}\nreturn outputs;",
|
|
94
|
+
"outputs": 6,
|
|
95
|
+
"outputLabels": ["Pump", "Breaker", "Thickener", "Vibration", "EStop", "Magnetite"],
|
|
96
|
+
"x": 720,
|
|
97
|
+
"y": 200,
|
|
98
|
+
"wires": [["sim-alarm-pump"], ["sim-alarm-breaker"], ["sim-alarm-thickener"], ["sim-alarm-vibration"], ["sim-alarm-estop"], ["sim-alarm-magnetite"]]
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"id": "comment-alarm-nodes",
|
|
102
|
+
"type": "comment",
|
|
103
|
+
"z": "alarm-sim-flow",
|
|
104
|
+
"name": "--- Event Alarm Nodes ---",
|
|
105
|
+
"info": "",
|
|
106
|
+
"x": 940,
|
|
107
|
+
"y": 40,
|
|
108
|
+
"wires": []
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"id": "sim-alarm-pump",
|
|
112
|
+
"type": "event-alarm",
|
|
113
|
+
"z": "alarm-sim-flow",
|
|
114
|
+
"name": "Pump P1A Trip",
|
|
115
|
+
"cache": "sim-cache",
|
|
116
|
+
"conditionId": "PumpTrip_P1A",
|
|
117
|
+
"conditionName": "Heavy Media Pump P1A Trip",
|
|
118
|
+
"inputMappings": [
|
|
119
|
+
{ "name": "active", "topic": "sim/plant/PumpTrip_P1A/active" }
|
|
120
|
+
],
|
|
121
|
+
"condition": "active == true",
|
|
122
|
+
"severity": "800",
|
|
123
|
+
"outputTopic": "alarm/PumpTrip_P1A",
|
|
124
|
+
"x": 960,
|
|
125
|
+
"y": 100,
|
|
126
|
+
"wires": [["debug-sim-raised"], ["debug-sim-acked"], ["debug-sim-cleared"], ["debug-sim-resolved"]]
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"id": "sim-alarm-breaker",
|
|
130
|
+
"type": "event-alarm",
|
|
131
|
+
"z": "alarm-sim-flow",
|
|
132
|
+
"name": "Breaker MCC3 Fault",
|
|
133
|
+
"cache": "sim-cache",
|
|
134
|
+
"conditionId": "BreakerFault_MCC3",
|
|
135
|
+
"conditionName": "MCC3 Main Breaker Fault",
|
|
136
|
+
"inputMappings": [
|
|
137
|
+
{ "name": "active", "topic": "sim/plant/BreakerFault_MCC3/active" }
|
|
138
|
+
],
|
|
139
|
+
"condition": "active == true",
|
|
140
|
+
"severity": "900",
|
|
141
|
+
"outputTopic": "alarm/BreakerFault_MCC3",
|
|
142
|
+
"x": 970,
|
|
143
|
+
"y": 180,
|
|
144
|
+
"wires": [["debug-sim-raised"], ["debug-sim-acked"], ["debug-sim-cleared"], ["debug-sim-resolved"]]
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"id": "sim-alarm-thickener",
|
|
148
|
+
"type": "event-alarm",
|
|
149
|
+
"z": "alarm-sim-flow",
|
|
150
|
+
"name": "Thickener Level High",
|
|
151
|
+
"cache": "sim-cache",
|
|
152
|
+
"conditionId": "HighLevel_Thickener",
|
|
153
|
+
"conditionName": "Thickener Overflow Level High",
|
|
154
|
+
"inputMappings": [
|
|
155
|
+
{ "name": "active", "topic": "sim/plant/HighLevel_Thickener/active" }
|
|
156
|
+
],
|
|
157
|
+
"condition": "active == true",
|
|
158
|
+
"severity": "600",
|
|
159
|
+
"outputTopic": "alarm/HighLevel_Thickener",
|
|
160
|
+
"x": 970,
|
|
161
|
+
"y": 260,
|
|
162
|
+
"wires": [["debug-sim-raised"], ["debug-sim-acked"], ["debug-sim-cleared"], ["debug-sim-resolved"]]
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"id": "sim-alarm-vibration",
|
|
166
|
+
"type": "event-alarm",
|
|
167
|
+
"z": "alarm-sim-flow",
|
|
168
|
+
"name": "Screen 2 Vibration",
|
|
169
|
+
"cache": "sim-cache",
|
|
170
|
+
"conditionId": "VibrationHigh_Screen2",
|
|
171
|
+
"conditionName": "Screen 2 Vibration Alarm High",
|
|
172
|
+
"inputMappings": [
|
|
173
|
+
{ "name": "active", "topic": "sim/plant/VibrationHigh_Screen2/active" }
|
|
174
|
+
],
|
|
175
|
+
"condition": "active == true",
|
|
176
|
+
"severity": "500",
|
|
177
|
+
"outputTopic": "alarm/VibrationHigh_Screen2",
|
|
178
|
+
"x": 970,
|
|
179
|
+
"y": 340,
|
|
180
|
+
"wires": [["debug-sim-raised"], ["debug-sim-acked"], ["debug-sim-cleared"], ["debug-sim-resolved"]]
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"id": "sim-alarm-estop",
|
|
184
|
+
"type": "event-alarm",
|
|
185
|
+
"z": "alarm-sim-flow",
|
|
186
|
+
"name": "Conveyor 7 EStop",
|
|
187
|
+
"cache": "sim-cache",
|
|
188
|
+
"conditionId": "EStop_Conveyor7",
|
|
189
|
+
"conditionName": "Conveyor 7 Emergency Stop",
|
|
190
|
+
"inputMappings": [
|
|
191
|
+
{ "name": "active", "topic": "sim/plant/EStop_Conveyor7/active" }
|
|
192
|
+
],
|
|
193
|
+
"condition": "active == true",
|
|
194
|
+
"severity": "1000",
|
|
195
|
+
"outputTopic": "alarm/EStop_Conveyor7",
|
|
196
|
+
"x": 970,
|
|
197
|
+
"y": 420,
|
|
198
|
+
"wires": [["debug-sim-raised"], ["debug-sim-acked"], ["debug-sim-cleared"], ["debug-sim-resolved"]]
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"id": "sim-alarm-magnetite",
|
|
202
|
+
"type": "event-alarm",
|
|
203
|
+
"z": "alarm-sim-flow",
|
|
204
|
+
"name": "Magnetite Level Low",
|
|
205
|
+
"cache": "sim-cache",
|
|
206
|
+
"conditionId": "MagnetiteLevel_Low",
|
|
207
|
+
"conditionName": "Magnetite Storage Tank Level Low",
|
|
208
|
+
"inputMappings": [
|
|
209
|
+
{ "name": "active", "topic": "sim/plant/MagnetiteLevel_Low/active" }
|
|
210
|
+
],
|
|
211
|
+
"condition": "active == true",
|
|
212
|
+
"severity": "400",
|
|
213
|
+
"outputTopic": "alarm/MagnetiteLevel_Low",
|
|
214
|
+
"x": 970,
|
|
215
|
+
"y": 500,
|
|
216
|
+
"wires": [["debug-sim-raised"], ["debug-sim-acked"], ["debug-sim-cleared"], ["debug-sim-resolved"]]
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"id": "comment-debug-outputs",
|
|
220
|
+
"type": "comment",
|
|
221
|
+
"z": "alarm-sim-flow",
|
|
222
|
+
"name": "--- Lifecycle Debug Outputs ---",
|
|
223
|
+
"info": "",
|
|
224
|
+
"x": 1270,
|
|
225
|
+
"y": 40,
|
|
226
|
+
"wires": []
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"id": "debug-sim-raised",
|
|
230
|
+
"type": "debug",
|
|
231
|
+
"z": "alarm-sim-flow",
|
|
232
|
+
"name": "RAISED",
|
|
233
|
+
"active": true,
|
|
234
|
+
"tosidebar": true,
|
|
235
|
+
"console": false,
|
|
236
|
+
"tostatus": true,
|
|
237
|
+
"statusVal": "payload.condition_name",
|
|
238
|
+
"statusType": "msg",
|
|
239
|
+
"x": 1240,
|
|
240
|
+
"y": 100,
|
|
241
|
+
"wires": []
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"id": "debug-sim-acked",
|
|
245
|
+
"type": "debug",
|
|
246
|
+
"z": "alarm-sim-flow",
|
|
247
|
+
"name": "ACKNOWLEDGED",
|
|
248
|
+
"active": true,
|
|
249
|
+
"tosidebar": true,
|
|
250
|
+
"console": false,
|
|
251
|
+
"tostatus": true,
|
|
252
|
+
"statusVal": "payload.condition_name",
|
|
253
|
+
"statusType": "msg",
|
|
254
|
+
"x": 1260,
|
|
255
|
+
"y": 180,
|
|
256
|
+
"wires": []
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"id": "debug-sim-cleared",
|
|
260
|
+
"type": "debug",
|
|
261
|
+
"z": "alarm-sim-flow",
|
|
262
|
+
"name": "CLEARED",
|
|
263
|
+
"active": true,
|
|
264
|
+
"tosidebar": true,
|
|
265
|
+
"console": false,
|
|
266
|
+
"tostatus": true,
|
|
267
|
+
"statusVal": "payload.condition_name",
|
|
268
|
+
"statusType": "msg",
|
|
269
|
+
"x": 1250,
|
|
270
|
+
"y": 260,
|
|
271
|
+
"wires": []
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"id": "debug-sim-resolved",
|
|
275
|
+
"type": "debug",
|
|
276
|
+
"z": "alarm-sim-flow",
|
|
277
|
+
"name": "RESOLVED",
|
|
278
|
+
"active": true,
|
|
279
|
+
"tosidebar": true,
|
|
280
|
+
"console": false,
|
|
281
|
+
"tostatus": true,
|
|
282
|
+
"statusVal": "payload.condition_name",
|
|
283
|
+
"statusType": "msg",
|
|
284
|
+
"x": 1250,
|
|
285
|
+
"y": 340,
|
|
286
|
+
"wires": []
|
|
287
|
+
}
|
|
288
|
+
]
|