@uwrl/qc-utils 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/delete-data.worker-Iw4O1ScI.js +0 -1
- package/dist/assets/fill-gaps.worker-BesEL5v2.js +0 -1
- package/dist/{index.mjs → index.js} +1 -8
- package/dist/{index.umd.js → index.umd.cjs} +1 -2
- package/package.json +6 -6
- package/dist/assets/delete-data.worker-Iw4O1ScI.js.map +0 -1
- package/dist/assets/fill-gaps.worker-BesEL5v2.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/index.umd.js.map +0 -1
|
@@ -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
|
-
(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
|
+
(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,R=w*7,F=_*30,N=w*365,T={[y.SECOND]:Y,[y.MINUTE]:b,[y.HOUR]:_,[y.DAY]:w,[y.WEEK]:R,[y.MONTH]:F,[y.YEAR]:N},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}},X=(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},I={"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},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};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,O=["date","value","qualifier"];class P{dataset={dimensions:O,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.cjs",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=X(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.cjs",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=C=>{S(C.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=>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);M[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}}E.ObservationRecord=P,Object.defineProperty(E,Symbol.toStringTag,{value:"Module"})});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uwrl/qc-utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "Quality Control Utilities",
|
|
5
5
|
"homepage": "https://github.com/hydroserver2/qc-utils#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
},
|
|
13
13
|
"license": "ISC",
|
|
14
14
|
"author": "Maurier Ramirez",
|
|
15
|
-
"type": "
|
|
16
|
-
"main": "./dist/index.
|
|
17
|
-
"module": "./dist/index.
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/index.cjs",
|
|
17
|
+
"module": "./dist/index.js",
|
|
18
18
|
"exports": {
|
|
19
19
|
".": {
|
|
20
|
-
"require": "./dist/index.
|
|
21
|
-
"import": "./dist/index.
|
|
20
|
+
"require": "./dist/index.cjs",
|
|
21
|
+
"import": "./dist/index.js"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
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"}
|
package/dist/index.mjs.map
DELETED
|
@@ -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.map
DELETED
|
@@ -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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"}
|