UNPKG

@iotize/ionic

Version:

Iotize specific building blocks on top of @ionic/angular.

834 lines (826 loc) 440 kB
import * as i0 from '@angular/core'; import { Injectable, Component, Input, NgModule, EventEmitter, Directive, Output, HostListener, PLATFORM_ID, ChangeDetectionStrategy, Inject, ViewChild, TemplateRef, ViewEncapsulation, ContentChild, ElementRef, NgZone, ChangeDetectorRef, Pipe } from '@angular/core'; import { BehaviorSubject, Observable, Subject, NEVER, combineLatest } from 'rxjs'; import * as i1 from '@angular/forms'; import { ReactiveFormsModule, FormControl, Validators, FormsModule } from '@angular/forms'; import * as i1$1 from '@ionic/angular'; import { IonicModule, ModalController, AlertController, ActionSheetController, Platform } from '@ionic/angular'; import * as i1$3 from '@iotize/ionic'; import { getFormFieldOrError, ExportToCsv, LibError, PendingCallManager, TapValueEditorContainerModule, InlineEditorModule, CurrentDeviceService, getAbstractVariableOrError, LibCommonModule } from '@iotize/ionic'; import * as i2 from '@angular/common'; import { CommonModule, DecimalPipe } from '@angular/common'; import * as i3 from '@swimlane/ngx-charts'; import { BaseChartComponent, LegendPosition, NgxChartsModule, GaugeComponent, HeatMapComponent, LineChartComponent, NumberCardComponent, PieChartComponent, PolarChartComponent, PieChartModule } from '@swimlane/ngx-charts'; import { takeUntil, switchMap, shareReplay, map } from 'rxjs/operators'; import * as i1$2 from '@awesome-cordova-plugins/file/ngx'; import { File } from '@awesome-cordova-plugins/file/ngx'; import { createDebugger } from '@iotize/common/debug'; import * as i3$1 from '@iotize/app-common'; import { IconModule } from '@iotize/app-common'; import { variableDataTypeToByteSize, TapVariable } from '@iotize/tap/data'; import * as i2$1 from '@angular/material/table'; import { MatTableModule } from '@angular/material/table'; import * as i3$2 from '@angular/cdk/table'; import { DataSource, CdkTableModule } from '@angular/cdk/table'; import { __decorate, __metadata } from 'tslib'; import { deepCopy, listEnumKeys } from '@iotize/common/utility'; import { TapStreamReader, NumberConverter, TapStreamWriter } from '@iotize/tap/client/impl'; import { VariableType } from '@iotize/tap/service/impl/variable'; import { GPIOMode } from '@iotize/tap/service/impl/gpio'; import * as shape from 'd3-shape'; import * as i3$3 from '@swimlane/ngx-datatable'; import { ColumnMode, NgxDatatableModule } from '@swimlane/ngx-datatable'; import * as i1$4 from '@angular/router'; import { bufferToHexString } from '@iotize/common/byte-converter'; class MonitoringSettingsService { constructor() { this.subject = new BehaviorSubject({ period: 1000, }); } set settings(value) { this.subject.next(value); } get settings() { return this.subject.value; } get settings$() { return this.subject; } } /** @nocollapse */ MonitoringSettingsService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MonitoringSettingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ MonitoringSettingsService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MonitoringSettingsService, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MonitoringSettingsService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }], ctorParameters: function () { return []; } }); class TapMonitoringSettingsComponent { get period() { return getFormFieldOrError(this.form, 'period'); } get dataSource() { return getFormFieldOrError(this.form, 'dataSource'); } constructor(formBuilder, monitoringSettingsService, modalController) { this.formBuilder = formBuilder; this.monitoringSettingsService = monitoringSettingsService; this.modalController = modalController; this.showDataSource = false; this.form = this.formBuilder.group({ period: [this.monitoringSettingsService.settings.period], dataSource: ['live'], }); } ngOnInit() { this.form.valueChanges.subscribe(() => { this.monitoringSettingsService.settings = { period: this.period.value, }; }); } async dismiss() { const topOverlay = await this.modalController.getTop(); if (topOverlay) { await this.modalController.dismiss(topOverlay.id); } } } /** @nocollapse */ TapMonitoringSettingsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TapMonitoringSettingsComponent, deps: [{ token: i1.FormBuilder }, { token: MonitoringSettingsService }, { token: i1$1.ModalController }], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ TapMonitoringSettingsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: TapMonitoringSettingsComponent, selector: "tap-monitoring-settings", inputs: { showDataSource: "showDataSource" }, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-button (click)=\"dismiss()\">\n <ion-icon name=\"arrow-back\"></ion-icon>\n </ion-button>\n </ion-buttons>\n <ion-title class=\"text-center\">\n Monitoring Settings\n </ion-title>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <form [formGroup]=\"form\">\n <ion-list>\n <ion-item>\n <ion-label>Frequency (in milliseconds)</ion-label>\n <ion-input\n [formControlName]=\"'period'\"\n slot=\"end\"\n text-right\n type=\"number\"\n ></ion-input>\n </ion-item>\n <ion-item *ngIf=\"showDataSource\">\n <ion-label>Data source</ion-label>\n <ion-select [formControlName]=\"'dataSource'\" slot=\"end\">\n <ion-select-option value=\"live\">Live data</ion-select-option>\n <ion-select-option value=\"datalog\">Data log</ion-select-option>\n </ion-select>\n </ion-item>\n </ion-list>\n </form>\n</ion-content>\n<ion-footer>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"dismiss()\">Ok</ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-footer>\n", styles: [""], dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i1$1.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i1$1.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i1$1.IonContent, selector: "ion-content", inputs: ["color", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i1$1.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i1$1.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i1$1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i1$1.IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: i1$1.IonItem, selector: "ion-item", inputs: ["button", "color", "counter", "counterFormatter", "detail", "detailIcon", "disabled", "download", "fill", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "shape", "target", "type"] }, { kind: "component", type: i1$1.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i1$1.IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: i1$1.IonSelect, selector: "ion-select", inputs: ["cancelText", "compareWith", "disabled", "interface", "interfaceOptions", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "value"] }, { kind: "component", type: i1$1.IonSelectOption, selector: "ion-select-option", inputs: ["disabled", "value"] }, { kind: "component", type: i1$1.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i1$1.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "directive", type: i1$1.NumericValueAccessor, selector: "ion-input[type=number]" }, { kind: "directive", type: i1$1.SelectValueAccessor, selector: "ion-range, ion-select, ion-radio-group, ion-segment, ion-datetime" }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TapMonitoringSettingsComponent, decorators: [{ type: Component, args: [{ selector: 'tap-monitoring-settings', template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-button (click)=\"dismiss()\">\n <ion-icon name=\"arrow-back\"></ion-icon>\n </ion-button>\n </ion-buttons>\n <ion-title class=\"text-center\">\n Monitoring Settings\n </ion-title>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <form [formGroup]=\"form\">\n <ion-list>\n <ion-item>\n <ion-label>Frequency (in milliseconds)</ion-label>\n <ion-input\n [formControlName]=\"'period'\"\n slot=\"end\"\n text-right\n type=\"number\"\n ></ion-input>\n </ion-item>\n <ion-item *ngIf=\"showDataSource\">\n <ion-label>Data source</ion-label>\n <ion-select [formControlName]=\"'dataSource'\" slot=\"end\">\n <ion-select-option value=\"live\">Live data</ion-select-option>\n <ion-select-option value=\"datalog\">Data log</ion-select-option>\n </ion-select>\n </ion-item>\n </ion-list>\n </form>\n</ion-content>\n<ion-footer>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"dismiss()\">Ok</ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-footer>\n" }] }], ctorParameters: function () { return [{ type: i1.FormBuilder }, { type: MonitoringSettingsService }, { type: i1$1.ModalController }]; }, propDecorators: { showDataSource: [{ type: Input }] } }); class TapMonitoringSettingsModule { } /** @nocollapse */ TapMonitoringSettingsModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TapMonitoringSettingsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); /** @nocollapse */ TapMonitoringSettingsModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: TapMonitoringSettingsModule, declarations: [TapMonitoringSettingsComponent], imports: [CommonModule, IonicModule, ReactiveFormsModule], exports: [TapMonitoringSettingsComponent] }); /** @nocollapse */ TapMonitoringSettingsModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TapMonitoringSettingsModule, imports: [CommonModule, IonicModule, ReactiveFormsModule] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TapMonitoringSettingsModule, decorators: [{ type: NgModule, args: [{ declarations: [TapMonitoringSettingsComponent], exports: [TapMonitoringSettingsComponent], entryComponents: [TapMonitoringSettingsComponent], imports: [CommonModule, IonicModule, ReactiveFormsModule], }] }] }); class ChartDataModel { constructor() { this.subList = []; this.currentData = []; } initLabel(indexOrArray) { if (indexOrArray) { this.labels = indexOrArray; } } initVariable(variableOrArray) { if (!variableOrArray) { return []; } const vArray = Array.isArray(variableOrArray) ? variableOrArray : [variableOrArray]; return vArray.map((variable) => { return { stream: variable.values, label: variable.id?.toString() || '', }; }); } /** * @param inputs data inputs * @param history to show data with time * @param valueCountLimit when history has true, this one define the limit of length */ defineInputStreamSingle(inputs, history, valueCountLimit, forceHistory) { this.currentData = inputs; return new Observable((observer) => { let results = []; if (inputs) { if (!Array.isArray(inputs)) { inputs = [inputs]; } inputs.forEach((input) => { if (input) { const inputStream = input; this.subList.push(inputStream.stream.subscribe({ next: (data) => { if (Array.isArray(data) && !forceHistory) { if (data.length > 1) { history = false; } } let i = 0; if (history) { if (Array.isArray(data)) { const vals = JSON.parse('{"name": "' + this.currentDate() + '"}'); data.forEach((d) => { vals[i] = d; i++; }); results.push(vals); results = [ ...results.map((r) => { let newKeys = {}; let i = 0; for (const [key, value] of Object.entries(r)) { if (key !== 'name') { if (typeof key === 'number') { newKeys[key] = this.labelConfig(key, input.label); } else { newKeys[key] = this.labelConfig(i, input.label); } } else { newKeys[key] = key; } if (!(key === 'name' && i === 0)) { i++; } } const renamedObj = this.renameKeys(r, newKeys); const mappedObj = {}; mappedObj['name'] = renamedObj['name']; for (const [key, v] of Object.entries(renamedObj)) { if (key !== 'name') { mappedObj[key] = v; } } return mappedObj; }), ]; const itemCount = results.length; if (valueCountLimit !== undefined && itemCount > valueCountLimit) { const toRemove = itemCount - valueCountLimit; results.splice(0, toRemove); } } else { results.push({ name: this.currentDate(), value: data, extra: 0, }); const itemCount = results.length; if (valueCountLimit !== undefined && itemCount > valueCountLimit) { const toRemove = itemCount - valueCountLimit; results.splice(0, toRemove); } } } else { if (Array.isArray(data)) { data.map(() => { results[i] = { name: this.labelConfig(i, input.label), value: data[i], extra: i, }; i++; }); } else { results[i] = { name: this.labelConfig(i, input.label), value: data, extra: i, }; } } results = [...results]; observer.next(results); }, error: (err) => { console.log(err); observer.next([]); }, })); } }); } }); } defineMultiInputStream(inputs) { this.currentData = inputs; return new Observable((observer) => { let results = []; let indexInputs = 0; inputs.forEach((input) => { this.subList.push(input.stream.subscribe({ next: (values) => { let indexInput = 0; if (!Array.isArray(values)) { values = [values]; } values.map((value) => { let indexOfLabel = 0; for (const inputLabel of inputs) { if (inputLabel.label === input.label) { indexInputs = indexOfLabel; } indexOfLabel++; } if (!results[indexInput]) { results[indexInput] = { name: this.labelConfig(indexInput, 'Index '), series: [], }; } results[indexInput].series[indexInputs] = { name: inputs[indexInputs].label, value: value, extra: indexInput, }; indexInput++; }); results = [...results]; observer.next(results); }, error: (err) => { console.log(err); observer.next([]); }, })); }); }); } defineMultiInputStreamTable(inputs) { this.currentData = inputs; return new Observable((observer) => { let results = []; inputs.forEach((input) => { let indexValues = 0; this.subList.push(input.stream.subscribe({ next: (values) => { let indexOfLabel = 0; for (const inputLabel of inputs) { if (inputLabel.label === input.label) { indexValues = indexOfLabel; } indexOfLabel++; } const vals = JSON.parse('{"Name": "' + input.label + '"}'); let turn = 0; if (!Array.isArray(values)) { values = [values]; } for (const val of values) { vals[this.labelConfig(turn, 'Index')] = val; turn++; } results[indexValues] = vals; results = [...results]; observer.next(results); }, error: (error) => { console.log(error); observer.next([]); }, })); }); }); } defineInputStreamMulti(inputs, history, valueCountLimit) { this.currentData = inputs; return new Observable((observer) => { let results = []; if (inputs) { if (!Array.isArray(inputs)) { inputs = [inputs]; } inputs.map((input) => { const inputStream = input; this.subList.push(inputStream.stream.subscribe({ next: (data) => { const seriesData = []; let indexLabel = 0; if (history) { if (Array.isArray(data)) { data.forEach((element) => { seriesData.push({ name: this.labelConfig(indexLabel, inputStream.label), value: element, extra: indexLabel, }); indexLabel++; }); results.push({ name: this.currentDate(), series: seriesData, }); const itemCount = results.length; if (valueCountLimit !== undefined && itemCount > valueCountLimit) { const toRemove = itemCount - valueCountLimit; results.splice(0, toRemove); } } else { results.push({ name: new Date(), series: [ { name: inputStream.label, value: data, extra: 0, }, ], }); const itemCount = results.length; if (valueCountLimit !== undefined && itemCount > valueCountLimit) { const toRemove = itemCount - valueCountLimit; results.splice(0, toRemove); } } } else { if (Array.isArray(data)) { data.map((element) => { seriesData.push({ name: this.labelConfig(indexLabel, inputStream.label), value: element, extra: indexLabel, }); indexLabel++; }); results = [ { name: inputStream.label, series: seriesData, }, ]; } else { results = [ { name: inputStream.label, series: [ { name: this.labelConfig(indexLabel, inputStream.label), value: data, extra: indexLabel, }, ], }, ]; } } results = [...results]; observer.next(results); }, error: (err) => { console.log(err); observer.next([]); }, })); }); } }); } defineInputStreamHistoryMultiLegend(inputs, valueCountLimit) { this.currentData = inputs; return new Observable((observer) => { let results = []; if (inputs) { if (!Array.isArray(inputs)) { inputs = [inputs]; } results = []; inputs.map((input) => { if (input.stream) { const inputStream = input; this.subList.push(inputStream.stream.subscribe({ next: (value) => { let indexLabel = 0; if (Array.isArray(value)) { value.map(() => { if (!results[indexLabel]) { results.push({ name: this.labelConfig(indexLabel, inputStream.label), series: [], }); } else { results[indexLabel].name = this.labelConfig(indexLabel, inputStream.label); } results[indexLabel].series.push({ name: new Date(), value: value[indexLabel], extra: indexLabel, }); const itemCount = results[indexLabel].series.length; if (valueCountLimit !== undefined && itemCount > valueCountLimit) { const toRemove = itemCount - valueCountLimit; results[indexLabel].series.splice(0, toRemove); } indexLabel++; }); } else { if (!results[0]) { results.push({ name: inputStream.label, series: [], }); } results[0].series.push({ name: new Date(), value: value, extra: 0, }); const itemCount = results[0].series.length; if (valueCountLimit !== undefined && itemCount > valueCountLimit) { const toRemove = itemCount - valueCountLimit; results[0].series.splice(0, toRemove); } } results = [...results]; observer.next(results); }, error: (err) => { console.log(err); observer.next([]); }, })); } }); } }); } labelConfig(index, labelBundle) { let labelToReturn = ''; if (typeof this.labels === 'function') { labelToReturn = this.labels(index); } else if (!this.labels) { labelToReturn = labelBundle + (index + 1); } else { if (Array.isArray(this.labels)) { if (this.labels[index]) { labelToReturn = this.labels[index]; } else { labelToReturn = labelBundle + (index + 1); } } else if (!Array.isArray(this.labels) && index < 1) { labelToReturn = this.labels; } else if (!Array.isArray(this.labels) && index > 0) { labelToReturn = labelBundle + (index + 1); } } return labelToReturn; } digit(n) { return n > 9 ? '' + n : '0' + n; } renameKeys(obj, newKeys) { const keyValues = Object.entries(obj).map(([key, value]) => { const newKey = newKeys[key] || key; return { [newKey]: value }; }); return Object.assign({}, ...keyValues); } async showDetail(event, mod, component) { const modal = await mod.create({ component: component, cssClass: 'modal-class', componentProps: { label: event.label, value: event.value, index: event.extra, variable: this.currentData[0], }, }); return await modal.present(); } currentDate() { return ('' + this.digit(+new Date().getHours()) + ':' + this.digit(+new Date().getMinutes()) + ':' + this.digit(+new Date().getSeconds())); } destroyCurrentSub() { for (const sub of this.subList) { sub.unsubscribe(); } } } class ExportDataFormat { } function dataWithSeriesToCsv(dataReceive) { if (dataReceive.length === 0) { return []; } else { const data = []; dataReceive[0].series.forEach((value) => { const element = {}; if (!isNaN(new Date(value.name).getDate())) { element['time'] = value.name; } else { element['label'] = value.name; } dataReceive.forEach((a) => { const tab = a.series.filter((n) => value.name.toString() === n.name.toString()); if (tab.length !== 0) { if (!element[a.name]) { element[a.name] = tab[0].value; } } }); data.push(element); }); return data; } } class TapVariableDataPopupComponent { set variable(inputs) { this.results.push({ name: this.label, series: [], }); inputs.stream.pipe(takeUntil(this.destroyed)).subscribe((value) => { if (Array.isArray(value)) { this.currentValue = value[this.index]; } else { this.currentValue = value; } const seriesData = { name: new Date(), value: this.currentValue, extra: 0, }; this.results[0].series.push(seriesData); const itemCount = this.results[0].series.length; if (itemCount > 10) { const toRemove = itemCount - 10; this.results[0].series.splice(0, toRemove); } this.results = [...this.results]; }); } constructor(modalController) { this.modalController = modalController; this.legend = false; this.showLabels = false; this.animations = true; this.xAxis = true; this.yAxis = true; this.showYAxisLabel = false; this.showXAxisLabel = false; this.timeline = false; this.colorScheme = { domain: ['#5AA454', '#A10A28', '#C7B42C', '#AAAAAA'], }; this.results = []; this.destroyed = new Subject(); this.currentDate = '' + this.digit(+new Date().getHours()) + ':' + this.digit(+new Date().getMinutes()) + ':' + this.digit(+new Date().getSeconds()); } ngOnDestroy() { this.destroyed.next(); } async dismissModal() { await this.modalController.dismiss(); } digit(n) { return n > 9 ? '' + n : '0' + n; } } /** @nocollapse */ TapVariableDataPopupComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TapVariableDataPopupComponent, deps: [{ token: i1$1.ModalController }], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ TapVariableDataPopupComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: TapVariableDataPopupComponent, selector: "tap-tap-variable-data-popup", inputs: { value: "value", label: "label", index: "index", variable: "variable" }, ngImport: i0, template: "<ion-header translucent>\n <ion-toolbar>\n <ion-buttons slot=\"start\">\n <ion-button (click)=\"dismissModal()\"\n ><ion-icon name=\"arrow-back-outline\"></ion-icon\n ></ion-button>\n </ion-buttons>\n <ion-title>{{ label }}</ion-title>\n <ion-buttons slot=\"end\">\n <span>Details</span>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <ion-list>\n <ion-item>\n <ion-label>Value on click</ion-label>\n <ion-note class=\"value\" slot=\"end\">{{ value }}</ion-note>\n </ion-item>\n <ion-item>\n <ion-label>Time on click</ion-label>\n <ion-note class=\"value\" slot=\"end\">{{ currentDate }}</ion-note>\n </ion-item>\n\n <ion-item>\n <ion-label>Current value</ion-label>\n <ion-note class=\"value\" slot=\"end\">{{ currentValue }}</ion-note>\n </ion-item>\n\n <!-- <ion-item>\n <ion-label>Dynamic graphic</ion-label>\n <div #containerRef style=\"height: 30vh; width: 100%\">\n <ngx-charts-line-chart\n [view]=\"[containerRef.offsetWidth, containerRef.offsetHeight]\"\n [scheme]=\"colorScheme\"\n [showXAxisLabel]=\"showXAxisLabel\"\n [showYAxisLabel]=\"showYAxisLabel\"\n [legend]=\"legend\"\n [xAxis]=\"xAxis\"\n [yAxis]=\"yAxis\"\n [timeline]=\"timeline\"\n [results]=\"results\"\n >\n </ngx-charts-line-chart>\n </div>\n </ion-item> -->\n </ion-list>\n</ion-content>\n", styles: [".my-custom-modal-css{--background: transparent !important}.value{font-size:1.3em;font-weight:700}\n"], dependencies: [{ kind: "component", type: i1$1.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i1$1.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i1$1.IonContent, selector: "ion-content", inputs: ["color", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i1$1.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i1$1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i1$1.IonItem, selector: "ion-item", inputs: ["button", "color", "counter", "counterFormatter", "detail", "detailIcon", "disabled", "download", "fill", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "shape", "target", "type"] }, { kind: "component", type: i1$1.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i1$1.IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: i1$1.IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: i1$1.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i1$1.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TapVariableDataPopupComponent, decorators: [{ type: Component, args: [{ selector: 'tap-tap-variable-data-popup', template: "<ion-header translucent>\n <ion-toolbar>\n <ion-buttons slot=\"start\">\n <ion-button (click)=\"dismissModal()\"\n ><ion-icon name=\"arrow-back-outline\"></ion-icon\n ></ion-button>\n </ion-buttons>\n <ion-title>{{ label }}</ion-title>\n <ion-buttons slot=\"end\">\n <span>Details</span>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <ion-list>\n <ion-item>\n <ion-label>Value on click</ion-label>\n <ion-note class=\"value\" slot=\"end\">{{ value }}</ion-note>\n </ion-item>\n <ion-item>\n <ion-label>Time on click</ion-label>\n <ion-note class=\"value\" slot=\"end\">{{ currentDate }}</ion-note>\n </ion-item>\n\n <ion-item>\n <ion-label>Current value</ion-label>\n <ion-note class=\"value\" slot=\"end\">{{ currentValue }}</ion-note>\n </ion-item>\n\n <!-- <ion-item>\n <ion-label>Dynamic graphic</ion-label>\n <div #containerRef style=\"height: 30vh; width: 100%\">\n <ngx-charts-line-chart\n [view]=\"[containerRef.offsetWidth, containerRef.offsetHeight]\"\n [scheme]=\"colorScheme\"\n [showXAxisLabel]=\"showXAxisLabel\"\n [showYAxisLabel]=\"showYAxisLabel\"\n [legend]=\"legend\"\n [xAxis]=\"xAxis\"\n [yAxis]=\"yAxis\"\n [timeline]=\"timeline\"\n [results]=\"results\"\n >\n </ngx-charts-line-chart>\n </div>\n </ion-item> -->\n </ion-list>\n</ion-content>\n", styles: [".my-custom-modal-css{--background: transparent !important}.value{font-size:1.3em;font-weight:700}\n"] }] }], ctorParameters: function () { return [{ type: i1$1.ModalController }]; }, propDecorators: { value: [{ type: Input }], label: [{ type: Input }], index: [{ type: Input }], variable: [{ type: Input }] } }); class ExportDataService { constructor(file, platform) { this.file = file; this.platform = platform; } async triggerDownload(blob, filename) { if (this.platform.is('cordova')) { const directory = this.getDownloadDirectory(); filename = await this.ensureFileNameAvailable(directory, filename); await this.file.writeFile(directory, filename, blob); return directory; } else { const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.setAttribute('visibility', 'hidden'); link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); return undefined; } } getDownloadDirectory() { let directory; if (this.file.externalRootDirectory) { directory = this.file.externalRootDirectory; if (this.platform.is('android')) { directory += 'Download/'; } } else if (this.file.dataDirectory) { directory = this.file.dataDirectory; } else if (this.file.documentsDirectory) { directory = this.file.documentsDirectory; } if (!directory) { throw new Error('Cannot find a valid directory to download the log file'); } return directory; } async ensureFileNameAvailable(directory, filename) { const parts = filename.split('.'); const extension = parts.length > 1 ? '.' + parts[parts.length - 1] : ''; const filenameWithoutExt = parts.length > 1 ? parts.slice(0, parts.length - 1).join('.') : parts[0]; let count = 1; while (await this.file .checkFile(directory, filename) .then((_) => true) .catch((err) => { console.log('Check file err', err); return false; })) { filename = `${filenameWithoutExt} (${count})${extension}`; count++; } return filename; } } /** @nocollapse */ ExportDataService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ExportDataService, deps: [{ token: i1$2.File }, { token: i1$1.Platform }], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ ExportDataService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ExportDataService, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ExportDataService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }], ctorParameters: function () { return [{ type: i1$2.File }, { type: i1$1.Platform }]; } }); class ExportDataDirective { constructor(alertController, platform, dataToExportService) { this.alertController = alertController; this.platform = platform; this.dataToExportService = dataToExportService; this.exportDataError = new EventEmitter(); } async onClickEvent() { if (this.exportedFileName === undefined) { this.exportedFileName = 'data'; } let alert; let data = typeof this.dataToExport === 'function' ? this.dataToExport() : this.dataToExport; if (data?.length !== 0) { if (!this.format) { alert = await this.alertController.create({ header: 'Export data', cssClass: 'custom-alert', message: 'Select file format', buttons: [ { text: 'CSV', handler: async () => { this.exportToCsv(data); }, }, { text: 'JSON', handler: () => { this.exportToJson(data); }, }, ], }); await alert.present(); } else { switch (this.format) { case 'csv': await this.exportToCsv(data); break; case 'json': await this.exportToJson(data); break; } } } else { const alert = await this.alertController.create({ header: 'Export data', message: 'Cannot export empty data.', buttons: [ { text: 'OK', }, ], }); await alert.present(); } } exportToJson(data) { try { this.dataToExportService.triggerDownload(new Blob([JSON.stringify(data, null, 4)]), `${this.exportedFileName}.json`); } catch (e) { this.exportDataError.emit(e); } } async exportToCsv(data) { try { if (data) { if (!this.exportedFileName) { this.exportedFileName = 'data'; } const csvExport = new ExportToCsv(data, this.exportedFileName, { headers: Object.keys(data[0]), }); const csvString = csvExport.csvToString(); await this.dataToExportService.triggerDownload(new Blob([csvString]), `${this.exportedFileName}.csv`); } } catch (e) { this.exportDataError.emit(e); } } } /** @nocollapse */ ExportDataDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ExportDataDirective, deps: [{ token: i1$1.AlertController }, { token: i1$1.Platform }, { token: ExportDataService }], target: i0.ɵɵFactoryTarget.Directive }); /** @nocollapse */ ExportDataDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.10", type: ExportDataDirective, selector: "[tapExportData]", inputs: { exportedFileName: "exportedFileName", dataToExport: "dataToExport", format: "format" }, outputs: { exportDataError: "exportDataError" }, host: { listeners: { "click": "onClickEvent()" } }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ExportDataDirec