iobroker.statechart 0.1.1 → 0.3.0

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.
Files changed (50) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +59 -438
  3. package/admin/build/tab.js +133 -130
  4. package/admin/statechart.png +0 -0
  5. package/admin/{style.css → tab.css} +0 -2
  6. package/admin/tab_m.html +1 -1
  7. package/build/lib/chart-analysis.js +367 -0
  8. package/build/lib/chart-analysis.js.map +7 -0
  9. package/build/lib/chart-codegen.js +405 -0
  10. package/build/lib/chart-codegen.js.map +7 -0
  11. package/build/lib/chart-helpers.js +102 -0
  12. package/build/lib/chart-helpers.js.map +7 -0
  13. package/build/lib/chart-model.js +477 -0
  14. package/build/lib/chart-model.js.map +7 -0
  15. package/build/lib/chart-runtime.js +1521 -0
  16. package/build/lib/chart-runtime.js.map +7 -0
  17. package/build/lib/chart-structure.js +172 -0
  18. package/build/lib/chart-structure.js.map +7 -0
  19. package/build/lib/chart-validation.js +372 -0
  20. package/build/lib/chart-validation.js.map +7 -0
  21. package/build/main.js +638 -0
  22. package/build/main.js.map +7 -0
  23. package/io-package.json +31 -34
  24. package/package.json +21 -17
  25. package/admin/i18n/de.json +0 -4
  26. package/admin/i18n/en.json +0 -4
  27. package/admin/i18n/es.json +0 -4
  28. package/admin/i18n/fr.json +0 -4
  29. package/admin/i18n/it.json +0 -4
  30. package/admin/i18n/nl.json +0 -4
  31. package/admin/i18n/pl.json +0 -4
  32. package/admin/i18n/pt.json +0 -4
  33. package/admin/i18n/ru.json +0 -4
  34. package/admin/i18n/uk.json +0 -4
  35. package/admin/i18n/zh-cn.json +0 -4
  36. package/admin/jsonConfig.json +0 -19
  37. package/admin/statechart.svg +0 -38
  38. package/lib/adapter-config.d.ts +0 -19
  39. package/lib/chart-codegen.js +0 -436
  40. package/lib/chart-codegen.test.js +0 -132
  41. package/lib/chart-helpers.js +0 -74
  42. package/lib/chart-helpers.test.js +0 -38
  43. package/lib/chart-model.js +0 -577
  44. package/lib/chart-model.test.js +0 -189
  45. package/lib/chart-runtime.js +0 -797
  46. package/lib/chart-runtime.test.js +0 -699
  47. package/lib/chart-structure.js +0 -106
  48. package/lib/chart-validation.js +0 -328
  49. package/lib/chart-validation.test.js +0 -149
  50. package/main.js +0 -279
