node-red-contrib-event-calc 3.3.6 → 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 +100 -62
- package/examples/alarm-simulation-example.json +288 -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 +72 -37
- package/nodes/event-calc.js +114 -124
- 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 +5 -2
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
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
|
```
|
|
@@ -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
|
|
142
181
|
|
|
143
|
-
|
|
144
|
-
|
|
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
|
|
145
186
|
|
|
146
|
-
|
|
147
|
-
```
|
|
187
|
+
### Utility Nodes
|
|
148
188
|
|
|
149
|
-
|
|
189
|
+
#### event-flatten
|
|
150
190
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
topic: power/watts
|
|
157
|
-
```
|
|
191
|
+
Flattens nested objects into individual topic/value pairs for the cache.
|
|
192
|
+
|
|
193
|
+
#### event-preview
|
|
194
|
+
|
|
195
|
+
Preview cached values directly in the Node-RED editor.
|
|
158
196
|
|
|
159
|
-
|
|
197
|
+
#### event-simulator
|
|
198
|
+
|
|
199
|
+
Generates simulated data for testing. Supports sine waves, random values, and ramps.
|
|
200
|
+
|
|
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 |
|
|
@@ -198,8 +244,6 @@ Real-time charting node for visualizing cached event data.
|
|
|
198
244
|
| `hasChanged('varName')` | `true` if value differs from previous (false on first message) |
|
|
199
245
|
| `timeSinceLastChange('varName')` | Milliseconds since the value last changed |
|
|
200
246
|
|
|
201
|
-
The cache automatically tracks the previous value for every topic. On the first message, `prev()` returns the same value as the current (so `hasChanged()` returns `false` and delta is `0`).
|
|
202
|
-
|
|
203
247
|
### Date/Time
|
|
204
248
|
| Function | Description |
|
|
205
249
|
|----------|-------------|
|
|
@@ -226,13 +270,9 @@ The cache automatically tracks the previous value for every topic. On the first
|
|
|
226
270
|
| `clamp(a, 0, 100)` | Constrain 0-100 |
|
|
227
271
|
| `map(a, 0, 1023, 0, 100)` | Scale ADC to % |
|
|
228
272
|
| `ifelse(a > b, 'high', 'low')` | Conditional |
|
|
229
|
-
| `pctChange(a, b)` | % change from b to a |
|
|
230
273
|
| `hasChanged('temp')` | Did temperature just change? |
|
|
231
274
|
| `temp - prev('temp')` | Delta from previous value |
|
|
232
|
-
| `round(timeSinceLastChange('temp') / 1000, 1)` | Seconds since last change |
|
|
233
275
|
| `isWeekday() && hoursBetween(8, 18)` | During business hours? |
|
|
234
|
-
| `hoursBetween(22, 6)` | Night shift (wraps midnight) |
|
|
235
|
-
| `ifelse(isWeekend(), temp * 0.8, temp)` | Reduce setpoint on weekends |
|
|
236
276
|
|
|
237
277
|
## API (for custom nodes)
|
|
238
278
|
|
|
@@ -248,10 +288,6 @@ cache.setValue('topic/path', 42, { source: 'sensor' });
|
|
|
248
288
|
const entry = cache.getValue('topic/path');
|
|
249
289
|
// { value: 42, ts: 1704000000000, metadata: { source: 'sensor' }, previous: { value: 40, ts: ..., metadata: ... } }
|
|
250
290
|
|
|
251
|
-
// Get previous value
|
|
252
|
-
const prev = cache.getPrevious('topic/path');
|
|
253
|
-
// { value: 40, ts: 1703999990000, metadata: { source: 'sensor' } }
|
|
254
|
-
|
|
255
291
|
// Subscribe to updates
|
|
256
292
|
const subId = cache.subscribe('sensors/room1/temp', (topic, entry) => {
|
|
257
293
|
console.log(`${topic} = ${entry.value}`);
|
|
@@ -270,9 +306,11 @@ cache.clear();
|
|
|
270
306
|
## HTTP Admin Endpoints
|
|
271
307
|
|
|
272
308
|
```
|
|
273
|
-
GET /event-cache/:id/stats
|
|
274
|
-
GET /event-cache/:id/topics
|
|
275
|
-
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
|
|
276
314
|
```
|
|
277
315
|
|
|
278
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
|
+
]
|