@uwrl/qc-utils 0.0.6 → 0.0.8

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.
@@ -1,2 +1 @@
1
1
  (function(){"use strict";self.onmessage=o=>{const{bufferX:s,bufferY:n,outputBufferX:u,outputBufferY:f,start:l,end:y,deleteSegment:a,startTarget:c}=o.data,A=new Float64Array(s),d=new Float32Array(n),g=new Float64Array(u),p=new Float32Array(f);let e=0,r=c;for(let t=l;t<=y;t++)e<a.length&&t===a[e]?e++:(g[r]=A[t],p[r]=d[t],r++);self.postMessage("Done")}})();
2
- //# sourceMappingURL=delete-data.worker-Iw4O1ScI.js.map
@@ -1,2 +1 @@
1
1
  (function(){"use strict";self.onmessage=function(e){const{bufferX:f,bufferY:t,outputBufferX:u,outputBufferY:s}=e.data;self.postMessage("Done")}})();
2
- //# sourceMappingURL=fill-gaps.worker-BesEL5v2.js.map
@@ -1,60 +1,59 @@
1
- const T = {
2
- [E.SECOND]: 1,
3
- [E.MINUTE]: 60,
4
- [E.HOUR]: 3600,
5
- [E.DAY]: 86400,
6
- [E.WEEK]: 604800,
7
- [E.MONTH]: 108e3,
8
- [E.YEAR]: 31536e3
9
- }, D = (o, t, a) => {
10
- if (a === E.MONTH) {
11
- const e = new Date(o);
12
- return e.setMonth(e.getMonth() + t), e.getTime();
13
- } else if (a === E.YEAR) {
14
- const e = new Date(o);
15
- return e.setFullYear(e.getFullYear() + t), e.getTime();
16
- } else
17
- return o + t * T[a] * 1e3;
18
- }, w = async (o, t) => {
19
- const a = performance.now(), e = await o(), r = performance.now();
20
- console.log(` Done in ${(r - a).toFixed(2)} ms`);
21
- const s = +(r - a);
22
- return { response: e, duration: s };
23
- }, S = (o, t) => {
24
- let a = 0, e = o.length;
1
+ const A = (i, t) => {
2
+ let a = 0, e = i.length;
25
3
  for (; a < e; ) {
26
4
  const r = a + e >> 1;
27
- o[r] < t ? a = r + 1 : e = r;
5
+ i[r] < t ? a = r + 1 : e = r;
28
6
  }
29
7
  return a;
30
- }, L = (o, t) => {
31
- let a = 0, e = o.length;
8
+ }, T = (i, t) => {
9
+ let a = 0, e = i.length;
32
10
  for (; a < e; ) {
33
11
  const r = a + e >> 1;
34
- o[r] > t ? e = r : a = r + 1;
12
+ i[r] > t ? e = r : a = r + 1;
35
13
  }
36
14
  return a - 1;
37
- }, Y = {
38
- "Less than": (o, t) => o < t,
39
- "Less than or equal to": (o, t) => o <= t,
40
- "Greater than": (o, t) => o > t,
41
- "Greater than or equal to": (o, t) => o >= t,
42
- Equal: (o, t) => o == t,
43
- "Start datetime": (o, t) => o == t,
44
- "End datetime": (o, t) => o == t
45
- }, B = {
46
- "Less than": (o, t) => o < t,
47
- "Less than or equal to": (o, t) => o <= t,
48
- "Greater than": (o, t) => o > t,
49
- "Greater than or equal to": (o, t) => o >= t,
50
- Equal: (o, t) => o == t
15
+ }, _ = async (i, t) => {
16
+ const a = performance.now(), e = await i(), r = performance.now();
17
+ console.log(` Done in ${(r - a).toFixed(2)} ms`);
18
+ const s = +(r - a);
19
+ return { response: e, duration: s };
51
20
  };
52
- var E = /* @__PURE__ */ ((o) => (o.SECOND = "s", o.MINUTE = "m", o.HOUR = "h", o.DAY = "D", o.WEEK = "W", o.MONTH = "M", o.YEAR = "Y", o))(E || {});
53
- const g = 20 * 1e3, N = ["date", "value", "qualifier"];
21
+ const w = {
22
+ s: 1,
23
+ m: 60,
24
+ h: 3600,
25
+ D: 86400,
26
+ W: 604800,
27
+ M: 108e3,
28
+ Y: 31536e3
29
+ }, D = (i, t, a) => {
30
+ if (a === "M") {
31
+ const e = new Date(i);
32
+ return e.setMonth(e.getMonth() + t), e.getTime();
33
+ } else if (a === "Y") {
34
+ const e = new Date(i);
35
+ return e.setFullYear(e.getFullYear() + t), e.getTime();
36
+ } else
37
+ return i + t * w[a] * 1e3;
38
+ }, S = {
39
+ "Less than": (i, t) => i < t,
40
+ "Less than or equal to": (i, t) => i <= t,
41
+ "Greater than": (i, t) => i > t,
42
+ "Greater than or equal to": (i, t) => i >= t,
43
+ Equal: (i, t) => i == t,
44
+ "Start datetime": (i, t) => i == t,
45
+ "End datetime": (i, t) => i == t
46
+ }, Y = {
47
+ "Less than": (i, t) => i < t,
48
+ "Less than or equal to": (i, t) => i <= t,
49
+ "Greater than": (i, t) => i > t,
50
+ "Greater than or equal to": (i, t) => i >= t,
51
+ Equal: (i, t) => i == t
52
+ }, g = 20 * 1e3, B = ["date", "value", "qualifier"];
54
53
  class b {
55
54
  /** The generated dataset to be used for plotting */
56
55
  dataset = {
57
- dimensions: N,
56
+ dimensions: B,
58
57
  source: {
59
58
  x: new Float64Array(
60
59
  new SharedArrayBuffer(
@@ -87,7 +86,7 @@ class b {
87
86
  if (!t)
88
87
  return;
89
88
  this.isLoading = !0;
90
- const a = await w(() => {
89
+ const a = await _(() => {
91
90
  this._growBuffer(t.datetimes.length), this._resizeTo(t.datetimes.length), this.dataX.set(t.datetimes), this.dataY.set(t.dataValues);
92
91
  });
93
92
  this.loadingTime = a.duration, this.history.length = 0, this.isLoading = !1;
@@ -130,8 +129,8 @@ class b {
130
129
  {
131
130
  maxByteLength: e * Float32Array.BYTES_PER_ELEMENT
132
131
  }
133
- ), n = new Float64Array(r), i = new Float32Array(s);
134
- n.set(this.dataX), i.set(this.dataY), this.dataset.source.x = n, this.dataset.source.y = i;
132
+ ), n = new Float64Array(r), o = new Float32Array(s);
133
+ n.set(this.dataX), o.set(this.dataY), this.dataset.source.x = n, this.dataset.source.y = o;
135
134
  }
136
135
  this.dataX.buffer.byteLength < t * Float64Array.BYTES_PER_ELEMENT && (this.dataX.buffer.grow(t * Float64Array.BYTES_PER_ELEMENT), this.dataY.buffer.grow(t * Float32Array.BYTES_PER_ELEMENT));
137
136
  }
@@ -186,22 +185,22 @@ class b {
186
185
  try {
187
186
  if (Array.isArray(t)) {
188
187
  for (let n = 0; n < t.length; n++) {
189
- const i = t[n][0], h = t[n].slice(1, t[n].length), l = {
190
- method: i,
188
+ const o = t[n][0], h = t[n].slice(1, t[n].length), l = {
189
+ method: o,
191
190
  args: h,
192
- icon: r[i],
191
+ icon: r[o],
193
192
  isLoading: !1
194
193
  };
195
194
  this.history.push(l);
196
195
  }
197
196
  for (let n = this.history.length - t.length; n < this.history.length; n++) {
198
- const i = this.history[n];
199
- i.isLoading = !0;
200
- const h = await w(async () => await e[i.method].apply(
197
+ const o = this.history[n];
198
+ o.isLoading = !0;
199
+ const h = await _(async () => await e[o.method].apply(
201
200
  this,
202
- i.args
201
+ o.args
203
202
  ));
204
- i.duration = h.duration, i.isLoading = !1, s.push(h.response);
203
+ o.duration = h.duration, o.isLoading = !1, s.push(h.response);
205
204
  }
206
205
  } else {
207
206
  const n = {
@@ -211,8 +210,8 @@ class b {
211
210
  isLoading: !0
212
211
  };
213
212
  this.history.push(n);
214
- const i = await w(async () => await e[t].apply(this, a));
215
- s = i.response, n.duration = i.duration, n.isLoading = !1;
213
+ const o = await _(async () => await e[t].apply(this, a));
214
+ s = o.response, n.duration = o.duration, n.isLoading = !1;
216
215
  }
217
216
  } catch (n) {
218
217
  console.log(
@@ -234,7 +233,7 @@ class b {
234
233
  try {
235
234
  if (Array.isArray(t))
236
235
  for (let s = 0; s < t.length; s++) {
237
- const n = t[s][0], i = t[s].slice(1, t[s].length), h = await e[n].apply(this, i);
236
+ const n = t[s][0], o = t[s].slice(1, t[s].length), h = await e[n].apply(this, o);
238
237
  r.push(h);
239
238
  }
240
239
  else
@@ -277,15 +276,15 @@ class b {
277
276
  _interpolate(t) {
278
277
  this._getConsecutiveGroups(t).forEach((e) => {
279
278
  const r = e[0], s = e[e.length - 1];
280
- let n = Math.max(0, r - 1), i = Math.min(this.dataset.source.y.length - 1, s + 1);
279
+ let n = Math.max(0, r - 1), o = Math.min(this.dataset.source.y.length - 1, s + 1);
281
280
  const h = this.dataset.source.x, l = this.dataset.source.y;
282
281
  for (let c = 0; c < e.length; c++)
283
282
  this.dataset.source.y[e[c]] = this._interpolateLinear(
284
283
  h[e[c]],
285
284
  h[n],
286
285
  l[n],
287
- h[i],
288
- l[i]
286
+ h[o],
287
+ l[o]
289
288
  );
290
289
  });
291
290
  }
@@ -308,13 +307,13 @@ class b {
308
307
  await this._deleteDataPoints(t), await this._addDataPoints(r);
309
308
  }
310
309
  async _fillGapsV2(t, a, e, r) {
311
- const s = navigator.hardwareConcurrency || 1, n = [], i = [], h = this.dataX.length, l = new SharedArrayBuffer(this.dataX.buffer.byteLength, {
310
+ const s = navigator.hardwareConcurrency || 1, n = [], o = [], h = this.dataX.length, l = new SharedArrayBuffer(this.dataX.buffer.byteLength, {
312
311
  maxByteLength: this.dataX.buffer.maxByteLength
313
312
  }), c = new SharedArrayBuffer(this.dataY.buffer.byteLength, {
314
313
  maxByteLength: this.dataY.buffer.maxByteLength
315
314
  });
316
315
  for (let u = 0; u < s; u++)
317
- i.push(
316
+ o.push(
318
317
  new Promise((d) => {
319
318
  const f = new Worker(
320
319
  new URL(
@@ -333,7 +332,7 @@ class b {
333
332
  };
334
333
  })
335
334
  );
336
- await Promise.all(i), n.forEach((u) => u.terminate()), this.dataset.source.x = new Float64Array(l), this.dataset.source.y = new Float32Array(c), this._resizeTo(h);
335
+ await Promise.all(o), n.forEach((u) => u.terminate()), this.dataset.source.x = new Float64Array(l), this.dataset.source.y = new Float32Array(c), this._resizeTo(h);
337
336
  }
338
337
  /**
339
338
  * Find gaps and fill them with placeholder value
@@ -346,15 +345,15 @@ class b {
346
345
  _fillGaps(t, a, e, r) {
347
346
  const s = this._findGaps(t[0], t[1], r);
348
347
  for (let n = s.length - 1; n >= 0; n--) {
349
- const i = s[n], h = this.dataX[i[0]], l = this.dataX[i[1]], c = [], u = a[0] * T[a[1]] * 1e3;
348
+ const o = s[n], h = this.dataX[o[0]], l = this.dataX[o[1]], c = [], u = a[0] * w[a[1]] * 1e3;
350
349
  let d = h + u;
351
350
  for (; d < l; ) {
352
351
  const f = e ? this._interpolateLinear(
353
352
  d,
354
- this.dataX[i[0]],
355
- this.dataY[i[0]],
356
- this.dataX[i[1]],
357
- this.dataY[i[1]]
353
+ this.dataX[o[0]],
354
+ this.dataY[o[0]],
355
+ this.dataX[o[1]],
356
+ this.dataY[o[1]]
358
357
  ) : -9999;
359
358
  c.push([d, f]), d += u;
360
359
  }
@@ -373,29 +372,29 @@ class b {
373
372
  async _deleteDataPoints(t) {
374
373
  const a = navigator.hardwareConcurrency || 1, e = Math.ceil(this.dataX.length / a), r = [], s = [];
375
374
  for (let u = 0; u < a; u++) {
376
- const d = u * e, f = Math.min((u + 1) * e - 1, this.dataX.length - 1), y = S(t, d), p = L(t, f), m = t.slice(y, p + 1);
375
+ const d = u * e, f = Math.min((u + 1) * e - 1, this.dataX.length - 1), y = A(t, d), E = T(t, f), m = t.slice(y, E + 1);
377
376
  s.push({ start: d, end: f, deleteSegment: m });
378
377
  }
379
378
  const n = new Array(a).fill(0);
380
379
  for (let u = 1; u < a; u++)
381
380
  n[u] = n[u - 1] + s[u - 1].deleteSegment.length;
382
- const i = [], h = this.dataX.length - t.length, l = new SharedArrayBuffer(this.dataX.buffer.byteLength, {
381
+ const o = [], h = this.dataX.length - t.length, l = new SharedArrayBuffer(this.dataX.buffer.byteLength, {
383
382
  maxByteLength: this.dataX.buffer.maxByteLength
384
383
  }), c = new SharedArrayBuffer(this.dataY.buffer.byteLength, {
385
384
  maxByteLength: this.dataY.buffer.maxByteLength
386
385
  });
387
386
  for (let u = 0; u < a; u++) {
388
- const { start: d, end: f, deleteSegment: y } = s[u], p = d - n[u];
389
- i.push(
387
+ const { start: d, end: f, deleteSegment: y } = s[u], E = d - n[u];
388
+ o.push(
390
389
  new Promise((m) => {
391
- const _ = new Worker(
390
+ const p = new Worker(
392
391
  new URL(
393
392
  /* @vite-ignore */
394
393
  "/assets/delete-data.worker-Iw4O1ScI.js",
395
394
  import.meta.url
396
395
  )
397
396
  );
398
- r.push(_), _.postMessage({
397
+ r.push(p), p.postMessage({
399
398
  bufferX: this.dataX.buffer,
400
399
  bufferY: this.dataY.buffer,
401
400
  outputBufferX: l,
@@ -403,14 +402,14 @@ class b {
403
402
  start: d,
404
403
  end: f,
405
404
  deleteSegment: y,
406
- startTarget: p
407
- }), _.onmessage = (A) => {
408
- m(A.data);
405
+ startTarget: E
406
+ }), p.onmessage = (L) => {
407
+ m(L.data);
409
408
  };
410
409
  })
411
410
  );
412
411
  }
413
- await Promise.all(i), r.forEach((u) => u.terminate()), this.dataset.source.x = new Float64Array(l), this.dataset.source.y = new Float32Array(c), this._resizeTo(h);
412
+ await Promise.all(o), r.forEach((u) => u.terminate()), this.dataset.source.x = new Float64Array(l), this.dataset.source.y = new Float32Array(c), this._resizeTo(h);
414
413
  }
415
414
  /**
416
415
  *
@@ -442,12 +441,12 @@ class b {
442
441
  async _addDataPoints(t) {
443
442
  const a = this.dataX.length + t.length;
444
443
  this._growBuffer(a), t.sort((s, n) => s[0] - n[0]);
445
- const e = t.map((s) => L(this.dataX, s[0]) + 1);
444
+ const e = t.map((s) => T(this.dataX, s[0]) + 1);
446
445
  this._resizeTo(a), e.push(this.dataX.length);
447
446
  let r = t.length;
448
447
  for (let s = e.length - 1; s > 0; s--) {
449
- const n = e[s - 1], i = e[s] - 1;
450
- for (let h = i; h >= n; h--)
448
+ const n = e[s - 1], o = e[s] - 1;
449
+ for (let h = o; h >= n; h--)
451
450
  this.dataX[h + r] = this.dataX[h], this.dataY[h + r] = this.dataY[h];
452
451
  r--, this.dataX[n + r] = t[s - 1][0], this.dataY[n + r] = t[s - 1][1];
453
452
  }
@@ -463,7 +462,7 @@ class b {
463
462
  _valueThreshold(t) {
464
463
  const a = [];
465
464
  return this.dataset.source.y.forEach((e, r) => {
466
- Object.keys(t).some((s) => Y[s]?.(
465
+ Object.keys(t).some((s) => S[s]?.(
467
466
  e,
468
467
  t[s]
469
468
  )) && a.push(r);
@@ -479,7 +478,7 @@ class b {
479
478
  const e = [], r = this.dataset.source.y;
480
479
  for (let s = 1; s < r.length; s++) {
481
480
  const n = r[s - 1], h = (r[s] - n) / Math.abs(n);
482
- B[t]?.(
481
+ Y[t]?.(
483
482
  h,
484
483
  a
485
484
  ) && e.push(s);
@@ -495,12 +494,12 @@ class b {
495
494
  */
496
495
  _findGaps(t, a, e) {
497
496
  const r = [], s = this.dataset.source.x;
498
- let n = 0, i = s.length;
499
- e?.[0] && e?.[1] && (n = e[0], i = e[1]);
497
+ let n = 0, o = s.length;
498
+ e?.[0] && e?.[1] && (n = e[0], o = e[1]);
500
499
  let h = s[n];
501
- for (let l = n + 1; l <= i; l++) {
500
+ for (let l = n + 1; l <= o; l++) {
502
501
  const c = s[l];
503
- c - h > t * T[a] * 1e3 && r.push([l - 1, l]), h = c;
502
+ c - h > t * w[a] * 1e3 && r.push([l - 1, l]), h = c;
504
503
  }
505
504
  return r;
506
505
  }
@@ -513,19 +512,12 @@ class b {
513
512
  _persistence(t, a) {
514
513
  let e = [], r = this.dataset.source.y, s = 0, n = r.length;
515
514
  a?.[0] && a?.[1] && (s = a[0], n = a[1]);
516
- let i = r[s], h = [];
515
+ let o = r[s], h = [];
517
516
  for (let l = s + 1; l < n; l++)
518
- r[l] != i || l === n ? (h.length >= t && (e = [...e, ...h]), h = []) : h.push(l);
517
+ r[l] != o || l === n ? (h.length >= t && (e = [...e, ...h]), h = []) : h.push(l);
519
518
  return e;
520
519
  }
521
520
  }
522
- const F = {
523
- install: (o) => {
524
- o.use(b);
525
- }
526
- };
527
521
  export {
528
- b as ObservationRecord,
529
- F as QcUtils
522
+ b as ObservationRecord
530
523
  };
531
- //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ (function(g,f){typeof exports=="object"&&typeof module<"u"?f(exports):typeof define=="function"&&define.amd?define(["exports"],f):(g=typeof globalThis<"u"?globalThis:g||self,f(g["@uwrl/qc-utils"]={}))})(this,function(g){"use strict";var f=typeof document<"u"?document.currentScript:null;const Y=(i,t)=>{let a=0,e=i.length;for(;a<e;){const r=a+e>>1;i[r]<t?a=r+1:e=r}return a},D=(i,t)=>{let a=0,e=i.length;for(;a<e;){const r=a+e>>1;i[r]>t?e=r:a=r+1}return a-1},E=async(i,t)=>{const a=performance.now(),e=await i(),r=performance.now();console.log(` Done in ${(r-a).toFixed(2)} ms`);const s=+(r-a);return{response:e,duration:s}},b=1,B=b*60,_=B*60,w=_*24,F=w*7,x=_*30,R=w*365,T={s:b,m:B,h:_,D:w,W:F,M:x,Y:R},X=(i,t,a)=>{if(a==="M"){const e=new Date(i);return e.setMonth(e.getMonth()+t),e.getTime()}else if(a==="Y"){const e=new Date(i);return e.setFullYear(e.getFullYear()+t),e.getTime()}else return i+t*T[a]*1e3},I={"Less than":(i,t)=>i<t,"Less than or equal to":(i,t)=>i<=t,"Greater than":(i,t)=>i>t,"Greater than or equal to":(i,t)=>i>=t,Equal:(i,t)=>i==t,"Start datetime":(i,t)=>i==t,"End datetime":(i,t)=>i==t},N={"Less than":(i,t)=>i<t,"Less than or equal to":(i,t)=>i<=t,"Greater than":(i,t)=>i>t,"Greater than or equal to":(i,t)=>i>=t,Equal:(i,t)=>i==t},p=20*1e3,P=["date","value","qualifier"];class M{dataset={dimensions:P,source:{x:new Float64Array(new SharedArrayBuffer(p*Float64Array.BYTES_PER_ELEMENT,{maxByteLength:p*Float64Array.BYTES_PER_ELEMENT})),y:new Float32Array(new SharedArrayBuffer(p*Float32Array.BYTES_PER_ELEMENT,{maxByteLength:p*Float32Array.BYTES_PER_ELEMENT}))}};history=[];loadingTime=null;isLoading=!0;rawData;constructor(t){this.history=[],this.rawData=t,this.loadData(this.rawData)}async loadData(t){if(!t)return;this.isLoading=!0;const a=await E(()=>{this._growBuffer(t.datetimes.length),this._resizeTo(t.datetimes.length),this.dataX.set(t.datetimes),this.dataY.set(t.dataValues)});this.loadingTime=a.duration,this.history.length=0,this.isLoading=!1}get dataX(){return this.dataset.source.x}get dataY(){return this.dataset.source.y}_resizeTo(t){this.dataset.source.x=new Float64Array(this.dataset.source.x.buffer).subarray(0,t),this.dataset.source.y=new Float32Array(this.dataset.source.y.buffer).subarray(0,t)}_growBuffer(t){const a=t*Float64Array.BYTES_PER_ELEMENT;let e=this.dataX.buffer.byteLength;for(;a>e;)e+=p*Float64Array.BYTES_PER_ELEMENT;if(e*Float64Array.BYTES_PER_ELEMENT>this.dataX.buffer.maxByteLength){const r=new SharedArrayBuffer(this.dataX.buffer.byteLength,{maxByteLength:e*Float64Array.BYTES_PER_ELEMENT}),s=new SharedArrayBuffer(this.dataY.buffer.byteLength,{maxByteLength:e*Float32Array.BYTES_PER_ELEMENT}),n=new Float64Array(r),o=new Float32Array(s);n.set(this.dataX),o.set(this.dataY),this.dataset.source.x=n,this.dataset.source.y=o}this.dataX.buffer.byteLength<t*Float64Array.BYTES_PER_ELEMENT&&(this.dataX.buffer.grow(t*Float64Array.BYTES_PER_ELEMENT),this.dataY.buffer.grow(t*Float32Array.BYTES_PER_ELEMENT))}async reload(){this.loadingTime=null,this.isLoading=!0,this.history.length=0,await this.loadData(this.rawData)}async reloadHistory(t){const a=this.history.slice(0,t+1);await this.reload(),await this.dispatch(a.map(e=>[e.method,...e.args||[]]))}async removeHistoryItem(t){const a=[...this.history];a.splice(t,1),await this.reload(),await this.dispatch(a.map(e=>[e.method,...e.args||[]]))}get beginTime(){return this.dataset.source.x.length?new Date(this.dataset.source.x[0]):null}get endTime(){return this.dataset.source.x.length?new Date(this.dataset.source.x[this.dataset.source.x.length-1]):null}async dispatch(t,...a){const e={ADD_POINTS:this._addDataPoints,CHANGE_VALUES:this._changeValues,DELETE_POINTS:this._deleteDataPoints,DRIFT_CORRECTION:this._driftCorrection,INTERPOLATE:this._interpolate,SHIFT_DATETIMES:this._shift,FILL_GAPS:this._fillGaps},r={ADD_POINTS:"mdi-plus",CHANGE_VALUES:"mdi-pencil",DELETE_POINTS:"mdi-trash-can",DRIFT_CORRECTION:"mdi-chart-sankey",INTERPOLATE:"mdi-transit-connection-horizontal",SHIFT_DATETIMES:"mdi-calendar",FILL_GAPS:"mdi-keyboard-space"};let s=[];try{if(Array.isArray(t)){for(let n=0;n<t.length;n++){const o=t[n][0],h=t[n].slice(1,t[n].length),u={method:o,args:h,icon:r[o],isLoading:!1};this.history.push(u)}for(let n=this.history.length-t.length;n<this.history.length;n++){const o=this.history[n];o.isLoading=!0;const h=await E(async()=>await e[o.method].apply(this,o.args));o.duration=h.duration,o.isLoading=!1,s.push(h.response)}}else{const n={method:t,args:a,icon:r[t],isLoading:!0};this.history.push(n);const o=await E(async()=>await e[t].apply(this,a));s=o.response,n.duration=o.duration,n.isLoading=!1}}catch(n){console.log(`Failed to execute operation: ${t} with arguments: `,a),console.log(n)}return s}async dispatchFilter(t,...a){const e={FIND_GAPS:this._findGaps,VALUE_THRESHOLD:this._valueThreshold,PERSISTENCE:this._persistence,RATE_OF_CHANGE:this._rateOfChange};let r=[];try{if(Array.isArray(t))for(let s=0;s<t.length;s++){const n=t[s][0],o=t[s].slice(1,t[s].length),h=await e[n].apply(this,o);r.push(h)}else r=await e[t].apply(this,a)}catch(s){console.log(`Failed to execute filter operation: ${t} with arguments: `,a),console.log(s)}return r}_changeValues(t,a,e){const r=s=>{switch(a){case"ADD":return s+e;case"ASSIGN":return e;case"DIV":return s/e;case"MULT":return s*e;case"SUB":return s-e;default:return s}};t.forEach(s=>{this.dataset.source.y[s]=r(this.dataset.source.y[s])})}_interpolate(t){this._getConsecutiveGroups(t).forEach(e=>{const r=e[0],s=e[e.length-1];let n=Math.max(0,r-1),o=Math.min(this.dataset.source.y.length-1,s+1);const h=this.dataset.source.x,u=this.dataset.source.y;for(let c=0;c<e.length;c++)this.dataset.source.y[e[c]]=this._interpolateLinear(h[e[c]],h[n],u[n],h[o],u[o])})}_interpolateLinear(t,a,e,r,s){return e+(t-a)*(s-e)/(r-a)}async _shift(t,a,e){const r=t.map(s=>[X(this.dataX[s],a,e),this.dataY[s]]);await this._deleteDataPoints(t),await this._addDataPoints(r)}async _fillGapsV2(t,a,e,r){const s=navigator.hardwareConcurrency||1,n=[],o=[],h=this.dataX.length,u=new SharedArrayBuffer(this.dataX.buffer.byteLength,{maxByteLength:this.dataX.buffer.maxByteLength}),c=new SharedArrayBuffer(this.dataY.buffer.byteLength,{maxByteLength:this.dataY.buffer.maxByteLength});for(let l=0;l<s;l++)o.push(new Promise(d=>{const y=new Worker(new URL("/assets/fill-gaps.worker-BesEL5v2.js",typeof document>"u"&&typeof location>"u"?require("url").pathToFileURL(__filename).href:typeof document>"u"?location.href:f&&f.tagName.toUpperCase()==="SCRIPT"&&f.src||new URL("index.umd.cjs",document.baseURI).href));n.push(y),y.postMessage({bufferX:this.dataX.buffer,bufferY:this.dataY.buffer,outputBufferX:u,outputBufferY:c}),y.onmessage=m=>{d(m.data)}}));await Promise.all(o),n.forEach(l=>l.terminate()),this.dataset.source.x=new Float64Array(u),this.dataset.source.y=new Float32Array(c),this._resizeTo(h)}_fillGaps(t,a,e,r){const s=this._findGaps(t[0],t[1],r);for(let n=s.length-1;n>=0;n--){const o=s[n],h=this.dataX[o[0]],u=this.dataX[o[1]],c=[],l=a[0]*T[a[1]]*1e3;let d=h+l;for(;d<u;){const y=e?this._interpolateLinear(d,this.dataX[o[0]],this.dataY[o[0]],this.dataX[o[1]],this.dataY[o[1]]):-9999;c.push([d,y]),d+=l}this._addDataPoints(c)}}async _deleteDataPoints(t){const a=navigator.hardwareConcurrency||1,e=Math.ceil(this.dataX.length/a),r=[],s=[];for(let l=0;l<a;l++){const d=l*e,y=Math.min((l+1)*e-1,this.dataX.length-1),m=Y(t,d),L=D(t,y),A=t.slice(m,L+1);s.push({start:d,end:y,deleteSegment:A})}const n=new Array(a).fill(0);for(let l=1;l<a;l++)n[l]=n[l-1]+s[l-1].deleteSegment.length;const o=[],h=this.dataX.length-t.length,u=new SharedArrayBuffer(this.dataX.buffer.byteLength,{maxByteLength:this.dataX.buffer.maxByteLength}),c=new SharedArrayBuffer(this.dataY.buffer.byteLength,{maxByteLength:this.dataY.buffer.maxByteLength});for(let l=0;l<a;l++){const{start:d,end:y,deleteSegment:m}=s[l],L=d-n[l];o.push(new Promise(A=>{const S=new Worker(new URL("/assets/delete-data.worker-Iw4O1ScI.js",typeof document>"u"&&typeof location>"u"?require("url").pathToFileURL(__filename).href:typeof document>"u"?location.href:f&&f.tagName.toUpperCase()==="SCRIPT"&&f.src||new URL("index.umd.cjs",document.baseURI).href));r.push(S),S.postMessage({bufferX:this.dataX.buffer,bufferY:this.dataY.buffer,outputBufferX:u,outputBufferY:c,start:d,end:y,deleteSegment:m,startTarget:L}),S.onmessage=O=>{A(O.data)}}))}await Promise.all(o),r.forEach(l=>l.terminate()),this.dataset.source.x=new Float64Array(u),this.dataset.source.y=new Float32Array(c),this._resizeTo(h)}_driftCorrection(t,a,e){const r=this.dataset.source.x,s=this.dataset.source.y,n=r[t],h=r[a]-n;for(let u=t;u<a;u++)this.dataset.source.y[u]=s[u]+e*((r[u]-n)/h)}_getConsecutiveGroups(t){const a=[[]];return t.reduce((e,r)=>{const s=e[e.length-1];return!s.length||r==s[s.length-1]+1?s.push(r):e.push([r]),e},a),a}async _addDataPoints(t){const a=this.dataX.length+t.length;this._growBuffer(a),t.sort((s,n)=>s[0]-n[0]);const e=t.map(s=>D(this.dataX,s[0])+1);this._resizeTo(a),e.push(this.dataX.length);let r=t.length;for(let s=e.length-1;s>0;s--){const n=e[s-1],o=e[s]-1;for(let h=o;h>=n;h--)this.dataX[h+r]=this.dataX[h],this.dataY[h+r]=this.dataY[h];r--,this.dataX[n+r]=t[s-1][0],this.dataY[n+r]=t[s-1][1]}}_valueThreshold(t){const a=[];return this.dataset.source.y.forEach((e,r)=>{Object.keys(t).some(s=>I[s]?.(e,t[s]))&&a.push(r)}),a}_rateOfChange(t,a){const e=[],r=this.dataset.source.y;for(let s=1;s<r.length;s++){const n=r[s-1],h=(r[s]-n)/Math.abs(n);N[t]?.(h,a)&&e.push(s)}return e}_findGaps(t,a,e){const r=[],s=this.dataset.source.x;let n=0,o=s.length;e?.[0]&&e?.[1]&&(n=e[0],o=e[1]);let h=s[n];for(let u=n+1;u<=o;u++){const c=s[u];c-h>t*T[a]*1e3&&r.push([u-1,u]),h=c}return r}_persistence(t,a){let e=[],r=this.dataset.source.y,s=0,n=r.length;a?.[0]&&a?.[1]&&(s=a[0],n=a[1]);let o=r[s],h=[];for(let u=s+1;u<n;u++)r[u]!=o||u===n?(h.length>=t&&(e=[...e,...h]),h=[]):h.push(u);return e}}g.ObservationRecord=M,Object.defineProperty(g,Symbol.toStringTag,{value:"Module"})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uwrl/qc-utils",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Quality Control Utilities",
5
5
  "homepage": "https://github.com/hydroserver2/qc-utils#readme",
6
6
  "bugs": {
@@ -12,12 +12,13 @@
12
12
  },
13
13
  "license": "ISC",
14
14
  "author": "Maurier Ramirez",
15
- "type": "commonjs",
16
- "main": "./dist/index.mjs",
15
+ "type": "module",
16
+ "main": "./dist/index.cjs",
17
+ "module": "./dist/index.js",
17
18
  "exports": {
18
19
  ".": {
19
- "require": "./dist/index.umd.js",
20
- "import": "./dist/index.mjs"
20
+ "require": "./dist/index.cjs",
21
+ "import": "./dist/index.js"
21
22
  }
22
23
  },
23
24
  "files": [
@@ -1 +0,0 @@
1
- {"version":3,"file":"delete-data.worker-Iw4O1ScI.js","sources":["../src/utils/plotting/delete-data.worker.ts"],"sourcesContent":["self.onmessage = (e) => {\r\n const {\r\n bufferX,\r\n bufferY,\r\n outputBufferX,\r\n outputBufferY,\r\n start,\r\n end,\r\n deleteSegment,\r\n startTarget,\r\n } = e.data\r\n const arrayX = new Float64Array(bufferX)\r\n const arrayY = new Float32Array(bufferY)\r\n const outputArrayX = new Float64Array(outputBufferX)\r\n const outputArrayY = new Float32Array(outputBufferY)\r\n\r\n let deletePtr = 0\r\n let writePtr = startTarget\r\n\r\n // Copy non-deleted elements to output buffer\r\n for (let readPtr = start; readPtr <= end; readPtr++) {\r\n if (\r\n deletePtr < deleteSegment.length &&\r\n readPtr === deleteSegment[deletePtr]\r\n ) {\r\n deletePtr++ // Skip deleted index\r\n } else {\r\n outputArrayX[writePtr] = arrayX[readPtr]\r\n outputArrayY[writePtr] = arrayY[readPtr]\r\n writePtr++\r\n }\r\n }\r\n\r\n\r\n self.postMessage('Done')\r\n}\r\n"],"names":["e","bufferX","bufferY","outputBufferX","outputBufferY","start","end","deleteSegment","startTarget","arrayX","arrayY","outputArrayX","outputArrayY","deletePtr","writePtr","readPtr"],"mappings":"yBAAA,KAAK,UAAaA,GAAM,CACtB,KAAM,CACJ,QAAAC,EACA,QAAAC,EACA,cAAAC,EACA,cAAAC,EACA,MAAAC,EACA,IAAAC,EACA,cAAAC,EACA,YAAAC,CAAA,EACER,EAAE,KACAS,EAAS,IAAI,aAAaR,CAAO,EACjCS,EAAS,IAAI,aAAaR,CAAO,EACjCS,EAAe,IAAI,aAAaR,CAAa,EAC7CS,EAAe,IAAI,aAAaR,CAAa,EAEnD,IAAIS,EAAY,EACZC,EAAWN,EAGf,QAASO,EAAUV,EAAOU,GAAWT,EAAKS,IAEtCF,EAAYN,EAAc,QAC1BQ,IAAYR,EAAcM,CAAS,EAEnCA,KAEAF,EAAaG,CAAQ,EAAIL,EAAOM,CAAO,EACvCH,EAAaE,CAAQ,EAAIJ,EAAOK,CAAO,EACvCD,KAKJ,KAAK,YAAY,MAAM,CACzB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"fill-gaps.worker-BesEL5v2.js","sources":["../src/utils/plotting/fill-gaps.worker.ts"],"sourcesContent":["// Work in progress...\r\nself.onmessage = function (e) {\r\n const { bufferX, bufferY, outputBufferX, outputBufferY } = e.data\r\n\r\n const arrayX = new Float64Array(bufferX)\r\n const arrayY = new Float32Array(bufferY)\r\n const outputArrayX = new Float64Array(outputBufferX)\r\n const outputArrayY = new Float32Array(outputBufferY)\r\n\r\n self.postMessage('Done')\r\n}\r\n"],"names":["bufferX","bufferY","outputBufferX","outputBufferY"],"mappings":"yBACA,KAAK,UAAY,SAAU,EAAG,CAC5B,KAAM,CAAE,QAAAA,EAAS,QAAAC,EAAS,cAAAC,EAAe,cAAAC,CAAA,EAAkB,EAAE,KAO7D,KAAK,YAAY,MAAM,CACzB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/utils/format.ts","../src/utils/ellapsedTime.ts","../src/utils/observationsUtils.ts","../src/utils/plotting/observationRecord.ts","../src/index.ts"],"sourcesContent":["import { EnumDictionary, TimeUnit } from \"./plotting/observationRecord\"\r\n\r\nconst SECOND = 1\r\nconst MINUTE = SECOND * 60\r\nconst HOUR = MINUTE * 60\r\nconst DAY = HOUR * 24\r\nconst WEEK = DAY * 7\r\nconst MONTH = HOUR * 30\r\nconst YEAR = DAY * 365\r\n\r\nexport const timeUnitMultipliers: EnumDictionary<TimeUnit, number> = {\r\n [TimeUnit.SECOND]: SECOND,\r\n [TimeUnit.MINUTE]: MINUTE,\r\n [TimeUnit.HOUR]: HOUR,\r\n [TimeUnit.DAY]: DAY,\r\n [TimeUnit.WEEK]: WEEK,\r\n [TimeUnit.MONTH]: MONTH,\r\n [TimeUnit.YEAR]: YEAR,\r\n}\r\n\r\nexport const formatDate = (date: Date) => {\r\n return date.toLocaleString(undefined, {\r\n year: 'numeric',\r\n month: 'short',\r\n day: '2-digit',\r\n hour: '2-digit',\r\n hour12: false,\r\n minute: '2-digit',\r\n second: '2-digit',\r\n })\r\n}\r\n\r\nexport const formatDuration = (duration: number) => {\r\n let value\r\n let unit\r\n\r\n if (duration >= MINUTE * 1000) {\r\n value = duration / (MINUTE * 1000)\r\n unit = 'm'\r\n } else if (duration >= 1000) {\r\n value = duration / 1000\r\n unit = 's'\r\n } else {\r\n value = duration\r\n unit = 'ms'\r\n }\r\n\r\n let formattedValue\r\n if (unit === 'ms') {\r\n formattedValue = Math.round(value).toString()\r\n } else {\r\n formattedValue = value.toFixed(2)\r\n }\r\n\r\n return `${formattedValue} ${unit}`\r\n}\r\n\r\nexport const shiftDatetime = (\r\n datetime: number,\r\n amount: number,\r\n unit: TimeUnit\r\n) => {\r\n if (unit === TimeUnit.MONTH) {\r\n const currentDate = new Date(datetime)\r\n currentDate.setMonth(currentDate.getMonth() + amount)\r\n return currentDate.getTime()\r\n } else if (unit === TimeUnit.YEAR) {\r\n const currentDate = new Date(datetime)\r\n currentDate.setFullYear(currentDate.getFullYear() + amount)\r\n return currentDate.getTime()\r\n } else {\r\n return datetime + amount * timeUnitMultipliers[unit] * 1000\r\n }\r\n}\r\n","export const measureEllapsedTime = async (\r\n fn: () => any,\r\n message?: string\r\n): Promise<{ response: any; duration: number }> => {\r\n if (message) {\r\n console.log(message)\r\n }\r\n const start = performance.now()\r\n const response = await fn()\r\n const end = performance.now()\r\n if (import.meta.env.MODE !== 'test') {\r\n console.log(`\\tDone in ${(end - start).toFixed(2)} ms`)\r\n }\r\n const duration = +(end - start)\r\n return { response, duration }\r\n}\r\n","\r\nexport function subtractHours(timestamp: string, hours: number): string {\r\n const date = new Date(timestamp)\r\n date.setHours(date.getHours() - hours)\r\n return date.toISOString()\r\n}\r\n\r\n/** Returns the index of the first value that is greater or equal to the target value */\r\nexport const findFirstGreaterOrEqual = (\r\n array: number[] | Float64Array<SharedArrayBuffer>,\r\n target: number\r\n) => {\r\n let low = 0,\r\n high = array.length\r\n while (low < high) {\r\n const mid = (low + high) >> 1\r\n if (array[mid] < target) low = mid + 1\r\n else high = mid\r\n }\r\n return low\r\n}\r\n\r\n/** Returns the index of the last value that is lesser or equal to the target value */\r\nexport const findLastLessOrEqual = (\r\n array: number[] | Float64Array<SharedArrayBuffer>,\r\n target: number\r\n) => {\r\n let low = 0,\r\n high = array.length\r\n while (low < high) {\r\n const mid = (low + high) >> 1\r\n if (array[mid] > target) high = mid\r\n else low = mid + 1\r\n }\r\n return low - 1\r\n}","import { shiftDatetime, timeUnitMultipliers } from '../format'\r\nimport { measureEllapsedTime } from '../ellapsedTime'\r\nimport { findFirstGreaterOrEqual, findLastLessOrEqual } from '../observationsUtils'\r\n\r\nexport type EnumDictionary<T extends string | symbol | number, U> = {\r\n [K in T]: U\r\n}\r\n\r\nexport enum FilterOperation {\r\n LT = 'Less than',\r\n LTE = 'Less than or equal to',\r\n GT = 'Greater than',\r\n GTE = 'Greater than or equal to',\r\n E = 'Equal',\r\n START = 'Start datetime',\r\n END = 'End datetime',\r\n}\r\n\r\nexport const FilterOperationFn: EnumDictionary<\r\n FilterOperation,\r\n (value: number, toCompare: number) => boolean\r\n> = {\r\n [FilterOperation.LT]: (value: number, toCompare: number) => {\r\n return value < toCompare\r\n },\r\n [FilterOperation.LTE]: (value: number, toCompare: number) => {\r\n return value <= toCompare\r\n },\r\n [FilterOperation.GT]: (value: number, toCompare: number) => {\r\n return value > toCompare\r\n },\r\n [FilterOperation.GTE]: (value: number, toCompare: number) => {\r\n return value >= toCompare\r\n },\r\n [FilterOperation.E]: (value: number, toCompare: number) => {\r\n return value == toCompare\r\n },\r\n [FilterOperation.START]: (value: number, toCompare: number) => {\r\n return value == toCompare\r\n },\r\n [FilterOperation.END]: (value: number, toCompare: number) => {\r\n return value == toCompare\r\n },\r\n}\r\n\r\nexport enum Operator {\r\n ADD = 'ADD',\r\n SUB = 'SUB',\r\n MULT = 'MULT',\r\n DIV = 'DIV',\r\n ASSIGN = 'ASSIGN',\r\n}\r\n\r\nexport enum RateOfChangeOperation {\r\n LT = 'Less than',\r\n LTE = 'Less than or equal to',\r\n GT = 'Greater than',\r\n GTE = 'Greater than or equal to',\r\n E = 'Equal',\r\n}\r\n\r\nexport const RateOfChangeComparator: EnumDictionary<\r\n RateOfChangeOperation,\r\n (value: number, toCompare: number) => boolean\r\n> = {\r\n [RateOfChangeOperation.LT]: (value: number, toCompare: number) => {\r\n return value < toCompare\r\n },\r\n [RateOfChangeOperation.LTE]: (value: number, toCompare: number) => {\r\n return value <= toCompare\r\n },\r\n [RateOfChangeOperation.GT]: (value: number, toCompare: number) => {\r\n return value > toCompare\r\n },\r\n [RateOfChangeOperation.GTE]: (value: number, toCompare: number) => {\r\n return value >= toCompare\r\n },\r\n [RateOfChangeOperation.E]: (value: number, toCompare: number) => {\r\n return value == toCompare\r\n },\r\n}\r\n\r\nexport enum TimeUnit {\r\n SECOND = 's',\r\n MINUTE = 'm',\r\n HOUR = 'h',\r\n DAY = 'D',\r\n WEEK = 'W',\r\n MONTH = 'M',\r\n YEAR = 'Y',\r\n}\r\n\r\nexport enum EnumEditOperations {\r\n ADD_POINTS = 'ADD_POINTS',\r\n CHANGE_VALUES = 'CHANGE_VALUES',\r\n DELETE_POINTS = 'DELETE_POINTS',\r\n DRIFT_CORRECTION = 'DRIFT_CORRECTION',\r\n INTERPOLATE = 'INTERPOLATE',\r\n SHIFT_DATETIMES = 'SHIFT_DATETIMES',\r\n FILL_GAPS = 'FILL_GAPS',\r\n}\r\n\r\nexport enum EnumFilterOperations {\r\n FIND_GAPS = 'FIND_GAPS',\r\n PERSISTENCE = 'PERSISTENCE',\r\n RATE_OF_CHANGE = 'RATE_OF_CHANGE',\r\n VALUE_THRESHOLD = 'VALUE_THRESHOLD',\r\n}\r\n\r\nexport type HistoryItem = {\r\n method: EnumEditOperations\r\n icon: string\r\n isLoading: boolean\r\n args?: any[]\r\n duration?: number\r\n status?: 'success' | 'failed'\r\n}\r\n\r\n/**\r\n * This number should approximate the number of observations that a dataset could increase by during a session.\r\n * The lower this number, the less memory the entire app uses.\r\n * Note that when a dataset number of data points increases by more than `INCREASE_AMOUNT`,\r\n * the `_growBuffer()` method will allocate a new buffer, and the data will be copied into it.\r\n */\r\nexport const INCREASE_AMOUNT = 20 * 1000\r\n\r\nconst components = ['date', 'value', 'qualifier'] // TODO: `qualifier` unused for now...\r\n\r\nexport class ObservationRecord {\r\n /** The generated dataset to be used for plotting */\r\n dataset: {\r\n dimensions: string[]\r\n source: {\r\n // Store datetimes in a Float64Array because plotly can't parse BigInts correctly.\r\n x: Float64Array<SharedArrayBuffer>\r\n y: Float32Array<SharedArrayBuffer>\r\n }\r\n } = {\r\n dimensions: components,\r\n source: {\r\n x: new Float64Array(\r\n new SharedArrayBuffer(\r\n INCREASE_AMOUNT * Float64Array.BYTES_PER_ELEMENT,\r\n {\r\n maxByteLength: INCREASE_AMOUNT * Float64Array.BYTES_PER_ELEMENT, // Max size the array can reach\r\n }\r\n )\r\n ),\r\n y: new Float32Array(\r\n new SharedArrayBuffer(\r\n INCREASE_AMOUNT * Float32Array.BYTES_PER_ELEMENT,\r\n {\r\n maxByteLength: INCREASE_AMOUNT * Float32Array.BYTES_PER_ELEMENT, // Max size the array can reach\r\n }\r\n )\r\n ),\r\n },\r\n }\r\n history: HistoryItem[] = []\r\n loadingTime: number | null = null\r\n isLoading: boolean = true\r\n rawData: {\r\n datetimes: Float64Array<ArrayBuffer> | number[]\r\n dataValues: Float32Array<ArrayBuffer> | number[]\r\n }\r\n\r\n constructor(dataArrays: {\r\n datetimes: Float64Array<ArrayBuffer> | number[]\r\n dataValues: Float32Array<ArrayBuffer> | number[]\r\n }) {\r\n this.history = []\r\n this.rawData = dataArrays\r\n this.loadData(this.rawData)\r\n }\r\n\r\n async loadData(dataArrays: {\r\n datetimes: Float64Array<ArrayBuffer> | number[]\r\n dataValues: Float32Array<ArrayBuffer> | number[]\r\n }) {\r\n if (!dataArrays) {\r\n return\r\n }\r\n this.isLoading = true\r\n const measurement = await measureEllapsedTime(() => {\r\n this._growBuffer(dataArrays.datetimes.length)\r\n this._resizeTo(dataArrays.datetimes.length)\r\n\r\n this.dataX.set(dataArrays.datetimes)\r\n this.dataY.set(dataArrays.dataValues)\r\n })\r\n\r\n this.loadingTime = measurement.duration\r\n\r\n this.history.length = 0\r\n this.isLoading = false\r\n }\r\n\r\n get dataX() {\r\n return this.dataset.source.x\r\n }\r\n\r\n get dataY() {\r\n return this.dataset.source.y\r\n }\r\n\r\n /**\r\n * Resizes the typed array\r\n * @param length The total number of elements that the view will contain\r\n */\r\n private _resizeTo(length: number) {\r\n // We need to resize the view to match our data length,\r\n // but TypedArrays using SharedArrayBuffer can't shrink.\r\n // Recreate the view to effectively resize it\r\n this.dataset.source.x = new Float64Array(\r\n this.dataset.source.x.buffer\r\n ).subarray(0, length)\r\n\r\n this.dataset.source.y = new Float32Array(\r\n this.dataset.source.y.buffer\r\n ).subarray(0, length)\r\n }\r\n\r\n /**\r\n * Buffer size is always in increments of `INCREASE_AMOUNT`.\r\n * Grows the buffer by `INCREASE_AMOUNT` in bytes if the current data doesn't fit\r\n * @param newLength The total number of elements that the view will contain\r\n */\r\n private _growBuffer(newLength: number) {\r\n const dataArrayByteSizeX = newLength * Float64Array.BYTES_PER_ELEMENT\r\n\r\n let maxByteLengthNeeded = this.dataX.buffer.byteLength\r\n while (dataArrayByteSizeX > maxByteLengthNeeded) {\r\n maxByteLengthNeeded += INCREASE_AMOUNT * Float64Array.BYTES_PER_ELEMENT\r\n }\r\n\r\n if (\r\n maxByteLengthNeeded * Float64Array.BYTES_PER_ELEMENT >\r\n this.dataX.buffer.maxByteLength\r\n ) {\r\n // More space is needed, beyond the maxByteLength initially set, to allocate the data. A new buffer needs to be allocated.\r\n const outputBufferX = new SharedArrayBuffer(\r\n this.dataX.buffer.byteLength,\r\n {\r\n maxByteLength: maxByteLengthNeeded * Float64Array.BYTES_PER_ELEMENT,\r\n }\r\n )\r\n\r\n const outputBufferY = new SharedArrayBuffer(\r\n this.dataY.buffer.byteLength,\r\n {\r\n maxByteLength: maxByteLengthNeeded * Float32Array.BYTES_PER_ELEMENT,\r\n }\r\n )\r\n\r\n const outputArrayX = new Float64Array(outputBufferX)\r\n const outputArrayY = new Float32Array(outputBufferY)\r\n outputArrayX.set(this.dataX)\r\n outputArrayY.set(this.dataY)\r\n\r\n // Swap to the new array and buffer\r\n this.dataset.source.x = outputArrayX\r\n this.dataset.source.y = outputArrayY\r\n }\r\n\r\n if (\r\n this.dataX.buffer.byteLength <\r\n newLength * Float64Array.BYTES_PER_ELEMENT\r\n ) {\r\n this.dataX.buffer.grow(newLength * Float64Array.BYTES_PER_ELEMENT)\r\n this.dataY.buffer.grow(newLength * Float32Array.BYTES_PER_ELEMENT)\r\n }\r\n }\r\n\r\n /**\r\n * Reloads the dataset with the raw data\r\n */\r\n async reload() {\r\n this.loadingTime = null\r\n this.isLoading = true\r\n this.history.length = 0\r\n await this.loadData(this.rawData)\r\n }\r\n\r\n /**\r\n * @param index\r\n * @returns\r\n */\r\n async reloadHistory(index: number) {\r\n const newHistory = this.history.slice(0, index + 1)\r\n await this.reload()\r\n\r\n await this.dispatch(newHistory.map((h) => [h.method, ...(h.args || [])]))\r\n return\r\n }\r\n\r\n /**\r\n * Remove a history item\r\n * @param index\r\n */\r\n async removeHistoryItem(index: number) {\r\n const newHistory = [...this.history]\r\n newHistory.splice(index, 1)\r\n await this.reload()\r\n await this.dispatch(newHistory.map((h) => [h.method, ...(h.args || [])]))\r\n }\r\n\r\n get beginTime(): Date | null {\r\n if (!this.dataset.source.x.length) {\r\n return null\r\n }\r\n return new Date(this.dataset.source.x[0])\r\n }\r\n\r\n get endTime(): Date | null {\r\n if (!this.dataset.source.x.length) {\r\n return null\r\n }\r\n return new Date(this.dataset.source.x[this.dataset.source.x.length - 1])\r\n }\r\n\r\n /** Dispatch an operation and log its signature in hisotry */\r\n async dispatch(\r\n action: EnumEditOperations | [EnumEditOperations, ...any][],\r\n ...args: any\r\n ) {\r\n const actions: EnumDictionary<EnumEditOperations, Function> = {\r\n [EnumEditOperations.ADD_POINTS]: this._addDataPoints,\r\n [EnumEditOperations.CHANGE_VALUES]: this._changeValues,\r\n [EnumEditOperations.DELETE_POINTS]: this._deleteDataPoints,\r\n [EnumEditOperations.DRIFT_CORRECTION]: this._driftCorrection,\r\n [EnumEditOperations.INTERPOLATE]: this._interpolate,\r\n [EnumEditOperations.SHIFT_DATETIMES]: this._shift,\r\n [EnumEditOperations.FILL_GAPS]: this._fillGaps,\r\n }\r\n\r\n // TODO: consolidate with icons in EditDrawer component\r\n const editIcons: EnumDictionary<EnumEditOperations, string> = {\r\n [EnumEditOperations.ADD_POINTS]: 'mdi-plus',\r\n [EnumEditOperations.CHANGE_VALUES]: 'mdi-pencil',\r\n [EnumEditOperations.DELETE_POINTS]: 'mdi-trash-can',\r\n [EnumEditOperations.DRIFT_CORRECTION]: 'mdi-chart-sankey',\r\n [EnumEditOperations.INTERPOLATE]: 'mdi-transit-connection-horizontal',\r\n [EnumEditOperations.SHIFT_DATETIMES]: 'mdi-calendar',\r\n [EnumEditOperations.FILL_GAPS]: 'mdi-keyboard-space',\r\n }\r\n\r\n let response: any[] = []\r\n\r\n try {\r\n if (Array.isArray(action)) {\r\n for (let i = 0; i < action.length; i++) {\r\n const method = action[i][0]\r\n const actionArgs = action[i].slice(1, action[i].length)\r\n const historyItem: HistoryItem = {\r\n method,\r\n args: actionArgs,\r\n icon: editIcons[method],\r\n isLoading: false,\r\n }\r\n this.history.push(historyItem)\r\n }\r\n\r\n for (\r\n let i = this.history.length - action.length;\r\n i < this.history.length;\r\n i++\r\n ) {\r\n const historyItem = this.history[i]\r\n historyItem.isLoading = true\r\n\r\n const measurement = await measureEllapsedTime(async () => {\r\n return await actions[historyItem.method].apply(\r\n this,\r\n historyItem.args\r\n )\r\n })\r\n historyItem.duration = measurement.duration\r\n historyItem.isLoading = false\r\n response.push(measurement.response)\r\n }\r\n } else {\r\n const historyItem: HistoryItem = {\r\n method: action,\r\n args,\r\n icon: editIcons[action],\r\n isLoading: true,\r\n }\r\n this.history.push(historyItem)\r\n const measurement = await measureEllapsedTime(async () => {\r\n return await actions[action].apply(this, args)\r\n })\r\n response = measurement.response\r\n historyItem.duration = measurement.duration\r\n historyItem.isLoading = false\r\n }\r\n } catch (e) {\r\n console.log(\r\n `Failed to execute operation: ${action} with arguments: `,\r\n args\r\n )\r\n console.log(e)\r\n }\r\n\r\n return response\r\n }\r\n\r\n /** Filter operations do not transform the data and are not logged in history */\r\n async dispatchFilter(\r\n action: EnumFilterOperations | [EnumFilterOperations, ...any][],\r\n ...args: any\r\n ) {\r\n const filters: EnumDictionary<EnumFilterOperations, Function> = {\r\n [EnumFilterOperations.FIND_GAPS]: this._findGaps,\r\n [EnumFilterOperations.VALUE_THRESHOLD]: this._valueThreshold,\r\n [EnumFilterOperations.PERSISTENCE]: this._persistence,\r\n [EnumFilterOperations.RATE_OF_CHANGE]: this._rateOfChange,\r\n }\r\n let response = []\r\n\r\n try {\r\n if (Array.isArray(action)) {\r\n for (let i = 0; i < action.length; i++) {\r\n const method = action[i][0]\r\n const args = action[i].slice(1, action[i].length)\r\n const res = await filters[method].apply(this, args)\r\n response.push(res)\r\n }\r\n } else {\r\n response = await filters[action].apply(this, args)\r\n }\r\n } catch (e) {\r\n console.log(\r\n `Failed to execute filter operation: ${action} with arguments: `,\r\n args\r\n )\r\n console.log(e)\r\n }\r\n return response\r\n }\r\n\r\n /**\r\n * @param index An array containing the list of index of values to perform the operations on.\r\n * @param operator The operator that will be applied\r\n * @param value The value to use in the operation\r\n * @returns The modified DataFrame\r\n */\r\n private _changeValues(index: number[], operator: Operator, value: number) {\r\n const operation = (x: number) => {\r\n switch (operator) {\r\n case Operator.ADD:\r\n return x + value\r\n case Operator.ASSIGN:\r\n return value\r\n case Operator.DIV:\r\n return x / value\r\n case Operator.MULT:\r\n return x * value\r\n case Operator.SUB:\r\n return x - value\r\n default:\r\n return x\r\n }\r\n }\r\n\r\n index.forEach((index: number) => {\r\n this.dataset.source.y[index] = operation(this.dataset.source.y[index])\r\n })\r\n }\r\n\r\n private _interpolate(index: number[]) {\r\n const groups = this._getConsecutiveGroups(index)\r\n\r\n groups.forEach((g) => {\r\n const start = g[0]\r\n const end = g[g.length - 1]\r\n\r\n let lowerIndex = Math.max(0, start - 1)\r\n let upperIndex = Math.min(this.dataset.source.y.length - 1, end + 1)\r\n\r\n const xData = this.dataset.source.x\r\n const yData = this.dataset.source.y\r\n for (let i = 0; i < g.length; i++) {\r\n this.dataset.source.y[g[i]] = this._interpolateLinear(\r\n xData[g[i]],\r\n xData[lowerIndex],\r\n yData[lowerIndex],\r\n xData[upperIndex],\r\n yData[upperIndex]\r\n )\r\n }\r\n })\r\n }\r\n\r\n /** Interpolate existing values in the data source */\r\n private _interpolateLinear(\r\n datetime: number,\r\n lowerDatetime: number,\r\n lowerValue: number,\r\n upperDatetime: number,\r\n upperValue: number\r\n ) {\r\n const interpolatedValue =\r\n lowerValue +\r\n ((datetime - lowerDatetime) * (upperValue - lowerValue)) /\r\n (upperDatetime - lowerDatetime)\r\n\r\n return interpolatedValue\r\n }\r\n\r\n /**\r\n * Shifts the selected indexes by specified amount of units. Elements are reinserted according to their datetime.\r\n * @param index The index of the elements to shift\r\n * @param amount Number of {@link TimeUnit}\r\n * @param unit {@link TimeUnit}\r\n * @returns\r\n */\r\n private async _shift(index: number[], amount: number, unit: TimeUnit) {\r\n // Collection that will be re-added using `_addDataPoints`\r\n const collection: [number, number][] = index.map((i) => [\r\n shiftDatetime(this.dataX[i], amount, unit),\r\n this.dataY[i],\r\n ])\r\n // TODO: add dedicated method to do these in one go\r\n await this._deleteDataPoints(index)\r\n await this._addDataPoints(collection)\r\n }\r\n\r\n private async _fillGapsV2(\r\n gap: [number, TimeUnit],\r\n fill: [number, TimeUnit],\r\n interpolateValues: boolean,\r\n range?: [number, number]\r\n ) {\r\n const numWorkers = navigator.hardwareConcurrency || 1\r\n const workers: Worker[] = []\r\n const promises = []\r\n const newLength = this.dataX.length\r\n\r\n // To avoid workers reading from a memory address where another working is writing to, we use separate output buffers.\r\n const outputBufferX = new SharedArrayBuffer(this.dataX.buffer.byteLength, {\r\n maxByteLength: this.dataX.buffer.maxByteLength,\r\n })\r\n\r\n const outputBufferY = new SharedArrayBuffer(this.dataY.buffer.byteLength, {\r\n maxByteLength: this.dataY.buffer.maxByteLength,\r\n })\r\n\r\n // Compute startTarget for each segment and start workers\r\n for (let i = 0; i < numWorkers; i++) {\r\n // Spawn workers\r\n promises.push(\r\n new Promise((resolve) => {\r\n const worker = new Worker(\r\n new URL('fill-gaps.worker.ts', import.meta.url)\r\n )\r\n workers.push(worker)\r\n worker.postMessage({\r\n bufferX: this.dataX.buffer,\r\n bufferY: this.dataY.buffer,\r\n outputBufferX,\r\n outputBufferY,\r\n })\r\n worker.onmessage = (event: MessageEvent) => {\r\n resolve(event.data)\r\n }\r\n })\r\n )\r\n }\r\n\r\n await Promise.all(promises)\r\n\r\n workers.forEach((worker) => worker.terminate()) // Important to terminate the workers\r\n\r\n this.dataset.source.x = new Float64Array(outputBufferX)\r\n this.dataset.source.y = new Float32Array(outputBufferY)\r\n this._resizeTo(newLength)\r\n }\r\n\r\n /**\r\n * Find gaps and fill them with placeholder value\r\n * @param gap Intervals to detect as gaps\r\n * @param fill Interval used to fill the detected gaps\r\n * @param interpolateValues If true, the new values will be linearly interpolated\r\n * @returns\r\n */\r\n // TODO: this needs to be improved using web workers\r\n private _fillGaps(\r\n gap: [number, TimeUnit],\r\n fill: [number, TimeUnit],\r\n interpolateValues: boolean,\r\n range?: [number, number]\r\n ) {\r\n const gaps = this._findGaps(gap[0], gap[1], range)\r\n\r\n for (let i = gaps.length - 1; i >= 0; i--) {\r\n const currentGap = gaps[i]\r\n const leftDatetime = this.dataX[currentGap[0]]\r\n const rightDatetime = this.dataX[currentGap[1]]\r\n const fillPoints: [number, number][] = []\r\n\r\n // TODO: number of seconds in a year or month is not constant\r\n // Use setMonth and setFullYear instead\r\n const fillDelta = fill[0] * timeUnitMultipliers[fill[1]] * 1000\r\n let nextFillDatetime = leftDatetime + fillDelta\r\n\r\n while (nextFillDatetime < rightDatetime) {\r\n const val: number = interpolateValues\r\n ? this._interpolateLinear(\r\n nextFillDatetime,\r\n this.dataX[currentGap[0]],\r\n this.dataY[currentGap[0]],\r\n this.dataX[currentGap[1]],\r\n this.dataY[currentGap[1]]\r\n )\r\n : -9999\r\n\r\n fillPoints.push([nextFillDatetime, val])\r\n nextFillDatetime += fillDelta\r\n }\r\n\r\n this._addDataPoints(fillPoints)\r\n }\r\n }\r\n\r\n /**\r\n Deletes data points from a large array using worker threads.\r\n 1. The main thread divides the original array into equal parts to distribute work among workers.\r\n 2. For each segment, binary search locates the indexes to delete (deleteSegment), ensuring efficient lookups.\r\n 3. The cumulative deletions before each segment help compute the starting index (startTarget) for each worker's output, ensuring no overlap.\r\n 4. Each worker processes its segment linearly, skipping deletions and copying kept elements to their computed positions.\r\n * @param deleteIndices \r\n */\r\n // TODO: implement similar multithread solutions for other operations\r\n private async _deleteDataPoints(deleteIndices: number[]) {\r\n const numWorkers = navigator.hardwareConcurrency || 1\r\n const segmentSize = Math.ceil(this.dataX.length / numWorkers)\r\n const workers: Worker[] = []\r\n const segments = []\r\n\r\n // Prepare segments\r\n for (let i = 0; i < numWorkers; i++) {\r\n const start = i * segmentSize\r\n const end = Math.min((i + 1) * segmentSize - 1, this.dataX.length - 1)\r\n\r\n // Binary search to find deleteSegment within [start, end]\r\n const first = findFirstGreaterOrEqual(deleteIndices, start)\r\n const last = findLastLessOrEqual(deleteIndices, end)\r\n const deleteSegment = deleteIndices.slice(first, last + 1)\r\n\r\n segments.push({ start, end, deleteSegment })\r\n }\r\n\r\n // Compute prefix sums. These help distribute the work evenly.\r\n const prefixSum = new Array(numWorkers).fill(0)\r\n for (let i = 1; i < numWorkers; i++) {\r\n prefixSum[i] = prefixSum[i - 1] + segments[i - 1].deleteSegment.length\r\n }\r\n\r\n const promises = []\r\n const newLength = this.dataX.length - deleteIndices.length\r\n\r\n // // To avoid workers reading from a memory address where another working is writing to, we use separate output buffers.\r\n const outputBufferX = new SharedArrayBuffer(this.dataX.buffer.byteLength, {\r\n maxByteLength: this.dataX.buffer.maxByteLength,\r\n })\r\n\r\n const outputBufferY = new SharedArrayBuffer(this.dataY.buffer.byteLength, {\r\n maxByteLength: this.dataY.buffer.maxByteLength,\r\n })\r\n\r\n // Compute startTarget for each segment and start workers\r\n for (let i = 0; i < numWorkers; i++) {\r\n const { start, end, deleteSegment } = segments[i]\r\n const startTarget = start - prefixSum[i]\r\n\r\n // Spawn workers\r\n promises.push(\r\n new Promise((resolve) => {\r\n const worker = new Worker(\r\n new URL('delete-data.worker.ts', import.meta.url)\r\n )\r\n workers.push(worker)\r\n worker.postMessage({\r\n bufferX: this.dataX.buffer,\r\n bufferY: this.dataY.buffer,\r\n outputBufferX,\r\n outputBufferY,\r\n start,\r\n end,\r\n deleteSegment,\r\n startTarget,\r\n })\r\n worker.onmessage = (event: MessageEvent) => {\r\n resolve(event.data)\r\n }\r\n })\r\n )\r\n }\r\n\r\n // Prevents vitest from halting during execution of multiple promises\r\n if (import.meta.env.MODE !== 'test') {\r\n await Promise.all(promises)\r\n }\r\n\r\n workers.forEach((worker) => worker.terminate()) // Important to terminate the workers\r\n\r\n this.dataset.source.x = new Float64Array(outputBufferX)\r\n this.dataset.source.y = new Float32Array(outputBufferY)\r\n this._resizeTo(newLength)\r\n }\r\n\r\n /**\r\n *\r\n * @param start The start index\r\n * @param end The end index\r\n * @param value The drift amount\r\n */\r\n private _driftCorrection(start: number, end: number, value: number) {\r\n const xData = this.dataset.source.x\r\n const yData = this.dataset.source.y\r\n\r\n const startDatetime = xData[start]\r\n const endDatetime = xData[end]\r\n const extent = endDatetime - startDatetime\r\n\r\n for (let i = start; i < end; i++) {\r\n // y_n = y_0 + G(x_i / extent)\r\n this.dataset.source.y[i] =\r\n yData[i] + value * ((xData[i] - startDatetime) / extent)\r\n }\r\n }\r\n\r\n /** Traverses the index array and returns groups of consecutive values.\r\n * i.e.: `[0, 1, 3, 4, 6] => [[0, 1], [3, 4], [6]]`\r\n * Assumes the input array is sorted.\r\n * @param index: the index array (sorted)\r\n */\r\n private _getConsecutiveGroups(index: number[]): number[][] {\r\n const groups: number[][] = [[]]\r\n\r\n // Form groups of consecutive points to delete in order to minimize the number of splice operations\r\n index.reduce((acc: number[][], curr: number) => {\r\n const target: number[] = acc[acc.length - 1]\r\n\r\n if (!target.length || curr == target[target.length - 1] + 1) {\r\n target.push(curr)\r\n } else {\r\n acc.push([curr])\r\n }\r\n\r\n return acc\r\n }, groups)\r\n\r\n return groups\r\n }\r\n\r\n /**\r\n * Adds data points. Their insert index is determined using `findFirstGreaterOrEqual` in the x-axis.\r\n * @param dataPoints\r\n */\r\n private async _addDataPoints(dataPoints: [number, number][]) {\r\n // Check if more space is needed\r\n const newLength = this.dataX.length + dataPoints.length\r\n this._growBuffer(newLength)\r\n // Sort the datapoints by datetime in reverse order\r\n dataPoints.sort((a, b) => {\r\n return a[0] - b[0]\r\n })\r\n\r\n const insertIndex = dataPoints.map((point) => {\r\n return findLastLessOrEqual(this.dataX, point[0]) + 1\r\n })\r\n\r\n this._resizeTo(newLength) // The space needs to be allocated before insertion can happen\r\n\r\n insertIndex.push(this.dataX.length)\r\n\r\n // Shift elements to the right to make room for the items to insert\r\n let toInsert = dataPoints.length\r\n for (let i = insertIndex.length - 1; i > 0; i--) {\r\n const left = insertIndex[i - 1]\r\n const right = insertIndex[i] - 1\r\n\r\n for (let n = right; n >= left; n--) {\r\n this.dataX[n + toInsert] = this.dataX[n]\r\n this.dataY[n + toInsert] = this.dataY[n]\r\n }\r\n toInsert--\r\n this.dataX[left + toInsert] = dataPoints[i - 1][0]\r\n this.dataY[left + toInsert] = dataPoints[i - 1][1]\r\n }\r\n }\r\n\r\n // =======================\r\n // FILTER OPERATIONS\r\n // =======================\r\n\r\n /**\r\n * Filter by applying a set of logical operations\r\n * @param appliedFilters\r\n * @returns\r\n */\r\n private _valueThreshold(appliedFilters: { [key: string]: number }) {\r\n const selection: number[] = []\r\n\r\n this.dataset.source.y.forEach((value: number, index: number) => {\r\n if (\r\n Object.keys(appliedFilters).some((key) => {\r\n return FilterOperationFn[key as FilterOperation]?.(\r\n value,\r\n appliedFilters[key]\r\n )\r\n })\r\n ) {\r\n selection.push(index)\r\n }\r\n })\r\n\r\n return selection\r\n }\r\n\r\n /**\r\n *\r\n * @param comparator\r\n * @param value\r\n * @returns\r\n */\r\n private _rateOfChange(comparator: string, value: number) {\r\n const selection: number[] = []\r\n const dataY = this.dataset.source.y\r\n\r\n for (let i = 0 + 1; i < dataY.length; i++) {\r\n const prev = dataY[i - 1]\r\n const curr = dataY[i]\r\n const rate = (curr - prev) / Math.abs(prev)\r\n\r\n if (\r\n RateOfChangeComparator[comparator as RateOfChangeOperation]?.(\r\n rate,\r\n value\r\n )\r\n ) {\r\n selection.push(i)\r\n }\r\n }\r\n return selection\r\n }\r\n\r\n /**\r\n * Find gaps in the data\r\n * @param value The time value\r\n * @param unit The time unit (TimeUnit)\r\n * @param range If specified, the gaps will be found only within the range\r\n * @returns\r\n */\r\n private _findGaps(\r\n value: number,\r\n unit: TimeUnit,\r\n range?: [number, number]\r\n ): [number, number][] {\r\n const selection: [number, number][] = []\r\n const dataX = this.dataset.source.x\r\n let start = 0\r\n let end = dataX.length\r\n\r\n if (range?.[0] && range?.[1]) {\r\n start = range[0]\r\n end = range[1]\r\n }\r\n\r\n let prevDatetime = dataX[start]\r\n\r\n for (let i = start + 1; i <= end; i++) {\r\n const curr = dataX[i]\r\n const delta = curr - prevDatetime // milliseconds\r\n\r\n if (delta > value * timeUnitMultipliers[unit] * 1000) {\r\n selection.push([i - 1, i])\r\n }\r\n prevDatetime = curr\r\n }\r\n\r\n return selection\r\n }\r\n\r\n /**\r\n * Find points where the values are the same at least x times in a row\r\n * @param times The number of times in a row that points can be equal\r\n * @param range If specified, the points will be found only within the range\r\n * @returns\r\n */\r\n private _persistence(times: number, range?: [number, number]) {\r\n let selection: number[] = []\r\n let dataY = this.dataset.source.y\r\n let start = 0\r\n let end = dataY.length\r\n if (range?.[0] && range?.[1]) {\r\n start = range[0]\r\n end = range[1]\r\n }\r\n\r\n let prev = dataY[start]\r\n let stack = []\r\n\r\n for (let i = start + 1; i < end; i++) {\r\n const curr = dataY[i]\r\n if (curr != prev || i === end) {\r\n if (stack.length >= times) {\r\n selection = [...selection, ...stack]\r\n }\r\n stack = []\r\n } else {\r\n stack.push(i)\r\n }\r\n }\r\n\r\n return selection\r\n }\r\n}\r\n","import { ObservationRecord } from './utils/plotting/observationRecord';\r\n\r\nconst QcUtils = {\r\n install: (app: any) => {\r\n app.use(ObservationRecord)\r\n },\r\n};\r\n\r\nexport {\r\n QcUtils,\r\n ObservationRecord,\r\n};\r\n"],"names":["timeUnitMultipliers","TimeUnit","shiftDatetime","datetime","amount","unit","currentDate","measureEllapsedTime","fn","message","start","response","end","duration","findFirstGreaterOrEqual","array","target","low","high","mid","findLastLessOrEqual","FilterOperationFn","value","toCompare","RateOfChangeComparator","INCREASE_AMOUNT","components","ObservationRecord","dataArrays","measurement","length","newLength","dataArrayByteSizeX","maxByteLengthNeeded","outputBufferX","outputBufferY","outputArrayX","outputArrayY","index","newHistory","h","action","args","actions","editIcons","i","method","actionArgs","historyItem","e","filters","res","operator","operation","x","g","lowerIndex","upperIndex","xData","yData","lowerDatetime","lowerValue","upperDatetime","upperValue","collection","gap","fill","interpolateValues","range","numWorkers","workers","promises","resolve","worker","event","gaps","currentGap","leftDatetime","rightDatetime","fillPoints","fillDelta","nextFillDatetime","val","deleteIndices","segmentSize","segments","first","last","deleteSegment","prefixSum","startTarget","startDatetime","extent","groups","acc","curr","dataPoints","a","b","insertIndex","point","toInsert","left","right","n","appliedFilters","selection","key","comparator","dataY","prev","rate","dataX","prevDatetime","times","stack","QcUtils","app"],"mappings":"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"}
package/dist/index.umd.js DELETED
@@ -1,2 +0,0 @@
1
- (function(E,f){typeof exports=="object"&&typeof module<"u"?f(exports):typeof define=="function"&&define.amd?define(["exports"],f):(E=typeof globalThis<"u"?globalThis:E||self,f(E["@uwrl/qc-utils"]={}))})(this,function(E){"use strict";var f=typeof document<"u"?document.currentScript:null;const Y=1,b=Y*60,_=b*60,w=_*24,F=w*7,N=_*30,x=w*365,T={[y.SECOND]:Y,[y.MINUTE]:b,[y.HOUR]:_,[y.DAY]:w,[y.WEEK]:F,[y.MONTH]:N,[y.YEAR]:x},X=(o,t,a)=>{if(a===y.MONTH){const e=new Date(o);return e.setMonth(e.getMonth()+t),e.getTime()}else if(a===y.YEAR){const e=new Date(o);return e.setFullYear(e.getFullYear()+t),e.getTime()}else return o+t*T[a]*1e3},L=async(o,t)=>{const a=performance.now(),e=await o(),r=performance.now();console.log(` Done in ${(r-a).toFixed(2)} ms`);const s=+(r-a);return{response:e,duration:s}},I=(o,t)=>{let a=0,e=o.length;for(;a<e;){const r=a+e>>1;o[r]<t?a=r+1:e=r}return a},B=(o,t)=>{let a=0,e=o.length;for(;a<e;){const r=a+e>>1;o[r]>t?e=r:a=r+1}return a-1},M={"Less than":(o,t)=>o<t,"Less than or equal to":(o,t)=>o<=t,"Greater than":(o,t)=>o>t,"Greater than or equal to":(o,t)=>o>=t,Equal:(o,t)=>o==t,"Start datetime":(o,t)=>o==t,"End datetime":(o,t)=>o==t},O={"Less than":(o,t)=>o<t,"Less than or equal to":(o,t)=>o<=t,"Greater than":(o,t)=>o>t,"Greater than or equal to":(o,t)=>o>=t,Equal:(o,t)=>o==t};var y=(o=>(o.SECOND="s",o.MINUTE="m",o.HOUR="h",o.DAY="D",o.WEEK="W",o.MONTH="M",o.YEAR="Y",o))(y||{});const p=20*1e3,P=["date","value","qualifier"];class R{dataset={dimensions:P,source:{x:new Float64Array(new SharedArrayBuffer(p*Float64Array.BYTES_PER_ELEMENT,{maxByteLength:p*Float64Array.BYTES_PER_ELEMENT})),y:new Float32Array(new SharedArrayBuffer(p*Float32Array.BYTES_PER_ELEMENT,{maxByteLength:p*Float32Array.BYTES_PER_ELEMENT}))}};history=[];loadingTime=null;isLoading=!0;rawData;constructor(t){this.history=[],this.rawData=t,this.loadData(this.rawData)}async loadData(t){if(!t)return;this.isLoading=!0;const a=await L(()=>{this._growBuffer(t.datetimes.length),this._resizeTo(t.datetimes.length),this.dataX.set(t.datetimes),this.dataY.set(t.dataValues)});this.loadingTime=a.duration,this.history.length=0,this.isLoading=!1}get dataX(){return this.dataset.source.x}get dataY(){return this.dataset.source.y}_resizeTo(t){this.dataset.source.x=new Float64Array(this.dataset.source.x.buffer).subarray(0,t),this.dataset.source.y=new Float32Array(this.dataset.source.y.buffer).subarray(0,t)}_growBuffer(t){const a=t*Float64Array.BYTES_PER_ELEMENT;let e=this.dataX.buffer.byteLength;for(;a>e;)e+=p*Float64Array.BYTES_PER_ELEMENT;if(e*Float64Array.BYTES_PER_ELEMENT>this.dataX.buffer.maxByteLength){const r=new SharedArrayBuffer(this.dataX.buffer.byteLength,{maxByteLength:e*Float64Array.BYTES_PER_ELEMENT}),s=new SharedArrayBuffer(this.dataY.buffer.byteLength,{maxByteLength:e*Float32Array.BYTES_PER_ELEMENT}),n=new Float64Array(r),i=new Float32Array(s);n.set(this.dataX),i.set(this.dataY),this.dataset.source.x=n,this.dataset.source.y=i}this.dataX.buffer.byteLength<t*Float64Array.BYTES_PER_ELEMENT&&(this.dataX.buffer.grow(t*Float64Array.BYTES_PER_ELEMENT),this.dataY.buffer.grow(t*Float32Array.BYTES_PER_ELEMENT))}async reload(){this.loadingTime=null,this.isLoading=!0,this.history.length=0,await this.loadData(this.rawData)}async reloadHistory(t){const a=this.history.slice(0,t+1);await this.reload(),await this.dispatch(a.map(e=>[e.method,...e.args||[]]))}async removeHistoryItem(t){const a=[...this.history];a.splice(t,1),await this.reload(),await this.dispatch(a.map(e=>[e.method,...e.args||[]]))}get beginTime(){return this.dataset.source.x.length?new Date(this.dataset.source.x[0]):null}get endTime(){return this.dataset.source.x.length?new Date(this.dataset.source.x[this.dataset.source.x.length-1]):null}async dispatch(t,...a){const e={ADD_POINTS:this._addDataPoints,CHANGE_VALUES:this._changeValues,DELETE_POINTS:this._deleteDataPoints,DRIFT_CORRECTION:this._driftCorrection,INTERPOLATE:this._interpolate,SHIFT_DATETIMES:this._shift,FILL_GAPS:this._fillGaps},r={ADD_POINTS:"mdi-plus",CHANGE_VALUES:"mdi-pencil",DELETE_POINTS:"mdi-trash-can",DRIFT_CORRECTION:"mdi-chart-sankey",INTERPOLATE:"mdi-transit-connection-horizontal",SHIFT_DATETIMES:"mdi-calendar",FILL_GAPS:"mdi-keyboard-space"};let s=[];try{if(Array.isArray(t)){for(let n=0;n<t.length;n++){const i=t[n][0],h=t[n].slice(1,t[n].length),u={method:i,args:h,icon:r[i],isLoading:!1};this.history.push(u)}for(let n=this.history.length-t.length;n<this.history.length;n++){const i=this.history[n];i.isLoading=!0;const h=await L(async()=>await e[i.method].apply(this,i.args));i.duration=h.duration,i.isLoading=!1,s.push(h.response)}}else{const n={method:t,args:a,icon:r[t],isLoading:!0};this.history.push(n);const i=await L(async()=>await e[t].apply(this,a));s=i.response,n.duration=i.duration,n.isLoading=!1}}catch(n){console.log(`Failed to execute operation: ${t} with arguments: `,a),console.log(n)}return s}async dispatchFilter(t,...a){const e={FIND_GAPS:this._findGaps,VALUE_THRESHOLD:this._valueThreshold,PERSISTENCE:this._persistence,RATE_OF_CHANGE:this._rateOfChange};let r=[];try{if(Array.isArray(t))for(let s=0;s<t.length;s++){const n=t[s][0],i=t[s].slice(1,t[s].length),h=await e[n].apply(this,i);r.push(h)}else r=await e[t].apply(this,a)}catch(s){console.log(`Failed to execute filter operation: ${t} with arguments: `,a),console.log(s)}return r}_changeValues(t,a,e){const r=s=>{switch(a){case"ADD":return s+e;case"ASSIGN":return e;case"DIV":return s/e;case"MULT":return s*e;case"SUB":return s-e;default:return s}};t.forEach(s=>{this.dataset.source.y[s]=r(this.dataset.source.y[s])})}_interpolate(t){this._getConsecutiveGroups(t).forEach(e=>{const r=e[0],s=e[e.length-1];let n=Math.max(0,r-1),i=Math.min(this.dataset.source.y.length-1,s+1);const h=this.dataset.source.x,u=this.dataset.source.y;for(let c=0;c<e.length;c++)this.dataset.source.y[e[c]]=this._interpolateLinear(h[e[c]],h[n],u[n],h[i],u[i])})}_interpolateLinear(t,a,e,r,s){return e+(t-a)*(s-e)/(r-a)}async _shift(t,a,e){const r=t.map(s=>[X(this.dataX[s],a,e),this.dataY[s]]);await this._deleteDataPoints(t),await this._addDataPoints(r)}async _fillGapsV2(t,a,e,r){const s=navigator.hardwareConcurrency||1,n=[],i=[],h=this.dataX.length,u=new SharedArrayBuffer(this.dataX.buffer.byteLength,{maxByteLength:this.dataX.buffer.maxByteLength}),c=new SharedArrayBuffer(this.dataY.buffer.byteLength,{maxByteLength:this.dataY.buffer.maxByteLength});for(let l=0;l<s;l++)i.push(new Promise(d=>{const g=new Worker(new URL("/assets/fill-gaps.worker-BesEL5v2.js",typeof document>"u"&&typeof location>"u"?require("url").pathToFileURL(__filename).href:typeof document>"u"?location.href:f&&f.tagName.toUpperCase()==="SCRIPT"&&f.src||new URL("index.umd.js",document.baseURI).href));n.push(g),g.postMessage({bufferX:this.dataX.buffer,bufferY:this.dataY.buffer,outputBufferX:u,outputBufferY:c}),g.onmessage=m=>{d(m.data)}}));await Promise.all(i),n.forEach(l=>l.terminate()),this.dataset.source.x=new Float64Array(u),this.dataset.source.y=new Float32Array(c),this._resizeTo(h)}_fillGaps(t,a,e,r){const s=this._findGaps(t[0],t[1],r);for(let n=s.length-1;n>=0;n--){const i=s[n],h=this.dataX[i[0]],u=this.dataX[i[1]],c=[],l=a[0]*T[a[1]]*1e3;let d=h+l;for(;d<u;){const g=e?this._interpolateLinear(d,this.dataX[i[0]],this.dataY[i[0]],this.dataX[i[1]],this.dataY[i[1]]):-9999;c.push([d,g]),d+=l}this._addDataPoints(c)}}async _deleteDataPoints(t){const a=navigator.hardwareConcurrency||1,e=Math.ceil(this.dataX.length/a),r=[],s=[];for(let l=0;l<a;l++){const d=l*e,g=Math.min((l+1)*e-1,this.dataX.length-1),m=I(t,d),A=B(t,g),S=t.slice(m,A+1);s.push({start:d,end:g,deleteSegment:S})}const n=new Array(a).fill(0);for(let l=1;l<a;l++)n[l]=n[l-1]+s[l-1].deleteSegment.length;const i=[],h=this.dataX.length-t.length,u=new SharedArrayBuffer(this.dataX.buffer.byteLength,{maxByteLength:this.dataX.buffer.maxByteLength}),c=new SharedArrayBuffer(this.dataY.buffer.byteLength,{maxByteLength:this.dataY.buffer.maxByteLength});for(let l=0;l<a;l++){const{start:d,end:g,deleteSegment:m}=s[l],A=d-n[l];i.push(new Promise(S=>{const D=new Worker(new URL("/assets/delete-data.worker-Iw4O1ScI.js",typeof document>"u"&&typeof location>"u"?require("url").pathToFileURL(__filename).href:typeof document>"u"?location.href:f&&f.tagName.toUpperCase()==="SCRIPT"&&f.src||new URL("index.umd.js",document.baseURI).href));r.push(D),D.postMessage({bufferX:this.dataX.buffer,bufferY:this.dataY.buffer,outputBufferX:u,outputBufferY:c,start:d,end:g,deleteSegment:m,startTarget:A}),D.onmessage=G=>{S(G.data)}}))}await Promise.all(i),r.forEach(l=>l.terminate()),this.dataset.source.x=new Float64Array(u),this.dataset.source.y=new Float32Array(c),this._resizeTo(h)}_driftCorrection(t,a,e){const r=this.dataset.source.x,s=this.dataset.source.y,n=r[t],h=r[a]-n;for(let u=t;u<a;u++)this.dataset.source.y[u]=s[u]+e*((r[u]-n)/h)}_getConsecutiveGroups(t){const a=[[]];return t.reduce((e,r)=>{const s=e[e.length-1];return!s.length||r==s[s.length-1]+1?s.push(r):e.push([r]),e},a),a}async _addDataPoints(t){const a=this.dataX.length+t.length;this._growBuffer(a),t.sort((s,n)=>s[0]-n[0]);const e=t.map(s=>B(this.dataX,s[0])+1);this._resizeTo(a),e.push(this.dataX.length);let r=t.length;for(let s=e.length-1;s>0;s--){const n=e[s-1],i=e[s]-1;for(let h=i;h>=n;h--)this.dataX[h+r]=this.dataX[h],this.dataY[h+r]=this.dataY[h];r--,this.dataX[n+r]=t[s-1][0],this.dataY[n+r]=t[s-1][1]}}_valueThreshold(t){const a=[];return this.dataset.source.y.forEach((e,r)=>{Object.keys(t).some(s=>M[s]?.(e,t[s]))&&a.push(r)}),a}_rateOfChange(t,a){const e=[],r=this.dataset.source.y;for(let s=1;s<r.length;s++){const n=r[s-1],h=(r[s]-n)/Math.abs(n);O[t]?.(h,a)&&e.push(s)}return e}_findGaps(t,a,e){const r=[],s=this.dataset.source.x;let n=0,i=s.length;e?.[0]&&e?.[1]&&(n=e[0],i=e[1]);let h=s[n];for(let u=n+1;u<=i;u++){const c=s[u];c-h>t*T[a]*1e3&&r.push([u-1,u]),h=c}return r}_persistence(t,a){let e=[],r=this.dataset.source.y,s=0,n=r.length;a?.[0]&&a?.[1]&&(s=a[0],n=a[1]);let i=r[s],h=[];for(let u=s+1;u<n;u++)r[u]!=i||u===n?(h.length>=t&&(e=[...e,...h]),h=[]):h.push(u);return e}}const C={install:o=>{o.use(R)}};E.ObservationRecord=R,E.QcUtils=C,Object.defineProperty(E,Symbol.toStringTag,{value:"Module"})});
2
- //# sourceMappingURL=index.umd.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.umd.js","sources":["../src/utils/format.ts","../src/utils/ellapsedTime.ts","../src/utils/observationsUtils.ts","../src/utils/plotting/observationRecord.ts","../src/index.ts"],"sourcesContent":["import { EnumDictionary, TimeUnit } from \"./plotting/observationRecord\"\r\n\r\nconst SECOND = 1\r\nconst MINUTE = SECOND * 60\r\nconst HOUR = MINUTE * 60\r\nconst DAY = HOUR * 24\r\nconst WEEK = DAY * 7\r\nconst MONTH = HOUR * 30\r\nconst YEAR = DAY * 365\r\n\r\nexport const timeUnitMultipliers: EnumDictionary<TimeUnit, number> = {\r\n [TimeUnit.SECOND]: SECOND,\r\n [TimeUnit.MINUTE]: MINUTE,\r\n [TimeUnit.HOUR]: HOUR,\r\n [TimeUnit.DAY]: DAY,\r\n [TimeUnit.WEEK]: WEEK,\r\n [TimeUnit.MONTH]: MONTH,\r\n [TimeUnit.YEAR]: YEAR,\r\n}\r\n\r\nexport const formatDate = (date: Date) => {\r\n return date.toLocaleString(undefined, {\r\n year: 'numeric',\r\n month: 'short',\r\n day: '2-digit',\r\n hour: '2-digit',\r\n hour12: false,\r\n minute: '2-digit',\r\n second: '2-digit',\r\n })\r\n}\r\n\r\nexport const formatDuration = (duration: number) => {\r\n let value\r\n let unit\r\n\r\n if (duration >= MINUTE * 1000) {\r\n value = duration / (MINUTE * 1000)\r\n unit = 'm'\r\n } else if (duration >= 1000) {\r\n value = duration / 1000\r\n unit = 's'\r\n } else {\r\n value = duration\r\n unit = 'ms'\r\n }\r\n\r\n let formattedValue\r\n if (unit === 'ms') {\r\n formattedValue = Math.round(value).toString()\r\n } else {\r\n formattedValue = value.toFixed(2)\r\n }\r\n\r\n return `${formattedValue} ${unit}`\r\n}\r\n\r\nexport const shiftDatetime = (\r\n datetime: number,\r\n amount: number,\r\n unit: TimeUnit\r\n) => {\r\n if (unit === TimeUnit.MONTH) {\r\n const currentDate = new Date(datetime)\r\n currentDate.setMonth(currentDate.getMonth() + amount)\r\n return currentDate.getTime()\r\n } else if (unit === TimeUnit.YEAR) {\r\n const currentDate = new Date(datetime)\r\n currentDate.setFullYear(currentDate.getFullYear() + amount)\r\n return currentDate.getTime()\r\n } else {\r\n return datetime + amount * timeUnitMultipliers[unit] * 1000\r\n }\r\n}\r\n","export const measureEllapsedTime = async (\r\n fn: () => any,\r\n message?: string\r\n): Promise<{ response: any; duration: number }> => {\r\n if (message) {\r\n console.log(message)\r\n }\r\n const start = performance.now()\r\n const response = await fn()\r\n const end = performance.now()\r\n if (import.meta.env.MODE !== 'test') {\r\n console.log(`\\tDone in ${(end - start).toFixed(2)} ms`)\r\n }\r\n const duration = +(end - start)\r\n return { response, duration }\r\n}\r\n","\r\nexport function subtractHours(timestamp: string, hours: number): string {\r\n const date = new Date(timestamp)\r\n date.setHours(date.getHours() - hours)\r\n return date.toISOString()\r\n}\r\n\r\n/** Returns the index of the first value that is greater or equal to the target value */\r\nexport const findFirstGreaterOrEqual = (\r\n array: number[] | Float64Array<SharedArrayBuffer>,\r\n target: number\r\n) => {\r\n let low = 0,\r\n high = array.length\r\n while (low < high) {\r\n const mid = (low + high) >> 1\r\n if (array[mid] < target) low = mid + 1\r\n else high = mid\r\n }\r\n return low\r\n}\r\n\r\n/** Returns the index of the last value that is lesser or equal to the target value */\r\nexport const findLastLessOrEqual = (\r\n array: number[] | Float64Array<SharedArrayBuffer>,\r\n target: number\r\n) => {\r\n let low = 0,\r\n high = array.length\r\n while (low < high) {\r\n const mid = (low + high) >> 1\r\n if (array[mid] > target) high = mid\r\n else low = mid + 1\r\n }\r\n return low - 1\r\n}","import { shiftDatetime, timeUnitMultipliers } from '../format'\r\nimport { measureEllapsedTime } from '../ellapsedTime'\r\nimport { findFirstGreaterOrEqual, findLastLessOrEqual } from '../observationsUtils'\r\n\r\nexport type EnumDictionary<T extends string | symbol | number, U> = {\r\n [K in T]: U\r\n}\r\n\r\nexport enum FilterOperation {\r\n LT = 'Less than',\r\n LTE = 'Less than or equal to',\r\n GT = 'Greater than',\r\n GTE = 'Greater than or equal to',\r\n E = 'Equal',\r\n START = 'Start datetime',\r\n END = 'End datetime',\r\n}\r\n\r\nexport const FilterOperationFn: EnumDictionary<\r\n FilterOperation,\r\n (value: number, toCompare: number) => boolean\r\n> = {\r\n [FilterOperation.LT]: (value: number, toCompare: number) => {\r\n return value < toCompare\r\n },\r\n [FilterOperation.LTE]: (value: number, toCompare: number) => {\r\n return value <= toCompare\r\n },\r\n [FilterOperation.GT]: (value: number, toCompare: number) => {\r\n return value > toCompare\r\n },\r\n [FilterOperation.GTE]: (value: number, toCompare: number) => {\r\n return value >= toCompare\r\n },\r\n [FilterOperation.E]: (value: number, toCompare: number) => {\r\n return value == toCompare\r\n },\r\n [FilterOperation.START]: (value: number, toCompare: number) => {\r\n return value == toCompare\r\n },\r\n [FilterOperation.END]: (value: number, toCompare: number) => {\r\n return value == toCompare\r\n },\r\n}\r\n\r\nexport enum Operator {\r\n ADD = 'ADD',\r\n SUB = 'SUB',\r\n MULT = 'MULT',\r\n DIV = 'DIV',\r\n ASSIGN = 'ASSIGN',\r\n}\r\n\r\nexport enum RateOfChangeOperation {\r\n LT = 'Less than',\r\n LTE = 'Less than or equal to',\r\n GT = 'Greater than',\r\n GTE = 'Greater than or equal to',\r\n E = 'Equal',\r\n}\r\n\r\nexport const RateOfChangeComparator: EnumDictionary<\r\n RateOfChangeOperation,\r\n (value: number, toCompare: number) => boolean\r\n> = {\r\n [RateOfChangeOperation.LT]: (value: number, toCompare: number) => {\r\n return value < toCompare\r\n },\r\n [RateOfChangeOperation.LTE]: (value: number, toCompare: number) => {\r\n return value <= toCompare\r\n },\r\n [RateOfChangeOperation.GT]: (value: number, toCompare: number) => {\r\n return value > toCompare\r\n },\r\n [RateOfChangeOperation.GTE]: (value: number, toCompare: number) => {\r\n return value >= toCompare\r\n },\r\n [RateOfChangeOperation.E]: (value: number, toCompare: number) => {\r\n return value == toCompare\r\n },\r\n}\r\n\r\nexport enum TimeUnit {\r\n SECOND = 's',\r\n MINUTE = 'm',\r\n HOUR = 'h',\r\n DAY = 'D',\r\n WEEK = 'W',\r\n MONTH = 'M',\r\n YEAR = 'Y',\r\n}\r\n\r\nexport enum EnumEditOperations {\r\n ADD_POINTS = 'ADD_POINTS',\r\n CHANGE_VALUES = 'CHANGE_VALUES',\r\n DELETE_POINTS = 'DELETE_POINTS',\r\n DRIFT_CORRECTION = 'DRIFT_CORRECTION',\r\n INTERPOLATE = 'INTERPOLATE',\r\n SHIFT_DATETIMES = 'SHIFT_DATETIMES',\r\n FILL_GAPS = 'FILL_GAPS',\r\n}\r\n\r\nexport enum EnumFilterOperations {\r\n FIND_GAPS = 'FIND_GAPS',\r\n PERSISTENCE = 'PERSISTENCE',\r\n RATE_OF_CHANGE = 'RATE_OF_CHANGE',\r\n VALUE_THRESHOLD = 'VALUE_THRESHOLD',\r\n}\r\n\r\nexport type HistoryItem = {\r\n method: EnumEditOperations\r\n icon: string\r\n isLoading: boolean\r\n args?: any[]\r\n duration?: number\r\n status?: 'success' | 'failed'\r\n}\r\n\r\n/**\r\n * This number should approximate the number of observations that a dataset could increase by during a session.\r\n * The lower this number, the less memory the entire app uses.\r\n * Note that when a dataset number of data points increases by more than `INCREASE_AMOUNT`,\r\n * the `_growBuffer()` method will allocate a new buffer, and the data will be copied into it.\r\n */\r\nexport const INCREASE_AMOUNT = 20 * 1000\r\n\r\nconst components = ['date', 'value', 'qualifier'] // TODO: `qualifier` unused for now...\r\n\r\nexport class ObservationRecord {\r\n /** The generated dataset to be used for plotting */\r\n dataset: {\r\n dimensions: string[]\r\n source: {\r\n // Store datetimes in a Float64Array because plotly can't parse BigInts correctly.\r\n x: Float64Array<SharedArrayBuffer>\r\n y: Float32Array<SharedArrayBuffer>\r\n }\r\n } = {\r\n dimensions: components,\r\n source: {\r\n x: new Float64Array(\r\n new SharedArrayBuffer(\r\n INCREASE_AMOUNT * Float64Array.BYTES_PER_ELEMENT,\r\n {\r\n maxByteLength: INCREASE_AMOUNT * Float64Array.BYTES_PER_ELEMENT, // Max size the array can reach\r\n }\r\n )\r\n ),\r\n y: new Float32Array(\r\n new SharedArrayBuffer(\r\n INCREASE_AMOUNT * Float32Array.BYTES_PER_ELEMENT,\r\n {\r\n maxByteLength: INCREASE_AMOUNT * Float32Array.BYTES_PER_ELEMENT, // Max size the array can reach\r\n }\r\n )\r\n ),\r\n },\r\n }\r\n history: HistoryItem[] = []\r\n loadingTime: number | null = null\r\n isLoading: boolean = true\r\n rawData: {\r\n datetimes: Float64Array<ArrayBuffer> | number[]\r\n dataValues: Float32Array<ArrayBuffer> | number[]\r\n }\r\n\r\n constructor(dataArrays: {\r\n datetimes: Float64Array<ArrayBuffer> | number[]\r\n dataValues: Float32Array<ArrayBuffer> | number[]\r\n }) {\r\n this.history = []\r\n this.rawData = dataArrays\r\n this.loadData(this.rawData)\r\n }\r\n\r\n async loadData(dataArrays: {\r\n datetimes: Float64Array<ArrayBuffer> | number[]\r\n dataValues: Float32Array<ArrayBuffer> | number[]\r\n }) {\r\n if (!dataArrays) {\r\n return\r\n }\r\n this.isLoading = true\r\n const measurement = await measureEllapsedTime(() => {\r\n this._growBuffer(dataArrays.datetimes.length)\r\n this._resizeTo(dataArrays.datetimes.length)\r\n\r\n this.dataX.set(dataArrays.datetimes)\r\n this.dataY.set(dataArrays.dataValues)\r\n })\r\n\r\n this.loadingTime = measurement.duration\r\n\r\n this.history.length = 0\r\n this.isLoading = false\r\n }\r\n\r\n get dataX() {\r\n return this.dataset.source.x\r\n }\r\n\r\n get dataY() {\r\n return this.dataset.source.y\r\n }\r\n\r\n /**\r\n * Resizes the typed array\r\n * @param length The total number of elements that the view will contain\r\n */\r\n private _resizeTo(length: number) {\r\n // We need to resize the view to match our data length,\r\n // but TypedArrays using SharedArrayBuffer can't shrink.\r\n // Recreate the view to effectively resize it\r\n this.dataset.source.x = new Float64Array(\r\n this.dataset.source.x.buffer\r\n ).subarray(0, length)\r\n\r\n this.dataset.source.y = new Float32Array(\r\n this.dataset.source.y.buffer\r\n ).subarray(0, length)\r\n }\r\n\r\n /**\r\n * Buffer size is always in increments of `INCREASE_AMOUNT`.\r\n * Grows the buffer by `INCREASE_AMOUNT` in bytes if the current data doesn't fit\r\n * @param newLength The total number of elements that the view will contain\r\n */\r\n private _growBuffer(newLength: number) {\r\n const dataArrayByteSizeX = newLength * Float64Array.BYTES_PER_ELEMENT\r\n\r\n let maxByteLengthNeeded = this.dataX.buffer.byteLength\r\n while (dataArrayByteSizeX > maxByteLengthNeeded) {\r\n maxByteLengthNeeded += INCREASE_AMOUNT * Float64Array.BYTES_PER_ELEMENT\r\n }\r\n\r\n if (\r\n maxByteLengthNeeded * Float64Array.BYTES_PER_ELEMENT >\r\n this.dataX.buffer.maxByteLength\r\n ) {\r\n // More space is needed, beyond the maxByteLength initially set, to allocate the data. A new buffer needs to be allocated.\r\n const outputBufferX = new SharedArrayBuffer(\r\n this.dataX.buffer.byteLength,\r\n {\r\n maxByteLength: maxByteLengthNeeded * Float64Array.BYTES_PER_ELEMENT,\r\n }\r\n )\r\n\r\n const outputBufferY = new SharedArrayBuffer(\r\n this.dataY.buffer.byteLength,\r\n {\r\n maxByteLength: maxByteLengthNeeded * Float32Array.BYTES_PER_ELEMENT,\r\n }\r\n )\r\n\r\n const outputArrayX = new Float64Array(outputBufferX)\r\n const outputArrayY = new Float32Array(outputBufferY)\r\n outputArrayX.set(this.dataX)\r\n outputArrayY.set(this.dataY)\r\n\r\n // Swap to the new array and buffer\r\n this.dataset.source.x = outputArrayX\r\n this.dataset.source.y = outputArrayY\r\n }\r\n\r\n if (\r\n this.dataX.buffer.byteLength <\r\n newLength * Float64Array.BYTES_PER_ELEMENT\r\n ) {\r\n this.dataX.buffer.grow(newLength * Float64Array.BYTES_PER_ELEMENT)\r\n this.dataY.buffer.grow(newLength * Float32Array.BYTES_PER_ELEMENT)\r\n }\r\n }\r\n\r\n /**\r\n * Reloads the dataset with the raw data\r\n */\r\n async reload() {\r\n this.loadingTime = null\r\n this.isLoading = true\r\n this.history.length = 0\r\n await this.loadData(this.rawData)\r\n }\r\n\r\n /**\r\n * @param index\r\n * @returns\r\n */\r\n async reloadHistory(index: number) {\r\n const newHistory = this.history.slice(0, index + 1)\r\n await this.reload()\r\n\r\n await this.dispatch(newHistory.map((h) => [h.method, ...(h.args || [])]))\r\n return\r\n }\r\n\r\n /**\r\n * Remove a history item\r\n * @param index\r\n */\r\n async removeHistoryItem(index: number) {\r\n const newHistory = [...this.history]\r\n newHistory.splice(index, 1)\r\n await this.reload()\r\n await this.dispatch(newHistory.map((h) => [h.method, ...(h.args || [])]))\r\n }\r\n\r\n get beginTime(): Date | null {\r\n if (!this.dataset.source.x.length) {\r\n return null\r\n }\r\n return new Date(this.dataset.source.x[0])\r\n }\r\n\r\n get endTime(): Date | null {\r\n if (!this.dataset.source.x.length) {\r\n return null\r\n }\r\n return new Date(this.dataset.source.x[this.dataset.source.x.length - 1])\r\n }\r\n\r\n /** Dispatch an operation and log its signature in hisotry */\r\n async dispatch(\r\n action: EnumEditOperations | [EnumEditOperations, ...any][],\r\n ...args: any\r\n ) {\r\n const actions: EnumDictionary<EnumEditOperations, Function> = {\r\n [EnumEditOperations.ADD_POINTS]: this._addDataPoints,\r\n [EnumEditOperations.CHANGE_VALUES]: this._changeValues,\r\n [EnumEditOperations.DELETE_POINTS]: this._deleteDataPoints,\r\n [EnumEditOperations.DRIFT_CORRECTION]: this._driftCorrection,\r\n [EnumEditOperations.INTERPOLATE]: this._interpolate,\r\n [EnumEditOperations.SHIFT_DATETIMES]: this._shift,\r\n [EnumEditOperations.FILL_GAPS]: this._fillGaps,\r\n }\r\n\r\n // TODO: consolidate with icons in EditDrawer component\r\n const editIcons: EnumDictionary<EnumEditOperations, string> = {\r\n [EnumEditOperations.ADD_POINTS]: 'mdi-plus',\r\n [EnumEditOperations.CHANGE_VALUES]: 'mdi-pencil',\r\n [EnumEditOperations.DELETE_POINTS]: 'mdi-trash-can',\r\n [EnumEditOperations.DRIFT_CORRECTION]: 'mdi-chart-sankey',\r\n [EnumEditOperations.INTERPOLATE]: 'mdi-transit-connection-horizontal',\r\n [EnumEditOperations.SHIFT_DATETIMES]: 'mdi-calendar',\r\n [EnumEditOperations.FILL_GAPS]: 'mdi-keyboard-space',\r\n }\r\n\r\n let response: any[] = []\r\n\r\n try {\r\n if (Array.isArray(action)) {\r\n for (let i = 0; i < action.length; i++) {\r\n const method = action[i][0]\r\n const actionArgs = action[i].slice(1, action[i].length)\r\n const historyItem: HistoryItem = {\r\n method,\r\n args: actionArgs,\r\n icon: editIcons[method],\r\n isLoading: false,\r\n }\r\n this.history.push(historyItem)\r\n }\r\n\r\n for (\r\n let i = this.history.length - action.length;\r\n i < this.history.length;\r\n i++\r\n ) {\r\n const historyItem = this.history[i]\r\n historyItem.isLoading = true\r\n\r\n const measurement = await measureEllapsedTime(async () => {\r\n return await actions[historyItem.method].apply(\r\n this,\r\n historyItem.args\r\n )\r\n })\r\n historyItem.duration = measurement.duration\r\n historyItem.isLoading = false\r\n response.push(measurement.response)\r\n }\r\n } else {\r\n const historyItem: HistoryItem = {\r\n method: action,\r\n args,\r\n icon: editIcons[action],\r\n isLoading: true,\r\n }\r\n this.history.push(historyItem)\r\n const measurement = await measureEllapsedTime(async () => {\r\n return await actions[action].apply(this, args)\r\n })\r\n response = measurement.response\r\n historyItem.duration = measurement.duration\r\n historyItem.isLoading = false\r\n }\r\n } catch (e) {\r\n console.log(\r\n `Failed to execute operation: ${action} with arguments: `,\r\n args\r\n )\r\n console.log(e)\r\n }\r\n\r\n return response\r\n }\r\n\r\n /** Filter operations do not transform the data and are not logged in history */\r\n async dispatchFilter(\r\n action: EnumFilterOperations | [EnumFilterOperations, ...any][],\r\n ...args: any\r\n ) {\r\n const filters: EnumDictionary<EnumFilterOperations, Function> = {\r\n [EnumFilterOperations.FIND_GAPS]: this._findGaps,\r\n [EnumFilterOperations.VALUE_THRESHOLD]: this._valueThreshold,\r\n [EnumFilterOperations.PERSISTENCE]: this._persistence,\r\n [EnumFilterOperations.RATE_OF_CHANGE]: this._rateOfChange,\r\n }\r\n let response = []\r\n\r\n try {\r\n if (Array.isArray(action)) {\r\n for (let i = 0; i < action.length; i++) {\r\n const method = action[i][0]\r\n const args = action[i].slice(1, action[i].length)\r\n const res = await filters[method].apply(this, args)\r\n response.push(res)\r\n }\r\n } else {\r\n response = await filters[action].apply(this, args)\r\n }\r\n } catch (e) {\r\n console.log(\r\n `Failed to execute filter operation: ${action} with arguments: `,\r\n args\r\n )\r\n console.log(e)\r\n }\r\n return response\r\n }\r\n\r\n /**\r\n * @param index An array containing the list of index of values to perform the operations on.\r\n * @param operator The operator that will be applied\r\n * @param value The value to use in the operation\r\n * @returns The modified DataFrame\r\n */\r\n private _changeValues(index: number[], operator: Operator, value: number) {\r\n const operation = (x: number) => {\r\n switch (operator) {\r\n case Operator.ADD:\r\n return x + value\r\n case Operator.ASSIGN:\r\n return value\r\n case Operator.DIV:\r\n return x / value\r\n case Operator.MULT:\r\n return x * value\r\n case Operator.SUB:\r\n return x - value\r\n default:\r\n return x\r\n }\r\n }\r\n\r\n index.forEach((index: number) => {\r\n this.dataset.source.y[index] = operation(this.dataset.source.y[index])\r\n })\r\n }\r\n\r\n private _interpolate(index: number[]) {\r\n const groups = this._getConsecutiveGroups(index)\r\n\r\n groups.forEach((g) => {\r\n const start = g[0]\r\n const end = g[g.length - 1]\r\n\r\n let lowerIndex = Math.max(0, start - 1)\r\n let upperIndex = Math.min(this.dataset.source.y.length - 1, end + 1)\r\n\r\n const xData = this.dataset.source.x\r\n const yData = this.dataset.source.y\r\n for (let i = 0; i < g.length; i++) {\r\n this.dataset.source.y[g[i]] = this._interpolateLinear(\r\n xData[g[i]],\r\n xData[lowerIndex],\r\n yData[lowerIndex],\r\n xData[upperIndex],\r\n yData[upperIndex]\r\n )\r\n }\r\n })\r\n }\r\n\r\n /** Interpolate existing values in the data source */\r\n private _interpolateLinear(\r\n datetime: number,\r\n lowerDatetime: number,\r\n lowerValue: number,\r\n upperDatetime: number,\r\n upperValue: number\r\n ) {\r\n const interpolatedValue =\r\n lowerValue +\r\n ((datetime - lowerDatetime) * (upperValue - lowerValue)) /\r\n (upperDatetime - lowerDatetime)\r\n\r\n return interpolatedValue\r\n }\r\n\r\n /**\r\n * Shifts the selected indexes by specified amount of units. Elements are reinserted according to their datetime.\r\n * @param index The index of the elements to shift\r\n * @param amount Number of {@link TimeUnit}\r\n * @param unit {@link TimeUnit}\r\n * @returns\r\n */\r\n private async _shift(index: number[], amount: number, unit: TimeUnit) {\r\n // Collection that will be re-added using `_addDataPoints`\r\n const collection: [number, number][] = index.map((i) => [\r\n shiftDatetime(this.dataX[i], amount, unit),\r\n this.dataY[i],\r\n ])\r\n // TODO: add dedicated method to do these in one go\r\n await this._deleteDataPoints(index)\r\n await this._addDataPoints(collection)\r\n }\r\n\r\n private async _fillGapsV2(\r\n gap: [number, TimeUnit],\r\n fill: [number, TimeUnit],\r\n interpolateValues: boolean,\r\n range?: [number, number]\r\n ) {\r\n const numWorkers = navigator.hardwareConcurrency || 1\r\n const workers: Worker[] = []\r\n const promises = []\r\n const newLength = this.dataX.length\r\n\r\n // To avoid workers reading from a memory address where another working is writing to, we use separate output buffers.\r\n const outputBufferX = new SharedArrayBuffer(this.dataX.buffer.byteLength, {\r\n maxByteLength: this.dataX.buffer.maxByteLength,\r\n })\r\n\r\n const outputBufferY = new SharedArrayBuffer(this.dataY.buffer.byteLength, {\r\n maxByteLength: this.dataY.buffer.maxByteLength,\r\n })\r\n\r\n // Compute startTarget for each segment and start workers\r\n for (let i = 0; i < numWorkers; i++) {\r\n // Spawn workers\r\n promises.push(\r\n new Promise((resolve) => {\r\n const worker = new Worker(\r\n new URL('fill-gaps.worker.ts', import.meta.url)\r\n )\r\n workers.push(worker)\r\n worker.postMessage({\r\n bufferX: this.dataX.buffer,\r\n bufferY: this.dataY.buffer,\r\n outputBufferX,\r\n outputBufferY,\r\n })\r\n worker.onmessage = (event: MessageEvent) => {\r\n resolve(event.data)\r\n }\r\n })\r\n )\r\n }\r\n\r\n await Promise.all(promises)\r\n\r\n workers.forEach((worker) => worker.terminate()) // Important to terminate the workers\r\n\r\n this.dataset.source.x = new Float64Array(outputBufferX)\r\n this.dataset.source.y = new Float32Array(outputBufferY)\r\n this._resizeTo(newLength)\r\n }\r\n\r\n /**\r\n * Find gaps and fill them with placeholder value\r\n * @param gap Intervals to detect as gaps\r\n * @param fill Interval used to fill the detected gaps\r\n * @param interpolateValues If true, the new values will be linearly interpolated\r\n * @returns\r\n */\r\n // TODO: this needs to be improved using web workers\r\n private _fillGaps(\r\n gap: [number, TimeUnit],\r\n fill: [number, TimeUnit],\r\n interpolateValues: boolean,\r\n range?: [number, number]\r\n ) {\r\n const gaps = this._findGaps(gap[0], gap[1], range)\r\n\r\n for (let i = gaps.length - 1; i >= 0; i--) {\r\n const currentGap = gaps[i]\r\n const leftDatetime = this.dataX[currentGap[0]]\r\n const rightDatetime = this.dataX[currentGap[1]]\r\n const fillPoints: [number, number][] = []\r\n\r\n // TODO: number of seconds in a year or month is not constant\r\n // Use setMonth and setFullYear instead\r\n const fillDelta = fill[0] * timeUnitMultipliers[fill[1]] * 1000\r\n let nextFillDatetime = leftDatetime + fillDelta\r\n\r\n while (nextFillDatetime < rightDatetime) {\r\n const val: number = interpolateValues\r\n ? this._interpolateLinear(\r\n nextFillDatetime,\r\n this.dataX[currentGap[0]],\r\n this.dataY[currentGap[0]],\r\n this.dataX[currentGap[1]],\r\n this.dataY[currentGap[1]]\r\n )\r\n : -9999\r\n\r\n fillPoints.push([nextFillDatetime, val])\r\n nextFillDatetime += fillDelta\r\n }\r\n\r\n this._addDataPoints(fillPoints)\r\n }\r\n }\r\n\r\n /**\r\n Deletes data points from a large array using worker threads.\r\n 1. The main thread divides the original array into equal parts to distribute work among workers.\r\n 2. For each segment, binary search locates the indexes to delete (deleteSegment), ensuring efficient lookups.\r\n 3. The cumulative deletions before each segment help compute the starting index (startTarget) for each worker's output, ensuring no overlap.\r\n 4. Each worker processes its segment linearly, skipping deletions and copying kept elements to their computed positions.\r\n * @param deleteIndices \r\n */\r\n // TODO: implement similar multithread solutions for other operations\r\n private async _deleteDataPoints(deleteIndices: number[]) {\r\n const numWorkers = navigator.hardwareConcurrency || 1\r\n const segmentSize = Math.ceil(this.dataX.length / numWorkers)\r\n const workers: Worker[] = []\r\n const segments = []\r\n\r\n // Prepare segments\r\n for (let i = 0; i < numWorkers; i++) {\r\n const start = i * segmentSize\r\n const end = Math.min((i + 1) * segmentSize - 1, this.dataX.length - 1)\r\n\r\n // Binary search to find deleteSegment within [start, end]\r\n const first = findFirstGreaterOrEqual(deleteIndices, start)\r\n const last = findLastLessOrEqual(deleteIndices, end)\r\n const deleteSegment = deleteIndices.slice(first, last + 1)\r\n\r\n segments.push({ start, end, deleteSegment })\r\n }\r\n\r\n // Compute prefix sums. These help distribute the work evenly.\r\n const prefixSum = new Array(numWorkers).fill(0)\r\n for (let i = 1; i < numWorkers; i++) {\r\n prefixSum[i] = prefixSum[i - 1] + segments[i - 1].deleteSegment.length\r\n }\r\n\r\n const promises = []\r\n const newLength = this.dataX.length - deleteIndices.length\r\n\r\n // // To avoid workers reading from a memory address where another working is writing to, we use separate output buffers.\r\n const outputBufferX = new SharedArrayBuffer(this.dataX.buffer.byteLength, {\r\n maxByteLength: this.dataX.buffer.maxByteLength,\r\n })\r\n\r\n const outputBufferY = new SharedArrayBuffer(this.dataY.buffer.byteLength, {\r\n maxByteLength: this.dataY.buffer.maxByteLength,\r\n })\r\n\r\n // Compute startTarget for each segment and start workers\r\n for (let i = 0; i < numWorkers; i++) {\r\n const { start, end, deleteSegment } = segments[i]\r\n const startTarget = start - prefixSum[i]\r\n\r\n // Spawn workers\r\n promises.push(\r\n new Promise((resolve) => {\r\n const worker = new Worker(\r\n new URL('delete-data.worker.ts', import.meta.url)\r\n )\r\n workers.push(worker)\r\n worker.postMessage({\r\n bufferX: this.dataX.buffer,\r\n bufferY: this.dataY.buffer,\r\n outputBufferX,\r\n outputBufferY,\r\n start,\r\n end,\r\n deleteSegment,\r\n startTarget,\r\n })\r\n worker.onmessage = (event: MessageEvent) => {\r\n resolve(event.data)\r\n }\r\n })\r\n )\r\n }\r\n\r\n // Prevents vitest from halting during execution of multiple promises\r\n if (import.meta.env.MODE !== 'test') {\r\n await Promise.all(promises)\r\n }\r\n\r\n workers.forEach((worker) => worker.terminate()) // Important to terminate the workers\r\n\r\n this.dataset.source.x = new Float64Array(outputBufferX)\r\n this.dataset.source.y = new Float32Array(outputBufferY)\r\n this._resizeTo(newLength)\r\n }\r\n\r\n /**\r\n *\r\n * @param start The start index\r\n * @param end The end index\r\n * @param value The drift amount\r\n */\r\n private _driftCorrection(start: number, end: number, value: number) {\r\n const xData = this.dataset.source.x\r\n const yData = this.dataset.source.y\r\n\r\n const startDatetime = xData[start]\r\n const endDatetime = xData[end]\r\n const extent = endDatetime - startDatetime\r\n\r\n for (let i = start; i < end; i++) {\r\n // y_n = y_0 + G(x_i / extent)\r\n this.dataset.source.y[i] =\r\n yData[i] + value * ((xData[i] - startDatetime) / extent)\r\n }\r\n }\r\n\r\n /** Traverses the index array and returns groups of consecutive values.\r\n * i.e.: `[0, 1, 3, 4, 6] => [[0, 1], [3, 4], [6]]`\r\n * Assumes the input array is sorted.\r\n * @param index: the index array (sorted)\r\n */\r\n private _getConsecutiveGroups(index: number[]): number[][] {\r\n const groups: number[][] = [[]]\r\n\r\n // Form groups of consecutive points to delete in order to minimize the number of splice operations\r\n index.reduce((acc: number[][], curr: number) => {\r\n const target: number[] = acc[acc.length - 1]\r\n\r\n if (!target.length || curr == target[target.length - 1] + 1) {\r\n target.push(curr)\r\n } else {\r\n acc.push([curr])\r\n }\r\n\r\n return acc\r\n }, groups)\r\n\r\n return groups\r\n }\r\n\r\n /**\r\n * Adds data points. Their insert index is determined using `findFirstGreaterOrEqual` in the x-axis.\r\n * @param dataPoints\r\n */\r\n private async _addDataPoints(dataPoints: [number, number][]) {\r\n // Check if more space is needed\r\n const newLength = this.dataX.length + dataPoints.length\r\n this._growBuffer(newLength)\r\n // Sort the datapoints by datetime in reverse order\r\n dataPoints.sort((a, b) => {\r\n return a[0] - b[0]\r\n })\r\n\r\n const insertIndex = dataPoints.map((point) => {\r\n return findLastLessOrEqual(this.dataX, point[0]) + 1\r\n })\r\n\r\n this._resizeTo(newLength) // The space needs to be allocated before insertion can happen\r\n\r\n insertIndex.push(this.dataX.length)\r\n\r\n // Shift elements to the right to make room for the items to insert\r\n let toInsert = dataPoints.length\r\n for (let i = insertIndex.length - 1; i > 0; i--) {\r\n const left = insertIndex[i - 1]\r\n const right = insertIndex[i] - 1\r\n\r\n for (let n = right; n >= left; n--) {\r\n this.dataX[n + toInsert] = this.dataX[n]\r\n this.dataY[n + toInsert] = this.dataY[n]\r\n }\r\n toInsert--\r\n this.dataX[left + toInsert] = dataPoints[i - 1][0]\r\n this.dataY[left + toInsert] = dataPoints[i - 1][1]\r\n }\r\n }\r\n\r\n // =======================\r\n // FILTER OPERATIONS\r\n // =======================\r\n\r\n /**\r\n * Filter by applying a set of logical operations\r\n * @param appliedFilters\r\n * @returns\r\n */\r\n private _valueThreshold(appliedFilters: { [key: string]: number }) {\r\n const selection: number[] = []\r\n\r\n this.dataset.source.y.forEach((value: number, index: number) => {\r\n if (\r\n Object.keys(appliedFilters).some((key) => {\r\n return FilterOperationFn[key as FilterOperation]?.(\r\n value,\r\n appliedFilters[key]\r\n )\r\n })\r\n ) {\r\n selection.push(index)\r\n }\r\n })\r\n\r\n return selection\r\n }\r\n\r\n /**\r\n *\r\n * @param comparator\r\n * @param value\r\n * @returns\r\n */\r\n private _rateOfChange(comparator: string, value: number) {\r\n const selection: number[] = []\r\n const dataY = this.dataset.source.y\r\n\r\n for (let i = 0 + 1; i < dataY.length; i++) {\r\n const prev = dataY[i - 1]\r\n const curr = dataY[i]\r\n const rate = (curr - prev) / Math.abs(prev)\r\n\r\n if (\r\n RateOfChangeComparator[comparator as RateOfChangeOperation]?.(\r\n rate,\r\n value\r\n )\r\n ) {\r\n selection.push(i)\r\n }\r\n }\r\n return selection\r\n }\r\n\r\n /**\r\n * Find gaps in the data\r\n * @param value The time value\r\n * @param unit The time unit (TimeUnit)\r\n * @param range If specified, the gaps will be found only within the range\r\n * @returns\r\n */\r\n private _findGaps(\r\n value: number,\r\n unit: TimeUnit,\r\n range?: [number, number]\r\n ): [number, number][] {\r\n const selection: [number, number][] = []\r\n const dataX = this.dataset.source.x\r\n let start = 0\r\n let end = dataX.length\r\n\r\n if (range?.[0] && range?.[1]) {\r\n start = range[0]\r\n end = range[1]\r\n }\r\n\r\n let prevDatetime = dataX[start]\r\n\r\n for (let i = start + 1; i <= end; i++) {\r\n const curr = dataX[i]\r\n const delta = curr - prevDatetime // milliseconds\r\n\r\n if (delta > value * timeUnitMultipliers[unit] * 1000) {\r\n selection.push([i - 1, i])\r\n }\r\n prevDatetime = curr\r\n }\r\n\r\n return selection\r\n }\r\n\r\n /**\r\n * Find points where the values are the same at least x times in a row\r\n * @param times The number of times in a row that points can be equal\r\n * @param range If specified, the points will be found only within the range\r\n * @returns\r\n */\r\n private _persistence(times: number, range?: [number, number]) {\r\n let selection: number[] = []\r\n let dataY = this.dataset.source.y\r\n let start = 0\r\n let end = dataY.length\r\n if (range?.[0] && range?.[1]) {\r\n start = range[0]\r\n end = range[1]\r\n }\r\n\r\n let prev = dataY[start]\r\n let stack = []\r\n\r\n for (let i = start + 1; i < end; i++) {\r\n const curr = dataY[i]\r\n if (curr != prev || i === end) {\r\n if (stack.length >= times) {\r\n selection = [...selection, ...stack]\r\n }\r\n stack = []\r\n } else {\r\n stack.push(i)\r\n }\r\n }\r\n\r\n return selection\r\n }\r\n}\r\n","import { ObservationRecord } from './utils/plotting/observationRecord';\r\n\r\nconst QcUtils = {\r\n install: (app: any) => {\r\n app.use(ObservationRecord)\r\n },\r\n};\r\n\r\nexport {\r\n QcUtils,\r\n ObservationRecord,\r\n};\r\n"],"names":["SECOND","MINUTE","HOUR","DAY","WEEK","MONTH","YEAR","timeUnitMultipliers","TimeUnit","shiftDatetime","datetime","amount","unit","currentDate","measureEllapsedTime","fn","message","start","response","end","duration","findFirstGreaterOrEqual","array","target","low","high","mid","findLastLessOrEqual","FilterOperationFn","value","toCompare","RateOfChangeComparator","INCREASE_AMOUNT","components","ObservationRecord","dataArrays","measurement","length","newLength","dataArrayByteSizeX","maxByteLengthNeeded","outputBufferX","outputBufferY","outputArrayX","outputArrayY","index","newHistory","h","action","args","actions","editIcons","i","method","actionArgs","historyItem","e","filters","res","operator","operation","x","g","lowerIndex","upperIndex","xData","yData","lowerDatetime","lowerValue","upperDatetime","upperValue","collection","gap","fill","interpolateValues","range","numWorkers","workers","promises","resolve","worker","_documentCurrentScript","event","gaps","currentGap","leftDatetime","rightDatetime","fillPoints","fillDelta","nextFillDatetime","val","deleteIndices","segmentSize","segments","first","last","deleteSegment","prefixSum","startTarget","startDatetime","extent","groups","acc","curr","dataPoints","a","b","insertIndex","point","toInsert","left","right","n","appliedFilters","selection","key","comparator","dataY","prev","rate","dataX","prevDatetime","times","stack","QcUtils","app"],"mappings":"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"}