package/build/main.js ADDED
@@ -0,0 +1,638 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+ var utils = __toESM(require("@iobroker/adapter-core"));
25
+ var import_chart_runtime = require("./lib/chart-runtime");
26
+ const HISTORY_LENGTH = 200;
27
+ const SIMULATION_TIMEOUT_MS = 2 * 60 * 1e3;
28
+ const CONTROLS = ["enabled", "reset", "event"];
29
+ class Statechart extends utils.Adapter {
30
+ /** running charts by chart ID */
31
+ runtimes = /* @__PURE__ */ new Map();
32
+ /** foreign states we are subscribed to */
33
+ subscribedInputs = /* @__PURE__ */ new Set();
34
+ /** chart (re)starts are executed one after another */
35
+ chartQueue = Promise.resolve();
36
+ /** for the astro helper functions */
37
+ location = null;
38
+ /** the last events of each chart by chart ID, newest last, also stored in charts.<chart>.history */
39
+ histories = /* @__PURE__ */ new Map();
40
+ /** charts which run as simulation (their runtime is in runtimes), with the timer which ends the simulation */
41
+ simulations = /* @__PURE__ */ new Map();
42
+ constructor(options = {}) {
43
+ super({
44
+ ...options,
45
+ name: "statechart"
46
+ });
47
+ this.on("ready", this.onReady.bind(this));
48
+ this.on("stateChange", this.onStateChange.bind(this));
49
+ this.on("objectChange", this.onObjectChange.bind(this));
50
+ this.on("message", this.onMessage.bind(this));
51
+ this.on("unload", this.onUnload.bind(this));
52
+ }
53
+ /**
54
+ * Is called when databases are connected and adapter received configuration.
55
+ */
56
+ async onReady() {
57
+ await this.setObjectNotExistsAsync("charts", {
58
+ type: "folder",
59
+ common: { name: "Charts" },
60
+ native: {}
61
+ });
62
+ await this.loadLocation();
63
+ const objects = await this.getForeignObjectsAsync(`${this.namespace}.charts.*`, "channel");
64
+ for (const [id, obj] of Object.entries(objects)) {
65
+ const chartId = this.getChartId(id);
66
+ if (chartId) {
67
+ await this.startChart(chartId, obj, true);
68
+ }
69
+ }
70
+ this.subscribeObjects("charts.*");
71
+ this.subscribeStates("charts.*.history");
72
+ for (const control of CONTROLS) {
73
+ this.subscribeStates(`charts.*.${control}`);
74
+ }
75
+ }
76
+ /**
77
+ * Reads the location of the ioBroker system settings, used by isDaytime() and astroTime()
78
+ */
79
+ async loadLocation() {
80
+ const config = await this.getForeignObjectAsync("system.config");
81
+ const latitude = parseFloat(String(config?.common?.latitude ?? ""));
82
+ const longitude = parseFloat(String(config?.common?.longitude ?? ""));
83
+ if (Number.isFinite(latitude) && Number.isFinite(longitude)) {
84
+ this.location = { latitude, longitude };
85
+ } else {
86
+ this.log.info("No location configured in the system settings, isDaytime() and astroTime() do not work");
87
+ }
88
+ }
89
+ /**
90
+ * Extracts the chart ID from the ID of a chart object
91
+ *
92
+ * @param id Object ID
93
+ * @returns undefined if the ID is no chart object
94
+ */
95
+ getChartId(id) {
96
+ const prefix = `${this.namespace}.charts.`;
97
+ if (!id.startsWith(prefix)) {
98
+ return void 0;
99
+ }
100
+ const chartId = id.substring(prefix.length);
101
+ return chartId.includes(".") ? void 0 : chartId;
102
+ }
103
+ /**
104
+ * @param id State ID
105
+ * @returns the chart ID and the name of the state if it is a state of a chart, e.g. "history"
106
+ */
107
+ parseChartStateId(id) {
108
+ const match = /^charts\.([^.]+)\.([^.]+)$/.exec(id.substring(this.namespace.length + 1));
109
+ return id.startsWith(`${this.namespace}.`) && match ? { chartId: match[1], name: match[2] } : void 0;
110
+ }
111
+ /**
112
+ * @param chartId Chart ID
113
+ * @param obj Chart object
114
+ * @param restore Continue with the persisted state instead of the initial state
115
+ */
116
+ async startChart(chartId, obj, restore = false) {
117
+ const chart = obj?.type === "channel" ? obj.native?.chart : void 0;
118
+ if (!chart) {
119
+ this.histories.delete(chartId);
120
+ return;
121
+ }
122
+ await this.createChartStates(chartId);
123
+ await this.loadHistory(chartId);
124
+ await this.setState(`charts.${chartId}.enabled`, chart.enabled !== false, true);
125
+ await this.setState(`charts.${chartId}.simulation`, false, true);
126
+ await this.setState(`charts.${chartId}.simulationStatus`, "", true);
127
+ const empty = !chart.states?.length;
128
+ if (!chart.enabled || empty) {
129
+ if (empty) {
130
+ this.log.warn(`Chart "${chart.name}" has no states, it is not started`);
131
+ } else {
132
+ this.log.info(`Chart "${chart.name}" is paused`);
133
+ }
134
+ await this.setState(`charts.${chartId}.activeState`, "", true);
135
+ await this.setState(`charts.${chartId}.activeStateIds`, "[]", true);
136
+ await this.setState(`charts.${chartId}.values`, "{}", true);
137
+ return;
138
+ }
139
+ const runtime = this.createRuntime(chartId, chart);
140
+ try {
141
+ await runtime.start({ restore });
142
+ } catch (err) {
143
+ this.log.error(`Cannot start chart "${chart.name}": ${err.message}`);
144
+ return;
145
+ }
146
+ this.runtimes.set(chartId, runtime);
147
+ this.updateSubscriptions();
148
+ this.log.info(`Chart "${chart.name}" started`);
149
+ }
150
+ /**
151
+ * @param chartId Chart ID
152
+ * @param chart The chart
153
+ * @param simulated Run it as simulation, see ChartRuntime
154
+ */
155
+ createRuntime(chartId, chart, simulated = false) {
156
+ return new import_chart_runtime.ChartRuntime(
157
+ chartId,
158
+ chart,
159
+ {
160
+ readState: async (id) => (await this.getForeignStateAsync(id))?.val ?? null,
161
+ getState: (id) => this.getForeignStateAsync(id),
162
+ writeState: async (id, val, ack = false) => {
163
+ await this.setForeignStateAsync(id, val, ack);
164
+ },
165
+ sendTo: (instance, command, message) => this.sendTo(instance, command, message),
166
+ getLocation: () => this.location,
167
+ onActiveChange: ({ names, ids }) => {
168
+ void this.setState(`charts.${chartId}.activeState`, names, true);
169
+ void this.setState(`charts.${chartId}.activeStateIds`, JSON.stringify(ids), true);
170
+ },
171
+ persist: (data) => {
172
+ void this.setState(`charts.${chartId}.snapshot`, JSON.stringify(data), true);
173
+ },
174
+ onValues: (values) => {
175
+ void this.setState(`charts.${chartId}.values`, JSON.stringify(values), true);
176
+ },
177
+ onHistory: (entries) => this.addHistory(chartId, entries),
178
+ onSimulationStatus: (status) => {
179
+ void this.setState(`charts.${chartId}.simulationStatus`, JSON.stringify(status), true);
180
+ },
181
+ loadPersisted: async () => {
182
+ const state = await this.getStateAsync(`charts.${chartId}.snapshot`);
183
+ return typeof state?.val === "string" && state.val ? JSON.parse(state.val) : null;
184
+ },
185
+ log: this.log,
186
+ // the timers of the adapter are cleared when it is unloaded
187
+ setTimeout: (callback, ms) => this.setTimeout(callback, ms),
188
+ clearTimeout: (handle) => this.clearTimeout(handle)
189
+ },
190
+ simulated
191
+ );
192
+ }
193
+ /**
194
+ * Stops the chart and runs the given version of it as simulation, see ChartRuntime
195
+ *
196
+ * @param chartId Chart ID
197
+ * @param chart The chart as shown in the editor, may have unsaved changes
198
+ * @param breakpoints IDs of the states and transitions with a breakpoint
199
+ */
200
+ async startSimulation(chartId, chart, breakpoints = []) {
201
+ if (!chart?.states?.length) {
202
+ throw new Error("The chart has no states");
203
+ }
204
+ this.stopChart(chartId);
205
+ this.endSimulationTimer(chartId);
206
+ const runtime = this.createRuntime(chartId, chart, true);
207
+ runtime.breakpoints = new Set(breakpoints);
208
+ this.simulations.set(
209
+ chartId,
210
+ this.setTimeout(() => this.onSimulationTimeout(chartId), SIMULATION_TIMEOUT_MS)
211
+ );
212
+ try {
213
+ await runtime.start();
214
+ } catch (err) {
215
+ this.endSimulationTimer(chartId);
216
+ await this.restartChart(chartId);
217
+ throw err;
218
+ }
219
+ this.runtimes.set(chartId, runtime);
220
+ this.updateSubscriptions();
221
+ await this.setState(`charts.${chartId}.simulation`, true, true);
222
+ this.log.info(`Chart "${chart.name}" runs as simulation, its outputs are not written`);
223
+ }
224
+ /**
225
+ * Ends the simulation of a chart, the real chart continues where it was before
226
+ *
227
+ * @param chartId Chart ID
228
+ */
229
+ async stopSimulation(chartId) {
230
+ if (!this.simulations.has(chartId)) {
231
+ return;
232
+ }
233
+ this.endSimulationTimer(chartId);
234
+ this.stopChart(chartId);
235
+ this.log.info(`Simulation of chart ${chartId} ended`);
236
+ await this.restartChart(chartId, true);
237
+ }
238
+ /**
239
+ * @param chartId Chart ID
240
+ */
241
+ endSimulationTimer(chartId) {
242
+ this.clearTimeout(this.simulations.get(chartId));
243
+ this.simulations.delete(chartId);
244
+ }
245
+ /**
246
+ * @param chartId Chart ID
247
+ */
248
+ onSimulationTimeout(chartId) {
249
+ this.log.info(`Simulation of chart ${chartId} ended, the editor did not answer anymore`);
250
+ this.chartQueue = this.chartQueue.then(() => this.stopSimulation(chartId)).catch((err) => this.log.error(`Cannot end the simulation of chart ${chartId}: ${err.message}`));
251
+ }
252
+ /**
253
+ * Starts a chart with the stored version
254
+ *
255
+ * @param chartId Chart ID
256
+ * @param restore Continue with the persisted state instead of the initial state
257
+ */
258
+ async restartChart(chartId, restore = false) {
259
+ await this.startChart(chartId, await this.getObjectAsync(`charts.${chartId}`), restore);
260
+ }
261
+ /**
262
+ * Messages of the editor, for the simulation
263
+ *
264
+ * @param obj The message
265
+ */
266
+ onMessage(obj) {
267
+ if (typeof obj !== "object" || !obj.command) {
268
+ return;
269
+ }
270
+ const message = obj.message || {};
271
+ const chartId = String(message.chartId || "");
272
+ const reply = (response) => {
273
+ if (obj.callback) {
274
+ this.sendTo(obj.from, obj.command, response, obj.callback);
275
+ }
276
+ };
277
+ const simulating = this.simulations.has(chartId);
278
+ if (simulating) {
279
+ this.clearTimeout(this.simulations.get(chartId));
280
+ this.simulations.set(
281
+ chartId,
282
+ this.setTimeout(() => this.onSimulationTimeout(chartId), SIMULATION_TIMEOUT_MS)
283
+ );
284
+ }
285
+ const runtime = this.runtimes.get(chartId);
286
+ switch (obj.command) {
287
+ case "simulationStart":
288
+ this.chartQueue = this.chartQueue.then(() => this.startSimulation(chartId, message.chart, message.breakpoints)).then(() => reply({ ok: true })).catch((err) => reply({ error: err.message }));
289
+ return;
290
+ case "simulationStop":
291
+ this.chartQueue = this.chartQueue.then(() => this.stopSimulation(chartId)).then(() => reply({ ok: true })).catch((err) => reply({ error: err.message }));
292
+ return;
293
+ case "simulationInput":
294
+ if (!simulating || !runtime) {
295
+ reply({ error: "No simulation is running" });
296
+ } else if (!runtime.simulateInput(String(message.name), message.value)) {
297
+ reply({ error: `Unknown input "${message.name}"` });
298
+ } else {
299
+ reply({ ok: true });
300
+ }
301
+ return;
302
+ case "simulationEvent":
303
+ if (!simulating || !runtime) {
304
+ reply({ error: "No simulation is running" });
305
+ } else if (!runtime.handleEvent(String(message.name))) {
306
+ reply({ error: `Unknown input "${message.name}"` });
307
+ } else {
308
+ reply({ ok: true });
309
+ }
310
+ return;
311
+ case "simulationPing":
312
+ reply({ ok: simulating });
313
+ return;
314
+ case "simulationBreakpoints":
315
+ case "simulationPause":
316
+ case "simulationResume":
317
+ case "simulationStep":
318
+ case "simulationAdvance":
319
+ if (!simulating || !runtime) {
320
+ reply({ error: "No simulation is running" });
321
+ return;
322
+ }
323
+ if (obj.command === "simulationBreakpoints") {
324
+ runtime.breakpoints = new Set(Array.isArray(message.breakpoints) ? message.breakpoints : []);
325
+ } else if (obj.command === "simulationPause") {
326
+ runtime.pauseSimulation();
327
+ } else if (obj.command === "simulationResume") {
328
+ runtime.resumeSimulation();
329
+ } else if (obj.command === "simulationStep") {
330
+ runtime.stepSimulation();
331
+ } else if (typeof message.ms === "number" && message.ms > 0) {
332
+ runtime.advanceTime(message.ms);
333
+ } else {
334
+ runtime.advanceToNextTimer();
335
+ }
336
+ reply({ ok: true });
337
+ return;
338
+ default:
339
+ reply({ error: `Unknown command "${obj.command}"` });
340
+ }
341
+ }
342
+ /**
343
+ * Reads the stored history of a chart, if it is not known yet
344
+ *
345
+ * @param chartId Chart ID
346
+ */
347
+ async loadHistory(chartId) {
348
+ if (this.histories.has(chartId)) {
349
+ return;
350
+ }
351
+ let history = [];
352
+ try {
353
+ const state = await this.getStateAsync(`charts.${chartId}.history`);
354
+ const parsed = typeof state?.val === "string" && state.val ? JSON.parse(state.val) : [];
355
+ history = Array.isArray(parsed) ? parsed : [];
356
+ } catch {
357
+ }
358
+ this.histories.set(chartId, history);
359
+ }
360
+ /**
361
+ * Appends events to the history of a chart and stores it
362
+ *
363
+ * @param chartId Chart ID
364
+ * @param entries The new events
365
+ */
366
+ addHistory(chartId, entries) {
367
+ const history = [...this.histories.get(chartId) || [], ...entries].slice(-HISTORY_LENGTH);
368
+ this.histories.set(chartId, history);
369
+ void this.setState(`charts.${chartId}.history`, JSON.stringify(history), true);
370
+ }
371
+ /**
372
+ * @param chartId Chart ID
373
+ */
374
+ stopChart(chartId) {
375
+ const runtime = this.runtimes.get(chartId);
376
+ if (runtime) {
377
+ runtime.stop();
378
+ this.runtimes.delete(chartId);
379
+ this.updateSubscriptions();
380
+ }
381
+ }
382
+ /**
383
+ * Creates the states which show the current status of a chart
384
+ *
385
+ * @param chartId Chart ID
386
+ */
387
+ async createChartStates(chartId) {
388
+ await this.setObjectNotExistsAsync(`charts.${chartId}.activeState`, {
389
+ type: "state",
390
+ common: {
391
+ name: "Active state",
392
+ type: "string",
393
+ role: "text",
394
+ read: true,
395
+ write: false
396
+ },
397
+ native: {}
398
+ });
399
+ await this.setObjectNotExistsAsync(`charts.${chartId}.snapshot`, {
400
+ type: "state",
401
+ common: {
402
+ name: "Stored state of the chart, restored after a restart of the adapter",
403
+ type: "string",
404
+ role: "json",
405
+ read: true,
406
+ write: false
407
+ },
408
+ native: {}
409
+ });
410
+ await this.setObjectNotExistsAsync(`charts.${chartId}.activeStateIds`, {
411
+ type: "state",
412
+ common: {
413
+ name: "IDs of all active states",
414
+ type: "string",
415
+ role: "json",
416
+ read: true,
417
+ write: false
418
+ },
419
+ native: {}
420
+ });
421
+ await this.setObjectNotExistsAsync(`charts.${chartId}.values`, {
422
+ type: "state",
423
+ common: {
424
+ name: "Current values of all variables",
425
+ type: "string",
426
+ role: "json",
427
+ read: true,
428
+ write: false
429
+ },
430
+ native: {}
431
+ });
432
+ await this.setObjectNotExistsAsync(`charts.${chartId}.history`, {
433
+ type: "state",
434
+ common: {
435
+ name: `Last ${HISTORY_LENGTH} events: starts and transitions taken, write anything to clear it`,
436
+ type: "string",
437
+ role: "json",
438
+ read: true,
439
+ write: true
440
+ },
441
+ native: {}
442
+ });
443
+ await this.setObjectNotExistsAsync(`charts.${chartId}.enabled`, {
444
+ type: "state",
445
+ common: {
446
+ name: "Chart runs: false pauses it, true starts it in its initial state",
447
+ type: "boolean",
448
+ role: "switch.enable",
449
+ read: true,
450
+ write: true
451
+ },
452
+ native: {}
453
+ });
454
+ await this.setObjectNotExistsAsync(`charts.${chartId}.reset`, {
455
+ type: "state",
456
+ common: {
457
+ name: "Restart the running chart in its initial state",
458
+ type: "boolean",
459
+ role: "button",
460
+ read: false,
461
+ write: true
462
+ },
463
+ native: {}
464
+ });
465
+ await this.setObjectNotExistsAsync(`charts.${chartId}.simulation`, {
466
+ type: "state",
467
+ common: {
468
+ name: "The chart runs as simulation from the editor, its outputs are not written",
469
+ type: "boolean",
470
+ role: "indicator",
471
+ read: true,
472
+ write: false
473
+ },
474
+ native: {}
475
+ });
476
+ await this.setObjectNotExistsAsync(`charts.${chartId}.simulationStatus`, {
477
+ type: "state",
478
+ common: {
479
+ name: "Status of the simulation as JSON: paused, time offset, queued wakeups, next timer, breakpoint",
480
+ type: "string",
481
+ role: "json",
482
+ read: true,
483
+ write: false
484
+ },
485
+ native: {}
486
+ });
487
+ await this.setObjectNotExistsAsync(`charts.${chartId}.event`, {
488
+ type: "state",
489
+ common: {
490
+ name: "Name of an input: wakes up the chart with this event, without changing the value of the input",
491
+ type: "string",
492
+ role: "text",
493
+ read: true,
494
+ write: true
495
+ },
496
+ native: {}
497
+ });
498
+ }
499
+ /**
500
+ * Executes a command written to a control state of a chart
501
+ *
502
+ * @param chartId Chart ID
503
+ * @param control Name of the state
504
+ * @param val The written value
505
+ */
506
+ async handleControl(chartId, control, val) {
507
+ const id = `charts.${chartId}.${control}`;
508
+ const obj = await this.getObjectAsync(`charts.${chartId}`);
509
+ const chart = obj?.type === "channel" ? obj.native?.chart : void 0;
510
+ if (!chart) {
511
+ return;
512
+ }
513
+ if (control === "enabled") {
514
+ const enabled = val === true || val === "true" || val === 1;
515
+ if (enabled === (chart.enabled !== false)) {
516
+ await this.setState(id, enabled, true);
517
+ return;
518
+ }
519
+ await this.extendObjectAsync(`charts.${chartId}`, { native: { chart: { enabled } } });
520
+ return;
521
+ }
522
+ const runtime = this.runtimes.get(chartId);
523
+ if (control === "reset") {
524
+ if (this.simulations.has(chartId)) {
525
+ this.log.info(`Chart "${chart.name}" is not reset, it runs as simulation`);
526
+ } else if (runtime) {
527
+ this.log.info(`Chart "${chart.name}" is reset`);
528
+ this.chartQueue = this.chartQueue.then(async () => {
529
+ this.stopChart(chartId);
530
+ await this.startChart(chartId, obj);
531
+ }).catch((err) => this.log.error(`Cannot reset chart ${chartId}: ${err.message}`));
532
+ await this.chartQueue;
533
+ } else {
534
+ this.log.info(`Chart "${chart.name}" is not reset, it does not run`);
535
+ }
536
+ await this.setState(id, false, true);
537
+ return;
538
+ }
539
+ const name = String(val ?? "").trim();
540
+ if (!runtime) {
541
+ this.log.info(`Event "${name}" ignored, chart "${chart.name}" does not run`);
542
+ } else if (!runtime.handleEvent(name)) {
543
+ this.log.warn(`Chart "${chart.name}": unknown event "${name}", use the name of an input`);
544
+ }
545
+ await this.setState(id, name, true);
546
+ }
547
+ /**
548
+ * Subscribes to all inputs of the running charts and unsubscribes from the ones no longer used
549
+ */
550
+ updateSubscriptions() {
551
+ const needed = new Set([...this.runtimes.values()].flatMap((runtime) => runtime.subscribedStateIds));
552
+ for (const id of needed) {
553
+ if (!this.subscribedInputs.has(id)) {
554
+ this.subscribeForeignStates(id);
555
+ }
556
+ }
557
+ for (const id of this.subscribedInputs) {
558
+ if (!needed.has(id)) {
559
+ this.unsubscribeForeignStates(id);
560
+ }
561
+ }
562
+ this.subscribedInputs = needed;
563
+ }
564
+ /**
565
+ * Is called when adapter shuts down - callback has to be called under any circumstances!
566
+ *
567
+ * @param callback Callback function
568
+ */
569
+ onUnload(callback) {
570
+ try {
571
+ for (const timer of this.simulations.values()) {
572
+ this.clearTimeout(timer);
573
+ }
574
+ this.simulations.clear();
575
+ for (const runtime of this.runtimes.values()) {
576
+ runtime.stop();
577
+ }
578
+ this.runtimes.clear();
579
+ callback();
580
+ } catch (error) {
581
+ this.log.error(`Error during unloading: ${error.message}`);
582
+ callback();
583
+ }
584
+ }
585
+ /**
586
+ * Is called if a subscribed object changes
587
+ *
588
+ * @param id Object ID
589
+ * @param obj Object
590
+ */
591
+ onObjectChange(id, obj) {
592
+ const chartId = this.getChartId(id);
593
+ if (!chartId) {
594
+ return;
595
+ }
596
+ this.chartQueue = this.chartQueue.then(async () => {
597
+ this.endSimulationTimer(chartId);
598
+ this.stopChart(chartId);
599
+ await this.startChart(chartId, obj);
600
+ }).catch((err) => this.log.error(`Cannot restart chart ${chartId}: ${err.message}`));
601
+ }
602
+ /**
603
+ * Is called if a subscribed state changes
604
+ *
605
+ * @param id State ID
606
+ * @param state State object
607
+ */
608
+ onStateChange(id, state) {
609
+ if (!state) {
610
+ return;
611
+ }
612
+ const own = this.parseChartStateId(id);
613
+ if (own?.name === "history") {
614
+ if (!state.ack) {
615
+ this.histories.set(own.chartId, []);
616
+ void this.setState(`charts.${own.chartId}.history`, "[]", true);
617
+ }
618
+ return;
619
+ }
620
+ if (own && CONTROLS.includes(own.name)) {
621
+ if (!state.ack) {
622
+ this.handleControl(own.chartId, own.name, state.val).catch(
623
+ (err) => this.log.error(`Cannot execute ${id}: ${err.message}`)
624
+ );
625
+ }
626
+ return;
627
+ }
628
+ for (const runtime of this.runtimes.values()) {
629
+ runtime.handleStateChange(id, state);
630
+ }
631
+ }
632
+ }
633
+ if (require.main !== module) {
634
+ module.exports = (options) => new Statechart(options);
635
+ } else {
636
+ (() => new Statechart())();
637
+ }
638
+ //# sourceMappingURL=main.js.map