@yarkivaev/scada 1.2.0 → 1.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yarkivaev/scada",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "SCADA domain objects for plant monitoring",
5
5
  "repository": {
6
6
  "type": "git",
@@ -15,7 +15,7 @@
15
15
  * @example
16
16
  * const sensor = clickhouseSensor(conn, 'icht1/voltage', 'Voltage', 'V');
17
17
  * sensor.name(); // 'Voltage'
18
- * await sensor.current(); // { timestamp, value, unit }
18
+ * await sensor.current(); // { found: true, timestamp, value, unit } or { found: false }
19
19
  * await sensor.measurements({ start, end }, 60000); // downsampled to 1-minute intervals
20
20
  * sensor.stream(since, 1000, callback); // live stream
21
21
  */
@@ -37,16 +37,16 @@ export default function clickhouseSensor(connection, topic, displayName, unit) {
37
37
  { topic }
38
38
  );
39
39
  if (rows.length === 0) {
40
- return { timestamp: new Date(), value: 0, unit };
40
+ return { found: false };
41
41
  }
42
- return { timestamp: new Date(`${rows[0].ts}Z`), value: rows[0].value, unit };
42
+ return { found: true, timestamp: new Date(`${rows[0].ts}Z`), value: rows[0].value, unit };
43
43
  },
44
44
  async measurements(range, step) {
45
45
  const seconds = Math.max(1, Math.floor(step / 1000));
46
46
  const rows = await connection.query(
47
47
  `SELECT
48
48
  toStartOfInterval(ts, INTERVAL ${seconds} SECOND) as ts,
49
- avg(value) as value
49
+ anyLast(value) as value
50
50
  FROM scada.metrics
51
51
  WHERE topic = {topic:String}
52
52
  AND ts >= toStartOfInterval({start:DateTime64(3)}, INTERVAL ${seconds} SECOND)
@@ -63,15 +63,16 @@ export default function clickhouseSensor(connection, topic, displayName, unit) {
63
63
  return { timestamp: new Date(`${row.ts}Z`), value: row.value, unit };
64
64
  });
65
65
  },
66
- stream(since, step, callback) {
66
+ stream(since, step, callback, clock) {
67
+ const time = clock || (() => { return new Date(); });
67
68
  let lastTs = since;
68
69
  const timer = setInterval(async () => {
69
70
  try {
70
71
  const rows = await connection.query(
71
72
  `SELECT ts, value FROM scada.metrics
72
- WHERE topic = {topic:String} AND ts > {since:DateTime64(3)}
73
+ WHERE topic = {topic:String} AND ts > {since:DateTime64(3)} AND ts <= {until:DateTime64(3)}
73
74
  ORDER BY ts LIMIT 100`,
74
- { topic, since: formatDateTime(lastTs) }
75
+ { topic, since: formatDateTime(lastTs), until: formatDateTime(time()) }
75
76
  );
76
77
  rows.forEach((row) => {
77
78
  const timestamp = new Date(`${row.ts}Z`);
@@ -25,7 +25,9 @@ export default function machineChronology(initial, history, sensors) {
25
25
  const values = await Promise.all(keys.map((key) => {
26
26
  return sensors[key].current();
27
27
  }));
28
- keys.forEach((key, i) => { result[key] = values[i]; });
28
+ keys.forEach((key, i) => {
29
+ result[key] = values[i].found ? values[i] : { value: 0, unit: '' };
30
+ });
29
31
  }
30
32
  return result;
31
33
  }
@@ -31,12 +31,13 @@ export default function scyllaSensor(connection, topic, displayName, unit) {
31
31
  return { timestamp: row.ts, value: row.value, unit };
32
32
  });
33
33
  },
34
- stream(since, step, callback) {
34
+ stream(since, step, callback, clock) {
35
+ const time = clock || (() => { return new Date(); });
35
36
  let lastTs = since;
36
37
  const timer = setInterval(async () => {
37
38
  const rows = await connection.query(
38
- 'SELECT ts, value FROM scada.metrics WHERE topic = ? AND ts > ? LIMIT 100',
39
- [topic, lastTs]
39
+ 'SELECT ts, value FROM scada.metrics WHERE topic = ? AND ts > ? AND ts <= ? LIMIT 100',
40
+ [topic, lastTs, time()]
40
41
  );
41
42
  rows.forEach((row) => {
42
43
  callback({ timestamp: row.ts, value: row.value, unit });