UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

498 lines 84.9 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ import { Component, ElementRef, EventEmitter, inject, Input, Output, ViewChild } from '@angular/core'; import { BehaviorSubject, forkJoin, lastValueFrom, of } from 'rxjs'; import { map, switchMap, tap } from 'rxjs/operators'; import { CustomMeasurementService } from './services/custom-measurements.service'; import { AlarmRealtimeService, CoreModule, DismissAlertStrategy, DynamicComponentAlert, DynamicComponentAlertAggregator, EventRealtimeService, gettext, MeasurementRealtimeService, WidgetTimeContextDateRangeService } from '@c8y/ngx-components'; import { EchartsOptionsService } from './services/echarts-options.service'; import { ChartRealtimeService } from './services/chart-realtime.service'; import { ChartTypesService } from './services/chart-types.service'; import { CommonModule } from '@angular/common'; import { NGX_ECHARTS_CONFIG, NgxEchartsModule } from 'ngx-echarts'; import { TooltipModule } from 'ngx-bootstrap/tooltip'; import { PopoverModule } from 'ngx-bootstrap/popover'; import { YAxisService } from './services/y-axis.service'; import { ChartAlertsComponent } from './chart-alerts/chart-alerts.component'; import { aggregationType, AlarmStatus } from '@c8y/client'; import { ChartEventsService } from './services/chart-events.service'; import { ChartAlarmsService } from './services/chart-alarms.service'; import { AlarmSeverityToIconPipe, AlarmSeverityToLabelPipe, AlarmsModule } from '@c8y/ngx-components/alarms'; import { INTERVALS } from '@c8y/ngx-components/interval-picker'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common"; import * as i2 from "@c8y/ngx-components"; import * as i3 from "ngx-echarts"; export class ChartsComponent { constructor() { this.showLoadMore = false; this.alarms = []; this.events = []; this.sliderZoomUsed = false; this.configChangeOnZoomOut = new EventEmitter(); this.timeRangeChangeOnRealtime = new EventEmitter(); this.datapointOutOfSync = new EventEmitter(); this.updateAlarmsAndEvents = new EventEmitter(); this.isMarkedAreaEnabled = new EventEmitter(); this.configChangedSubject = new BehaviorSubject(null); this.measurementService = inject(CustomMeasurementService); this.echartsOptionsService = inject(EchartsOptionsService); this.chartRealtimeService = inject(ChartRealtimeService); this.chartEventsService = inject(ChartEventsService); this.chartAlarmsService = inject(ChartAlarmsService); this.widgetTimeContextDateRangeService = inject(WidgetTimeContextDateRangeService); this.chartOption$ = this.configChangedSubject.pipe(switchMap(() => this.loadAlarmsAndEvents()), switchMap(() => this.fetchSeriesForDatapoints$()), switchMap((datapointsWithValues) => { if (datapointsWithValues.length === 0) { this.echartsInstance?.clear(); this.showLoadMore = false; return of(this.getDefaultChartOptions()); } return this.getChartOptions(datapointsWithValues); }), tap(v => { if (v['empty']) { return; } this.chartRealtimeService.stopRealtime(); this.startRealtimeIfPossible(); if (this.echartsInstance) { this.echartsInstance.setOption(v, true); const options = this.echartsInstance.getOption(); const dataZoom = options['dataZoom'][0]; if (dataZoom.start !== 0) { this.showLoadMore = false; } } this.sliderZoomUsed = false; })); } ngOnChanges() { this.configChangedSubject.next(); } ngOnInit() { if (this.alerts) { this.alerts.setAlertGroupDismissStrategy('warning', DismissAlertStrategy.TEMPORARY_OR_PERMANENT); } } ngOnDestroy() { this.chartRealtimeService.stopRealtime(); } onChartInit(ec) { this.echartsInstance = ec; this.echartsOptionsService.echartsInstance = this.echartsInstance; this.startRealtimeIfPossible(); const dataZoomHandler = (evt) => { if (evt.batch?.[0]?.start === 0 || evt['start'] === 0) { this.showLoadMore = true; } const options = this.echartsInstance.getOption(); const dataZoom = options['dataZoom'][0]; this.sliderZoomUsed = true; this.configChangeOnZoomOut.emit({ dateFrom: new Date(dataZoom['startValue']), dateTo: new Date(dataZoom['endValue']), interval: 'custom' }); this.echartsInstance.setOption(options); this.chartRealtimeService.stopRealtime(); }; const debouncedDataZoomHandler = this.config.realtime ? dataZoomHandler : this.debounce(dataZoomHandler, 1000); this.echartsInstance.on('dataZoom', debouncedDataZoomHandler); this.echartsInstance.on('click', this.onChartClick.bind(this)); let originalFormatter = null; this.echartsInstance.on('mouseover', (params) => { if (params?.componentType !== 'markLine' && params?.componentType !== 'markPoint') { return; } const options = this.echartsInstance.getOption(); if (!options.tooltip || !Array.isArray(options.tooltip) || !options.tooltip[0]) { return; } originalFormatter = originalFormatter ?? options['tooltip'][0].formatter; const updatedOptions = { tooltip: options['tooltip'][0] }; if (!updatedOptions.tooltip) { return; } updatedOptions.tooltip.formatter = (tooltipParams) => { return this.echartsOptionsService.getTooltipFormatterForAlarmAndEvents(tooltipParams, params, this.events, this.alarms); }; this.echartsInstance.setOption(updatedOptions); }); this.echartsInstance.on('mouseout', () => { const options = this.echartsInstance.getOption(); if (originalFormatter) { options['tooltip'][0].formatter = originalFormatter; this.echartsInstance.setOption(options); } }); } onChartClick(params) { const options = this.echartsInstance.getOption(); if (!this.isAlarmClick(params)) { this.echartsInstance.setOption({ tooltip: { triggerOn: 'mousemove' }, series: [{ markArea: { data: [] }, markLine: { data: [] } }] }); return; } const clickedAlarms = this.alarms.filter(alarm => alarm.type === params.data.itemType); this.isMarkedAreaEnabled.emit(this.hasMarkArea(options)); const updatedOptions = !this.hasMarkArea(options) ? { tooltip: { enterable: true, triggerOn: 'click' }, series: [ { markArea: { label: { show: false }, data: this.getMarkedAreaData(clickedAlarms) }, markLine: { showSymbol: true, symbol: ['none', 'none'], data: this.getMarkedLineData(clickedAlarms) } } ] } : // if markArea already exists, remove it and remove lastUpdated from markLine { tooltip: { triggerOn: 'mousemove' }, series: [{ markArea: { data: [] }, markLine: { data: [] } }] }; this.echartsInstance.setOption(updatedOptions); } isAlarmClick(params) { return this.alarms.some(alarm => alarm.type === params.data.itemType); } hasMarkArea(options) { return options?.series?.[0]?.markArea?.data?.length > 0; } saveAsImage() { this.echartsInstance.setOption({ legend: { show: true } }); const url = this.echartsInstance.getDataURL({ pixelRatio: 2, backgroundColor: '#fff', type: 'png' }); const link = document.createElement('a'); link.href = url; link.download = 'datapoints-graph-screenshot'; document.body.appendChild(link); link.click(); document.body.removeChild(link); this.echartsInstance.setOption({ legend: { show: false } }); } loadMoreData() { const currentTimeRangeInMs = this.echartsOptionsService.calculateExtendedIntervalInMs(this.config.interval || 'custom', { dateTo: this.widgetTimeContextDateRangeService.initialTimeRange().dateTo, dateFrom: this.widgetTimeContextDateRangeService.initialTimeRange().dateFrom }); this.widgetTimeContextDateRangeService.updateInitialTimeRange({ dateFrom: new Date(new Date(this.widgetTimeContextDateRangeService.initialTimeRange().dateFrom).valueOf() - currentTimeRangeInMs / 4).toISOString(), dateTo: this.widgetTimeContextDateRangeService.initialTimeRange().dateTo, interval: 'custom' }); const options = this.echartsInstance.getOption(); const dataZoom = options['dataZoom'][0]; this.configChangeOnZoomOut.emit({ dateFrom: new Date(dataZoom['startValue']), dateTo: new Date(dataZoom['endValue']), interval: 'custom' }); this.showLoadMore = false; } debounce(func, wait) { let timeout; return (...args) => { clearTimeout(timeout); timeout = setTimeout(() => func(...args), wait); }; } getDefaultChartOptions() { return { title: { text: gettext('No data available.'), left: 'center', top: 'center', textStyle: { fontSize: 30 } }, empty: true, xAxis: { type: 'category', data: [] }, yAxis: { type: 'value' }, series: [] }; } getMarkedAreaData(clickedAlarms) { const timeRange = this.getTimeRange(); const clearedAlarmColor = 'rgba(221,255,221,1.00)'; const activeAlarmColor = 'rgba(255, 173, 177, 0.4)'; return clickedAlarms.map(clickedAlarm => { return [ { name: clickedAlarm.type, xAxis: clickedAlarm.creationTime, itemStyle: { color: clickedAlarm.status === AlarmStatus.CLEARED ? clearedAlarmColor : activeAlarmColor } }, { xAxis: clickedAlarm['lastUpdated'] === clickedAlarm.creationTime || clickedAlarm.status !== AlarmStatus.CLEARED ? timeRange.dateTo : clickedAlarm['lastUpdated'] } ]; }); } getMarkedLineData(clickedAlarms) { return clickedAlarms.reduce((acc, alarm) => { const isClickedAlarmCleared = alarm.status === AlarmStatus.CLEARED; if (isClickedAlarmCleared) { return acc.concat([ { xAxis: alarm.creationTime, itemType: alarm.type, label: { show: false, formatter: () => alarm.type }, itemStyle: { color: alarm['color'] } }, { xAxis: alarm['lastUpdated'], itemType: alarm.type, label: { show: false, formatter: () => alarm.type }, itemStyle: { color: alarm['color'] } } ]); } return acc.concat([ { xAxis: alarm.creationTime, itemType: alarm.type, label: { show: false, formatter: () => alarm.type }, itemStyle: { color: alarm['color'] } } ]); }, []); } async loadAlarmsAndEvents(customTimeRange) { const timeRange = this.getTimeRange(); const updatedTimeRange = { lastUpdatedFrom: customTimeRange ? customTimeRange.dateFrom : timeRange.dateFrom, createdTo: customTimeRange ? customTimeRange.dateTo : timeRange.dateTo }; if (!this.config.alarmsEventsConfigs) return; const visibleAlarmsOrEvents = this.config.alarmsEventsConfigs?.filter(alarmOrEvent => !alarmOrEvent.__hidden && alarmOrEvent.__active); const alarms = visibleAlarmsOrEvents?.filter(alarmOrEvent => alarmOrEvent.timelineType === 'ALARM'); const events = visibleAlarmsOrEvents?.filter(alarmOrEvent => alarmOrEvent.timelineType === 'EVENT'); const [listedEvents, listedAlarms] = await Promise.all([ this.chartEventsService.listEvents(updatedTimeRange, events), this.chartAlarmsService.listAlarms(updatedTimeRange, alarms) ]); this.events = listedEvents; this.alarms = listedAlarms; await this.addActiveAlarms(alarms); this.updateAlarmsAndEvents.emit(this.config.alarmsEventsConfigs); } startRealtimeIfPossible() { const activeDatapoints = this.config?.datapoints?.filter(dp => dp.__active); if (activeDatapoints && this.config.realtime && this.echartsInstance) { this.chartRealtimeService.startRealtime(this.echartsInstance, activeDatapoints, this.getTimeRange(), dp => this.datapointOutOfSync.emit(dp), timeRange => this.timeRangeChangeOnRealtime.emit(timeRange), this.config.alarmsEventsConfigs, { displayMarkedLine: this.config.displayMarkedLine || false, displayMarkedPoint: this.config.displayMarkedPoint || false }); } } /* This method should check and add active alarms from the begining of time to the alarm array */ async addActiveAlarms(alarms) { const timeRange = this.getTimeRange(); const params = { dateFrom: '1970-01-01T01:00:00+01:00', dateTo: timeRange.dateTo, status: AlarmStatus.ACTIVE }; const activeAlarms = await this.chartAlarmsService.listAlarms(params, alarms); this.config.activeAlarmTypesOutOfRange = []; // iterate through the activeAlarms and check if the alarm is in the alarms array, if not update the config.activeAlarmTypesOutOfRange prop activeAlarms.forEach(activeAlarm => { const alarmType = activeAlarm.type; const alarm = this.alarms.find(alarm => alarm.type === alarmType); if (!alarm && this.config.activeAlarmTypesOutOfRange) { this.config.activeAlarmTypesOutOfRange.push(alarmType); } }); } async getChartOptions(datapointsWithValues) { const timeRange = { ...this.getTimeRange(), interval: this.config.interval }; if (!this.widgetTimeContextDateRangeService.initialTimeRange()) { this.widgetTimeContextDateRangeService.updateInitialTimeRange(timeRange); const aggregatedDatapoints = await lastValueFrom(this.fetchSeriesForDatapoints$({ dateFrom: new Date(new Date(timeRange.dateFrom).valueOf() - this.echartsOptionsService.calculateExtendedIntervalInMs(this.config.interval || 'custom', { dateFrom: timeRange.dateFrom, dateTo: timeRange.dateTo })).toISOString(), dateTo: timeRange.dateTo })); return this.echartsOptionsService.getChartOptions(datapointsWithValues, timeRange, { YAxis: this.config.yAxisSplitLines || false, XAxis: this.config.xAxisSplitLines || false }, this.events, this.alarms, { displayMarkedLine: this.config.displayMarkedLine || false, displayMarkedPoint: this.config.displayMarkedPoint || false, mergeMatchingDatapoints: this.config.mergeMatchingDatapoints || false, showLabelAndUnit: this.config.showLabelAndUnit || false, showSlider: this.config.showSlider || false }, undefined, aggregatedDatapoints); } else { const aggregatedDatapoints = await lastValueFrom(this.fetchSeriesForDatapoints$({ dateFrom: new Date(new Date(this.widgetTimeContextDateRangeService.initialTimeRange().dateFrom).valueOf() - this.echartsOptionsService.calculateExtendedIntervalInMs(this.config.interval || 'custom', { dateFrom: this.widgetTimeContextDateRangeService.initialTimeRange().dateFrom, dateTo: this.widgetTimeContextDateRangeService.initialTimeRange().dateTo })).toISOString(), dateTo: new Date(this.widgetTimeContextDateRangeService.initialTimeRange().dateTo).toISOString() })); return this.echartsOptionsService.getChartOptions(datapointsWithValues, this.widgetTimeContextDateRangeService.initialTimeRange(), { YAxis: this.config.yAxisSplitLines || false, XAxis: this.config.xAxisSplitLines || false }, this.events, this.alarms, { displayMarkedLine: this.config.displayMarkedLine || false, displayMarkedPoint: this.config.displayMarkedPoint || false, mergeMatchingDatapoints: this.config.mergeMatchingDatapoints || false, showLabelAndUnit: this.config.showLabelAndUnit || false, showSlider: this.config.showSlider || false }, timeRange, aggregatedDatapoints, true); } } fetchSeriesForDatapoints$(customTimeRange) { const activeDatapoints = this.config?.datapoints?.filter(dp => dp.__active); if (!activeDatapoints || activeDatapoints.length === 0) { return of([]); } const datapointsWithValuesRequests = []; const timeRange = this.getTimeRange(60_000); for (const dp of activeDatapoints) { const request = this.measurementService .listSeries$({ ...(customTimeRange ? customTimeRange : timeRange), source: dp.__target?.id || '', series: [`${dp.fragment}.${dp.series}`], ...(this.config.aggregation || customTimeRange ? { aggregationType: customTimeRange ? aggregationType.HOURLY : this.config.aggregation } : {}) }) .pipe(map(res => { const values = res.data.values; if (res.data.truncated && this.config.dateFrom) { values[new Date(this.config.dateFrom).toISOString()] = [{ min: null, max: null }]; } else { if (this.alerts) { this.alerts.clear(); } } return { ...dp, values, truncated: res.data.truncated }; })); datapointsWithValuesRequests.push(request); } return forkJoin(datapointsWithValuesRequests).pipe(tap((dpsWithValues) => { if (dpsWithValues.some(dp => dp['truncated'])) { this.addTruncatedDataAlert(); } })); } addTruncatedDataAlert() { if (this.alerts?.alertGroups.find(a => a.type === 'warning')?.value?.alerts?.length) { return; } const alert = new DynamicComponentAlert({ type: 'warning', text: gettext('Truncated data. Change aggregation or select shorter date range.') }); this.alerts?.addAlerts(alert); } getTimeRange(additionalPadding) { let timeRange; if (this.config.widgetInstanceGlobalTimeContext || (this.config.interval === 'custom' && !this.config.realtime)) { timeRange = { dateFrom: new Date(this.config.dateFrom), dateTo: new Date(this.config.dateTo) }; } else { let timeRangeInMs = 0; if (this.config.interval && this.config.interval !== 'custom') { timeRangeInMs = INTERVALS.find(i => i.id === this.config.interval)?.timespanInMs || 0; } else if (this.config.realtime) { timeRangeInMs = new Date(this.config.dateTo).valueOf() - new Date(this.config.dateFrom).valueOf(); } const now = new Date(); timeRange = { dateFrom: new Date(now.valueOf() - timeRangeInMs), dateTo: now }; } if (additionalPadding) { timeRange.dateFrom = new Date(timeRange.dateFrom.valueOf() - additionalPadding); timeRange.dateTo = new Date(timeRange.dateTo.valueOf() + additionalPadding); } return { dateFrom: timeRange.dateFrom.toISOString(), dateTo: timeRange.dateTo.toISOString() }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ChartsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: ChartsComponent, isStandalone: true, selector: "c8y-charts", inputs: { config: "config", alerts: "alerts" }, outputs: { configChangeOnZoomOut: "configChangeOnZoomOut", timeRangeChangeOnRealtime: "timeRangeChangeOnRealtime", datapointOutOfSync: "datapointOutOfSync", updateAlarmsAndEvents: "updateAlarmsAndEvents", isMarkedAreaEnabled: "isMarkedAreaEnabled" }, providers: [ { provide: NGX_ECHARTS_CONFIG, useFactory: () => ({ echarts: () => import('echarts') }) }, ChartRealtimeService, MeasurementRealtimeService, AlarmRealtimeService, EventRealtimeService, ChartTypesService, EchartsOptionsService, CustomMeasurementService, YAxisService, AlarmSeverityToIconPipe, AlarmSeverityToLabelPipe ], viewQueries: [{ propertyName: "chart", first: true, predicate: ["chart"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"p-relative fit-h chart-container\"\n>\n <div\n class=\"fit-w fit-h\"\n #chart\n echarts\n [options]=\"chartOption$ | async\"\n (chartInit)=\"onChartInit($event)\"\n ></div>\n\n <c8y-chart-alerts [alerts]=\"alerts\"></c8y-chart-alerts>\n\n <div *ngIf=\"showLoadMore\" class=\"chart-load-more\">\n <button\n class=\"btn btn-primary btn-sm btn-block\"\n (click)=\"loadMoreData()\"\n >\n <i c8yIcon=\"restore\"></i>\n {{ 'Load more' | translate }}\n </button>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }, { kind: "ngmodule", type: NgxEchartsModule }, { kind: "directive", type: i3.NgxEchartsDirective, selector: "echarts, [echarts]", inputs: ["options", "theme", "initOpts", "merge", "autoResize", "loading", "loadingType", "loadingOpts"], outputs: ["chartInit", "optionsError", "chartClick", "chartDblClick", "chartMouseDown", "chartMouseMove", "chartMouseUp", "chartMouseOver", "chartMouseOut", "chartGlobalOut", "chartContextMenu", "chartHighlight", "chartDownplay", "chartSelectChanged", "chartLegendSelectChanged", "chartLegendSelected", "chartLegendUnselected", "chartLegendLegendSelectAll", "chartLegendLegendInverseSelect", "chartLegendScroll", "chartDataZoom", "chartDataRangeSelected", "chartGraphRoam", "chartGeoRoam", "chartTreeRoam", "chartTimelineChanged", "chartTimelinePlayChanged", "chartRestore", "chartDataViewChanged", "chartMagicTypeChanged", "chartGeoSelectChanged", "chartGeoSelected", "chartGeoUnselected", "chartAxisAreaSelected", "chartBrush", "chartBrushEnd", "chartBrushSelected", "chartGlobalCursorTaken", "chartRendered", "chartFinished"], exportAs: ["echarts"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: ChartAlertsComponent, selector: "c8y-chart-alerts", inputs: ["alerts"] }, { kind: "ngmodule", type: AlarmsModule }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ChartsComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-charts', providers: [ { provide: NGX_ECHARTS_CONFIG, useFactory: () => ({ echarts: () => import('echarts') }) }, ChartRealtimeService, MeasurementRealtimeService, AlarmRealtimeService, EventRealtimeService, ChartTypesService, EchartsOptionsService, CustomMeasurementService, YAxisService, AlarmSeverityToIconPipe, AlarmSeverityToLabelPipe ], standalone: true, imports: [ CommonModule, CoreModule, NgxEchartsModule, TooltipModule, PopoverModule, ChartAlertsComponent, AlarmsModule ], template: "<div\n class=\"p-relative fit-h chart-container\"\n>\n <div\n class=\"fit-w fit-h\"\n #chart\n echarts\n [options]=\"chartOption$ | async\"\n (chartInit)=\"onChartInit($event)\"\n ></div>\n\n <c8y-chart-alerts [alerts]=\"alerts\"></c8y-chart-alerts>\n\n <div *ngIf=\"showLoadMore\" class=\"chart-load-more\">\n <button\n class=\"btn btn-primary btn-sm btn-block\"\n (click)=\"loadMoreData()\"\n >\n <i c8yIcon=\"restore\"></i>\n {{ 'Load more' | translate }}\n </button>\n </div>\n</div>\n" }] }], ctorParameters: () => [], propDecorators: { config: [{ type: Input }], alerts: [{ type: Input }], configChangeOnZoomOut: [{ type: Output }], timeRangeChangeOnRealtime: [{ type: Output }], datapointOutOfSync: [{ type: Output }], updateAlarmsAndEvents: [{ type: Output }], isMarkedAreaEnabled: [{ type: Output }], chart: [{ type: ViewChild, args: ['chart'] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hhcnRzLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL2VjaGFydC9jaGFydHMuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vZWNoYXJ0L2NoYXJ0cy5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx1REFBdUQ7QUFDdkQsT0FBTyxFQUNMLFNBQVMsRUFDVCxVQUFVLEVBQ1YsWUFBWSxFQUNaLE1BQU0sRUFDTixLQUFLLEVBSUwsTUFBTSxFQUNOLFNBQVMsRUFDVixNQUFNLGVBQWUsQ0FBQztBQVd2QixPQUFPLEVBQUUsZUFBZSxFQUFFLFFBQVEsRUFBRSxhQUFhLEVBQWMsRUFBRSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ2hGLE9BQU8sRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3JELE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLHdDQUF3QyxDQUFDO0FBQ2xGLE9BQU8sRUFDTCxvQkFBb0IsRUFDcEIsVUFBVSxFQUNWLG9CQUFvQixFQUNwQixxQkFBcUIsRUFDckIsK0JBQStCLEVBQy9CLG9CQUFvQixFQUNwQixPQUFPLEVBQ1AsMEJBQTBCLEVBQzFCLGlDQUFpQyxFQUNsQyxNQUFNLHFCQUFxQixDQUFDO0FBQzdCLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQzNFLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBTXpFLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ25FLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMvQyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbkUsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ3RELE9BQU8sRUFBRSxh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