UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

382 lines 53.6 kB
import { Injectable } from '@angular/core'; import { DataFetchingService } from '@c8y/ngx-components/datapoints-export-selector'; import { INTERVAL_VALUES } from '@c8y/ngx-components/interval-picker'; import { DURATION_OPTIONS } from '../datapoints-table-widget.model'; import * as i0 from "@angular/core"; import * as i1 from "@c8y/ngx-components/datapoints-export-selector"; function mapToSourceValueObject([key, value]) { return { key, value }; } export class DatapointsTableViewService { constructor(dataFetchingService) { this.dataFetchingService = dataFetchingService; } /** * Filters out inactive data points from the given array. * * @param datapoints - The array of data points to filter. * @returns An array of data points that are active. */ filterOutInactiveDatapoints(datapoints) { return datapoints.filter((datapoint) => datapoint.__active); } hasMultipleDatapoints(datapoints) { const ids = datapoints.map(dp => dp.__target.id); return ids.length > 1; } /** * Returns a map of active data points device IDs with their corresponding series. * * Example output: * ```typescript * new Map([ * [ * "844657202", * [ * "c8y_Temperature.T" * ] * ], * [ * "32666427", * [ * "c8y_Battery.Battery" * ] * ] * ]); * ``` * @param datapoints - An array of data points. * @returns A map where the key is the data point ID and the value is an array of data point series. */ groupSeriesByDeviceId(activeDatapoints) { return activeDatapoints.reduce((map, { fragment, series, __target: { id } }) => { const value = `${fragment}.${series}`; const existingValue = map.get(id) ?? []; map.set(id, [...existingValue, value]); return map; }, new Map()); } /** * Retrieves the active data points series data and returns it as a map. * * @param datapointsValuesDataMap - A map of data point sources with their associated series. * @param config - The configuration of the data points table. * @param roundSeconds - Whether to round the seconds or not. * If true, the seconds will be rounded to 0. * If false, the seconds will be displayed as they are. * @returns A Promise that resolves to a Map object with data point IDs as keys and DataObject as values or undefined when all series has forbidden access. */ async getAllActiveSeriesDataMap(datapointsValuesDataMap, config, roundSeconds) { const promises = Array.from(datapointsValuesDataMap).map(async ([source, series]) => { const params = { dateFrom: config.dateFrom, dateTo: config.dateTo, source, series, aggregationType: config.aggregation }; const { data, res } = await this.dataFetchingService.fetchSeriesData(params, roundSeconds); return { source, data, res }; }); const results = await Promise.all(promises); const allSeriesHasForbiddenAccess = results.every(item => item.res?.status === 403); if (allSeriesHasForbiddenAccess) { throw new Error('Access forbidden: All series have a 403 status code'); } const filteredResults = this.filterOutElementsWithForbiddenResponses(results); const resultMap = new Map(); filteredResults.forEach(result => resultMap.set(result.source, result.data)); return resultMap; } /** * Creates an array of DatapointsWithValues based on the provided datapoints and datapointsSeriesDataMap. * * Finds an index of a current data point within series object and based on that index filters values array. * * @param datapoints - An array of data points. * @param datapointsSeriesDataMap - A map containing series data for data points. * @returns An array of DatapointsWithValues. */ getDatapointsWithValues(datapoints, datapointsSeriesDataMap) { return datapoints.map((dp) => { const seriesData = datapointsSeriesDataMap.get(dp.__target.id); if (!seriesData) { return { ...dp, values: {} }; } // Find an index of a corresponding datapoint data, within series object. const datapointSeriesArrayIndex = seriesData.series.findIndex(s => s.name === dp.series && s.type === dp.fragment); const valuesFilteredByDatapointSeriesArrayIndex = Object.fromEntries(Object.entries(seriesData.values).map(([key, arr]) => [ key, arr.filter((_, index) => index === datapointSeriesArrayIndex) ])); return { ...dp, seriesUnit: seriesData.series[datapointSeriesArrayIndex]?.unit, values: valuesFilteredByDatapointSeriesArrayIndex }; }); } /** * Creates the column headers for the devices in the data points table. * * @param datapointsWithValues - An array of data points. * @returns An array of column headers for the devices. */ getColumnHeaders(datapointsWithValues) { return datapointsWithValues.map(({ __target: { name }, label, renderType, unit }) => { return { deviceName: name, label, renderType, unit }; }); } mapDatapointsWithValuesToList(datapointsWithValues) { return datapointsWithValues.flatMap(dp => { if (!dp.values) { return []; } return Object.entries(dp.values).flatMap(([date, valuesArray]) => { const value = this.findMinMaxValues(valuesArray); if (value == null) { return []; } return [ { dateAndTime: date, deviceName: dp.__target.name, fragment: dp.fragment, label: dp.label, redRangeMax: dp.redRangeMax, redRangeMin: dp.redRangeMin, renderType: dp.renderType, series: dp.series, value: value, yellowRangeMax: dp.yellowRangeMax, yellowRangeMin: dp.yellowRangeMin } ]; }); }); } /** * Finds the overall minimum and maximum values from an array of objects containing 'min' and 'max' properties. * * If the array contains only one object, that object's 'min' and 'max' values will be returned. * * @param valuesArray - An array with objects, where each contains 'min' and 'max' properties. * @returns An object with the smallest 'min' and largest 'max' values found in the array. * * @example * const values = [ * { min: 1, max: 10 } * ]; * * const result = findMinMaxValues(values); * // result is { min: 1, max: 10 } */ findMinMaxValues(valuesArray) { const validItems = valuesArray.filter((item) => item && item.min != null && item.max != null); if (validItems.length === 0) { return null; } const initialValue = { min: validItems[0].min, max: validItems[0].max }; return validItems.reduce((acc, item) => ({ min: Math.min(acc.min, item.min), max: Math.max(acc.max, item.max) }), initialValue); } /** * Groups a list of data points by date and device, based on given references. * * @param dataList - The list of data points to be grouped. * @param references - The column headers that serve as references for grouping. * @returns An array of grouped data points, where each group corresponds to a unique date and device. */ groupByDateAndDevice(dataList, references) { const map = this.generateDataPointMap(dataList, references); return this.mergeDatapoints(map); } /** * Generates and populates a map with data points. * * This function processes the provided data points and organizes them into a map structure * where each key is a unique combination of date and device identifiers, and the value is an * array of data points (or null values) associated with that key. This structured data is then * used in the data point table to render the data appropriately. * * @param dataList - The list of data point table items to be processed. * @param columnsHeadersReferences - The list of column headers used to determine the order and structure of the map values. * @returns A map where the key is a datapoint identifier containing the date and device name, and the value is an array of data point table items or null. */ generateDataPointMap(dataList, columnsHeadersReferences) { // Map to store the data points indexed by a unique identifier. const map = new Map(); dataList.forEach(obj => { // Generate a unique identifier for each data point using the date and device name. const dateKey = this.toISOFormat(obj.dateAndTime); const datapointIdentifier = `${dateKey}_${obj.deviceName}`; // Initialize the map entry if it does not exist with a unique identifier. if (!map.has(datapointIdentifier)) { map.set(datapointIdentifier, Array(columnsHeadersReferences.length).fill(null)); } // Find the index of the reference that matches the current data point. const matchingColumnIndex = columnsHeadersReferences.findIndex(ref => ref.deviceName === obj.deviceName && ref.label === obj.label); // Update the map entry with the data point. const tableItem = map.get(datapointIdentifier); if (tableItem) { tableItem[matchingColumnIndex] = { ...obj }; } }); return map; } /** * Merges the data points from the given map into an array of grouped data point table items. * * @param map - The map containing the data points to be merged. * @returns An array of grouped data point table items. */ mergeDatapoints(map) { const mergedData = []; map.forEach((values, datapointIdentifier) => { const [dateKey, deviceName] = datapointIdentifier.split('_'); const validDataPoint = values.some(item => item && Object.keys(item.value).length > 0); if (validDataPoint) { mergedData.push({ dateAndTime: dateKey, deviceName: deviceName, rowItems: values.map(item => (item ? { ...item } : null)) }); } }); return mergedData; } sortDataByDateDescending(data) { return data.sort((a, b) => new Date(b.dateAndTime).getTime() - new Date(a.dateAndTime).getTime()); } /** * Prepares the updated time range based on the selected interval. * * In case of a 'custom' interval or no quantity, the original date range is returned. * * @param interval - The selected interval type. * @returns An object containing the `dateFrom` and `dateTo` in ISO string format. */ prepareTimeRange(interval, dateFromInput, dateToInput) { if (!interval || interval === INTERVAL_VALUES.custom) { return { dateFrom: dateFromInput, dateTo: dateToInput }; } const selected = DURATION_OPTIONS.find(selectedDuration => selectedDuration.id === interval); if (!selected?.amount) { return { dateFrom: dateFromInput, dateTo: dateToInput }; } const now = new Date(); return { dateFrom: this.subtractTime(now, selected.amount, selected.unit).toISOString(), dateTo: now.toISOString() }; } /** * Subtracts an amount of time from a given date. * * @param date - The original date. * @param amount - The amount of time units to subtract. * @param unit - The unit of time to subtract (e.g., minutes, hours, days, weeks, months). * @returns A new date with the specified time subtracted. */ subtractTime(date, amount, unit) { const newDate = new Date(date); switch (unit) { case INTERVAL_VALUES.minutes: newDate.setUTCMinutes(newDate.getUTCMinutes() - amount); break; case INTERVAL_VALUES.hours: newDate.setUTCHours(newDate.getUTCHours() - amount); break; case INTERVAL_VALUES.days: newDate.setUTCDate(newDate.getUTCDate() - amount); break; case INTERVAL_VALUES.weeks: newDate.setUTCDate(newDate.getUTCDate() - amount * 7); break; case INTERVAL_VALUES.months: this.subtractMonthsAndAdjustDay(newDate, amount); break; } return newDate; } getSeriesWithoutPermissionToRead(activeDatapointsSeriesData, activeDatapointsIdsWithSeries) { if (!activeDatapointsSeriesData) { // Returns all activeDatapointsIdsWithSeries entries if activeDatapointsSeriesData is undefined. // It means that the user does not have permission to see any of the selected datapoints data. return Array.from(activeDatapointsIdsWithSeries, mapToSourceValueObject); } const availableSources = new Set(activeDatapointsSeriesData.keys()); return Array.from(activeDatapointsIdsWithSeries) .filter(([source]) => !availableSources.has(source)) .map(mapToSourceValueObject); } hasSecondsAndMillisecondsEqualZero(timeString) { if (!timeString) { return false; } const date = new Date(timeString); if (isNaN(date.getTime())) { return false; } return date.getUTCSeconds() === 0 && date.getUTCMilliseconds() === 0; } /** * Converts a date string to ISO format. * * @param dateStr - The date string to convert. * @returns The ISO format of the given date string. */ toISOFormat(dateStr) { return new Date(dateStr).toISOString(); } filterOutElementsWithForbiddenResponses(seriesDataWithResponse) { return seriesDataWithResponse.filter(item => { return !(item.res?.status === 403); }); } subtractMonthsAndAdjustDay(date, monthsToSubtract) { const currentMonth = date.getUTCMonth(); const expectedTargetMonth = this.calculateTargetMonth(currentMonth, monthsToSubtract); date.setUTCMonth(currentMonth - monthsToSubtract); const actualMonth = date.getUTCMonth(); const dayDoesNotExistInTargetMonth = actualMonth !== expectedTargetMonth; if (dayDoesNotExistInTargetMonth) { this.setToLastDayOfPreviousMonth(date); } } /** * Calculates the target month number (0-11) after subtracting months from the current month. * Handles negative month numbers by normalizing them to the valid 0-11 range. * * Examples: * - January(0) - 1 month = December(11) * - March(2) - 4 months = November(10) * - December(11) - 1 month = November(10) * * @param currentMonth - Current month (0-11, where 0 is January) * @param monthsToSubtract - Number of months to subtract * @returns Normalized month number in range 0-11 */ calculateTargetMonth(currentMonth, monthsToSubtract) { return (currentMonth - monthsToSubtract + 12) % 12; } /** * Sets the date to the last day of the previous month. * Using 0 as dateValue makes JavaScript automatically calculate * last day of previous month, per JavaScript Date API behavior. * @param date - Date to modify */ setToLastDayOfPreviousMonth(date) { date.setUTCDate(0); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatapointsTableViewService, deps: [{ token: i1.DataFetchingService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatapointsTableViewService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatapointsTableViewService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1.DataFetchingService }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YXBvaW50cy10YWJsZS12aWV3LnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi93aWRnZXRzL2ltcGxlbWVudGF0aW9ucy9kYXRhcG9pbnRzLXRhYmxlL2RhdGFwb2ludHMtdGFibGUtdmlldy9kYXRhcG9pbnRzLXRhYmxlLXZpZXcuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRzNDLE9BQU8sRUFDTCxtQkFBbUIsRUFJcEIsTUFBTSxnREFBZ0QsQ0FBQztBQUN4RCxPQUFPLEVBQUUsZUFBZSxFQUFZLE1BQU0scUNBQXFDLENBQUM7QUFDaEYsT0FBTyxFQUNMLGdCQUFnQixFQWFqQixNQUFNLGtDQUFrQyxDQUFDOzs7QUFFMUMsU0FBUyxzQkFBc0IsQ0FBQyxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQXVCO0lBSWhFLE9BQU8sRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLENBQUM7QUFDeEIsQ0FBQztBQUtELE1BQU0sT0FBTywwQkFBMEI7SUFDckMsWUFBb0IsbUJBQXdDO1FBQXhDLHdCQUFtQixHQUFuQixtQkFBbUIsQ0FBcUI7SUFBRyxDQUFDO0lBRWhFOzs7OztPQUtHO0lBQ0gsMkJBQTJCLENBQUMsVUFBd0I7UUFDbEQsT0FBTyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUMsU0FBcUIsRUFBRSxFQUFFLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQzFFLENBQUM7SUFFRCxxQkFBcUIsQ0FBQyxVQUF3QjtRQUM1QyxNQUFNLEdBQUcsR0FBRyxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNqRCxPQUFPLEdBQUcsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQ3hCLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7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bXBvcnQgeyBJUmVzdWx0LCBJU2VyaWVzLCBJU2VyaWVzRmlsdGVyLCBhZ2dyZWdhdGlvblR5cGUgfSBmcm9tICdAYzh5L2NsaWVudCc7XG5pbXBvcnQgeyBLUElEZXRhaWxzIH0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cy9kYXRhcG9pbnQtc2VsZWN0b3InO1xuaW1wb3J0IHtcbiAgRGF0YUZldGNoaW5nU2VydmljZSxcbiAgRGF0YXBvaW50c1ZhbHVlc0RhdGFNYXAsXG4gIE1pbk1heFZhbHVlcyxcbiAgU291cmNlSWRcbn0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cy9kYXRhcG9pbnRzLWV4cG9ydC1zZWxlY3Rvcic7XG5pbXBvcnQgeyBJTlRFUlZBTF9WQUxVRVMsIEludGVydmFsIH0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cy9pbnRlcnZhbC1waWNrZXInO1xuaW1wb3J0IHtcbiAgRFVSQVRJT05fT1BUSU9OUyxcbiAgRGF0YVBvaW50c1RhYmxlTWFwLFxuICBEYXRhcG9pbnRUYWJsZUl0ZW0sXG4gIERhdGFwb2ludFRhYmxlTWFwS2V5LFxuICBEYXRhcG9pbnRXaXRoVmFsdWVzLFxuICBEYXRhcG9pbnRzU2VyaWVzRGF0YU1hcCxcbiAgRGF0YXBvaW50c1RhYmxlQ29uZmlnLFxuICBEdXJhdGlvbixcbiAgR3JvdXBlZERhdGFwb2ludFRhYmxlSXRlbSxcbiAgTWVhc3VyZW1lbnRSYW5nZXMsXG4gIFNlcmllc0RhdGFXaXRoUmVzcG9uc2UsXG4gIFRhYmxlQ29sdW1uSGVhZGVyLFxuICBWYWx1ZVxufSBmcm9tICcuLi9kYXRhcG9pbnRzLXRhYmxlLXdpZGdldC5tb2RlbCc7XG5cbmZ1bmN0aW9uIG1hcFRvU291cmNlVmFsdWVPYmplY3QoW2tleSwgdmFsdWVdOiBbU291cmNlSWQsIHN0cmluZ1tdXSk6IHtcbiAga2V5OiBTb3VyY2VJZDtcbiAgdmFsdWU6IHN0cmluZ1tdO1xufSB7XG4gIHJldHVybiB7IGtleSwgdmFsdWUgfTtcbn1cblxuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCdcbn0pXG5leHBvcnQgY2xhc3MgRGF0YXBvaW50c1RhYmxlVmlld1NlcnZpY2Uge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGRhdGFGZXRjaGluZ1NlcnZpY2U6IERhdGFGZXRjaGluZ1NlcnZpY2UpIHt9XG5cbiAgLyoqXG4gICAqIEZpbHRlcnMgb3V0IGluYWN0aXZlIGRhdGEgcG9pbnRzIGZyb20gdGhlIGdpdmVuIGFycmF5LlxuICAgKlxuICAgKiBAcGFyYW0gZGF0YXBvaW50cyAtIFRoZSBhcnJheSBvZiBkYXRhIHBvaW50cyB0byBmaWx0ZXIuXG4gICAqIEByZXR1cm5zIEFuIGFycmF5IG9mIGRhdGEgcG9pbnRzIHRoYXQgYXJlIGFjdGl2ZS5cbiAgICovXG4gIGZpbHRlck91dEluYWN0aXZlRGF0YXBvaW50cyhkYXRhcG9pbnRzOiBLUElEZXRhaWxzW10pOiBLUElEZXRhaWxzW10ge1xuICAgIHJldHVybiBkYXRhcG9pbnRzLmZpbHRlcigoZGF0YXBvaW50OiBLUElEZXRhaWxzKSA9PiBkYXRhcG9pbnQuX19hY3RpdmUpO1xuICB9XG5cbiAgaGFzTXVsdGlwbGVEYXRhcG9pbnRzKGRhdGFwb2ludHM6IEtQSURldGFpbHNbXSk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IGlkcyA9IGRhdGFwb2ludHMubWFwKGRwID0+IGRwLl9fdGFyZ2V0LmlkKTtcbiAgICByZXR1cm4gaWRzLmxlbmd0aCA+IDE7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBhIG1hcCBvZiBhY3RpdmUgZGF0YSBwb2ludHMgZGV2aWNlIElEcyB3aXRoIHRoZWlyIGNvcnJlc3BvbmRpbmcgc2VyaWVzLlxuICAgKlxuICAgKiBFeGFtcGxlIG91dHB1dDpcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBuZXcgTWFwKFtcbiAgICogICBbXG4gICAqICAgICBcIjg0NDY1NzIwMlwiLFxuICAgKiAgICAgW1xuICAgKiAgICAgICBcImM4eV9UZW1wZXJhdHVyZS5UXCJcbiAgICogICAgIF1cbiAgICogICBdLFxuICAgKiAgIFtcbiAgICogICAgIFwiMzI2NjY0MjdcIixcbiAgICogICAgIFtcbiAgICogICAgICAgXCJjOHlfQmF0dGVyeS5CYXR0ZXJ5XCJcbiAgICogICAgIF1cbiAgICogICBdXG4gICAqIF0pO1xuICAgKiBgYGBcbiAgICogQHBhcmFtIGRhdGFwb2ludHMgLSBBbiBhcnJheSBvZiBkYXRhIHBvaW50cy5cbiAgICogQHJldHVybnMgQSBtYXAgd2hlcmUgdGhlIGtleSBpcyB0aGUgZGF0YSBwb2ludCBJRCBhbmQgdGhlIHZhbHVlIGlzIGFuIGFycmF5IG9mIGRhdGEgcG9pbnQgc2VyaWVzLlxuICAgKi9cbiAgZ3JvdXBTZXJpZXNCeURldmljZUlkKGFjdGl2ZURhdGFwb2ludHM6IEtQSURldGFpbHNbXSk6IERhdGFwb2ludHNWYWx1ZXNEYXRhTWFwIHtcbiAgICByZXR1cm4gYWN0aXZlRGF0YXBvaW50cy5yZWR1Y2UoXG4gICAgICAobWFwOiBEYXRhcG9pbnRzVmFsdWVzRGF0YU1hcCwgeyBmcmFnbWVudCwgc2VyaWVzLCBfX3RhcmdldDogeyBpZCB9IH06IEtQSURldGFpbHMpID0+IHtcbiAgICAgICAgY29uc3QgdmFsdWUgPSBgJHtmcmFnbWVudH0uJHtzZXJpZXN9YDtcbiAgICAgICAgY29uc3QgZXhpc3RpbmdWYWx1ZSA9IG1hcC5nZXQoaWQpID8/IFtdO1xuICAgICAgICBtYXAuc2V0KGlkLCBbLi4uZXhpc3RpbmdWYWx1ZSwgdmFsdWVdKTtcbiAgICAgICAgcmV0dXJuIG1hcDtcbiAgICAgIH0sXG4gICAgICBuZXcgTWFwPFNvdXJjZUlkLCBzdHJpbmdbXT4oKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogUmV0cmlldmVzIHRoZSBhY3RpdmUgZGF0YSBwb2ludHMgc2VyaWVzIGRhdGEgYW5kIHJldHVybnMgaXQgYXMgYSBtYXAuXG4gICAqXG4gICAqIEBwYXJhbSBkYXRhcG9pbnRzVmFsdWVzRGF0YU1hcCAtIEEgbWFwIG9mIGRhdGEgcG9pbnQgc291cmNlcyB3aXRoIHRoZWlyIGFzc29jaWF0ZWQgc2VyaWVzLlxuICAgKiBAcGFyYW0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2YgdGhlIGRhdGEgcG9pbnRzIHRhYmxlLlxuICAgKiBAcGFyYW0gcm91bmRTZWNvbmRzIC0gV2hldGhlciB0byByb3VuZCB0aGUgc2Vjb25kcyBvciBub3QuXG4gICAqICAgICAgICAgICAgICAgICAgICAgICBJZiB0cnVlLCB0aGUgc2Vjb25kcyB3aWxsIGJlIHJvdW5kZWQgdG8gMC5cbiAgICogICAgICAgICAgICAgICAgICAgICAgIElmIGZhbHNlLCB0aGUgc2Vjb25kcyB3aWxsIGJlIGRpc3BsYXllZCBhcyB0aGV5IGFyZS5cbiAgICogQHJldHVybnMgQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSBNYXAgb2JqZWN0IHdpdGggZGF0YSBwb2ludCBJRHMgYXMga2V5cyBhbmQgRGF0YU9iamVjdCBhcyB2YWx1ZXMgb3IgdW5kZWZpbmVkIHdoZW4gYWxsIHNlcmllcyBoYXMgZm9yYmlkZGVuIGFjY2Vzcy5cbiAgICovXG4gIGFzeW5jIGdldEFsbEFjdGl2ZVNlcmllc0RhdGFNYXAoXG4gICAgZGF0YXBvaW50c1ZhbHVlc0RhdGFNYXA6IERhdGFwb2ludHNWYWx1ZXNEYXRhTWFwLFxuICAgIGNvbmZpZzogRGF0YXBvaW50c1RhYmxlQ29uZmlnLFxuICAgIHJvdW5kU2Vjb25kczogYm9vbGVhblxuICApOiBQcm9taXNlPE1hcDxzdHJpbmcgfCBudW1iZXIsIElTZXJpZXM+PiB7XG4gICAgY29uc3QgcHJvbWlzZXMgPSBBcnJheS5mcm9tKGRhdGFwb2ludHNWYWx1ZXNEYXRhTWFwKS5tYXAoYXN5bmMgKFtzb3VyY2UsIHNlcmllc10pID0+IHtcbiAgICAgIGNvbnN0IHBhcmFtczogSVNlcmllc0ZpbHRlciA9IHtcbiAgICAgICAgZGF0ZUZyb206IGNvbmZpZy5kYXRlRnJvbSxcbiAgICAgICAgZGF0ZVRvOiBjb25maWcuZGF0ZVRvLFxuICAgICAgICBzb3VyY2UsXG4gICAgICAgIHNlcmllcyxcbiAgICAgICAgYWdncmVnYXRpb25UeXBlOiBjb25maWcuYWdncmVnYXRpb24gYXMgYWdncmVnYXRpb25UeXBlXG4gICAgICB9O1xuICAgICAgY29uc3QgeyBkYXRhLCByZXMgfTogSVJlc3VsdDxJU2VyaWVzPiA9IGF3YWl0IHRoaXMuZGF0YUZldGNoaW5nU2VydmljZS5mZXRjaFNlcmllc0RhdGEoXG4gICAgICAgIHBhcmFtcyxcbiAgICAgICAgcm91bmRTZWNvbmRzXG4gICAgICApO1xuXG4gICAgICByZXR1cm4geyBzb3VyY2UsIGRhdGEsIHJlcyB9O1xuICAgIH0pO1xuXG4gICAgY29uc3QgcmVzdWx0czogU2VyaWVzRGF0YVdpdGhSZXNwb25zZVtdID0gYXdhaXQgUHJvbWlzZS5hbGwocHJvbWlzZXMpO1xuXG4gICAgY29uc3QgYWxsU2VyaWVzSGFzRm9yYmlkZGVuQWNjZXNzID0gcmVzdWx0cy5ldmVyeShpdGVtID0+IGl0ZW0ucmVzPy5zdGF0dXMgPT09IDQwMyk7XG4gICAgaWYgKGFsbFNlcmllc0hhc0ZvcmJpZGRlbkFjY2Vzcykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdBY2Nlc3MgZm9yYmlkZGVuOiBBbGwgc2VyaWVzIGhhdmUgYSA0MDMgc3RhdHVzIGNvZGUnKTtcbiAgICB9XG5cbiAgICBjb25zdCBmaWx0ZXJlZFJlc3VsdHM6IFNlcmllc0RhdGFXaXRoUmVzcG9uc2VbXSA9XG4gICAgICB0aGlzLmZpbHRlck91dEVsZW1lbnRzV2l0aEZvcmJpZGRlblJlc3BvbnNlcyhyZXN1bHRzKTtcblxuICAgIGNvbnN0IHJlc3VsdE1hcDogRGF0YXBvaW50c1Nlcmllc0RhdGFNYXAgPSBuZXcgTWFwPHN0cmluZyB8IG51bWJlciwgSVNlcmllcz4oKTtcbiAgICBmaWx0ZXJlZFJlc3VsdHMuZm9yRWFjaChyZXN1bHQgPT4gcmVzdWx0TWFwLnNldChyZXN1bHQuc291cmNlLCByZXN1bHQuZGF0YSkpO1xuXG4gICAgcmV0dXJuIHJlc3VsdE1hcDtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGFuIGFycmF5IG9mIERhdGFwb2ludHNXaXRoVmFsdWVzIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBkYXRhcG9pbnRzIGFuZCBkYXRhcG9pbnRzU2VyaWVzRGF0YU1hcC5cbiAgICpcbiAgICogRmluZHMgYW4gaW5kZXggb2YgYSBjdXJyZW50IGRhdGEgcG9pbnQgd2l0aGluIHNlcmllcyBvYmplY3QgYW5kIGJhc2VkIG9uIHRoYXQgaW5kZXggZmlsdGVycyB2YWx1ZXMgYXJyYXkuXG4gICAqXG4gICAqIEBwYXJhbSBkYXRhcG9pbnRzIC0gQW4gYXJyYXkgb2YgZGF0YSBwb2ludHMuXG4gICAqIEBwYXJhbSBkYXRhcG9pbnRzU2VyaWVzRGF0YU1hcCAtIEEgbWFwIGNvbnRhaW5pbmcgc2VyaWVzIGRhdGEgZm9yIGRhdGEgcG9pbnRzLlxuICAgKiBAcmV0dXJucyBBbiBhcnJheSBvZiBEYXRhcG9pbnRzV2l0aFZhbHVlcy5cbiAgICovXG4gIGdldERhdGFwb2ludHNXaXRoVmFsdWVzKFxuICAgIGRhdGFwb2ludHM6IEtQSURldGFpbHNbXSxcbiAgICBkYXRhcG9pbnRzU2VyaWVzRGF0YU1hcDogRGF0YXBvaW50c1Nlcmllc0RhdGFNYXBcbiAgKTogRGF0YXBvaW50V2l0aFZhbHVlc1tdIHtcbiAgICByZXR1cm4gZGF0YXBvaW50cy5tYXAoKGRwOiBLUElEZXRhaWxzKSA9PiB7XG4gICAgICBjb25zdCBzZXJpZXNEYXRhOiBJU2VyaWVzID0gZGF0YXBvaW50c1Nlcmllc0RhdGFNYXAuZ2V0KGRwLl9fdGFyZ2V0LmlkKTtcblxuICAgICAgaWYgKCFzZXJpZXNEYXRhKSB7XG4gICAgICAgIHJldHVybiB7IC4uLmRwLCB2YWx1ZXM6IHt9IH07XG4gICAgICB9XG5cbiAgICAgIC8vIEZpbmQgYW4gaW5kZXggb2YgYSBjb3JyZXNwb25kaW5nIGRhdGFwb2ludCBkYXRhLCB3aXRoaW4gc2VyaWVzIG9iamVjdC5cbiAgICAgIGNvbnN0IGRhdGFwb2ludFNlcmllc0FycmF5SW5kZXggPSBzZXJpZXNEYXRhLnNlcmllcy5maW5kSW5kZXgoXG4gICAgICAgIHMgPT4gcy5uYW1lID09PSBkcC5zZXJpZXMgJiYgcy50eXBlID09PSBkcC5mcmFnbWVudFxuICAgICAgKTtcblxuICAgICAgY29uc3QgdmFsdWVzRmlsdGVyZWRCeURhdGFwb2ludFNlcmllc0FycmF5SW5kZXg6IE1lYXN1cmVtZW50UmFuZ2VzID0gT2JqZWN0LmZyb21FbnRyaWVzKFxuICAgICAgICBPYmplY3QuZW50cmllcyhzZXJpZXNEYXRhLnZhbHVlcykubWFwKChba2V5LCBhcnJdKSA9PiBbXG4gICAgICAgICAga2V5LFxuICAgICAgICAgIGFyci5maWx0ZXIoKF8sIGluZGV4KSA9PiBpbmRleCA9PT0gZGF0YXBvaW50U2VyaWVzQXJyYXlJbmRleClcbiAgICAgICAgXSlcbiAgICAgICk7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIC4uLmRwLFxuICAgICAgICBzZXJpZXNVbml0OiBzZXJpZXNEYXRhLnNlcmllc1tkYXRhcG9pbnRTZXJpZXNBcnJheUluZGV4XT8udW5pdCxcbiAgICAgICAgdmFsdWVzOiB2YWx1ZXNGaWx0ZXJlZEJ5RGF0YXBvaW50U2VyaWVzQXJyYXlJbmRleFxuICAgICAgfTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIHRoZSBjb2x1bW4gaGVhZGVycyBmb3IgdGhlIGRldmljZXMgaW4gdGhlIGRhdGEgcG9pbnRzIHRhYmxlLlxuICAgKlxuICAgKiBAcGFyYW0gZGF0YXBvaW50c1dpdGhWYWx1ZXMgLSBBbiBhcnJheSBvZiBkYXRhIHBvaW50cy5cbiAgICogQHJldHVybnMgQW4gYXJyYXkgb2YgY29sdW1uIGhlYWRlcnMgZm9yIHRoZSBkZXZpY2VzLlxuICAgKi9cbiAgZ2V0Q29sdW1uSGVhZGVycyhkYXRhcG9pbnRzV2l0aFZhbHVlczogS1BJRGV0YWlsc1tdKTogVGFibGVDb2x1bW5IZWFkZXJbXSB7XG4gICAgcmV0dXJuIGRhdGFwb2ludHNXaXRoVmFsdWVzLm1hcChcbiAgICAgICh7IF9fdGFyZ2V0OiB7IG5hbWUgfSwgbGFiZWwsIHJlbmRlclR5cGUsIHVuaXQgfSk6IFRhYmxlQ29sdW1uSGVhZGVyID0+IHtcbiAgICAgICAgcmV0dXJuIHsgZGV2aWNlTmFtZTogbmFtZSwgbGFiZWwsIHJlbmRlclR5cGUsIHVuaXQgfTtcbiAgICAgIH1cbiAgICApO1xuICB9XG5cbiAgbWFwRGF0YXBvaW50c1dpdGhWYWx1ZXNUb0xpc3QoZGF0YXBvaW50c1dpdGhWYWx1ZXM6IERhdGFwb2ludFdpdGhWYWx1ZXNbXSk6IERhdGFwb2ludFRhYmxlSXRlbVtdIHtcbiAgICByZXR1cm4gZGF0YXBvaW50c1dpdGhWYWx1ZXMuZmxhdE1hcChkcCA9PiB7XG4gICAgICBpZiAoIWRwLnZhbHVlcykge1xuICAgICAgICByZXR1cm4gW107XG4gICAgICB9XG4gICAgICByZXR1cm4gT2JqZWN0LmVudHJpZXMoZHAudmFsdWVzKS5mbGF0TWFwKChbZGF0ZSwgdmFsdWVzQXJyYXldKSA9PiB7XG4gICAgICAgIGNvbnN0IHZhbHVlID0gdGhpcy5maW5kTWluTWF4VmFsdWVzKHZhbHVlc0FycmF5KTtcbiAgICAgICAgaWYgKHZhbHVlID09IG51bGwpIHtcbiAgICAgICAgICByZXR1cm4gW107XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICB7XG4gICAgICAgICAgICBkYXRlQW5kVGltZTogZGF0ZSxcbiAgICAgICAgICAgIGRldmljZU5hbWU6IGRwLl9fdGFyZ2V0Lm5hbWUsXG4gICAgICAgICAgICBmcmFnbWVudDogZHAuZnJhZ21lbnQsXG4gICAgICAgICAgICBsYWJlbDogZHAubGFiZWwsXG4gICAgICAgICAgICByZWRSYW5nZU1heDogZHAucmVkUmFuZ2VNYXgsXG4gICAgICAgICAgICByZWRSYW5nZU1pbjogZHAucmVkUmFuZ2VNaW4sXG4gICAgICAgICAgICByZW5kZXJUeXBlOiBkcC5yZW5kZXJUeXBlLFxuICAgICAgICAgICAgc2VyaWVzOiBkcC5zZXJpZXMsXG4gICAgICAgICAgICB2YWx1ZTogdmFsdWUsXG4gICAgICAgICAgICB5ZWxsb3dSYW5nZU1heDogZHAueWVsbG93UmFuZ2VNYXgsXG4gICAgICAgICAgICB5ZWxsb3dSYW5nZU1pbjogZHAueWVsbG93UmFuZ2VNaW5cbiAgICAgICAgICB9XG4gICAgICAgIF07XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGaW5kcyB0aGUgb3ZlcmFsbCBtaW5pbXVtIGFuZCBtYXhpbXVtIHZhbHVlcyBmcm9tIGFuIGFycmF5IG9mIG9iamVjdHMgY29udGFpbmluZyAnbWluJyBhbmQgJ21heCcgcHJvcGVydGllcy5cbiAgICpcbiAgICogSWYgdGhlIGFycmF5IGNvbnRhaW5zIG9ubHkgb25lIG9iamVjdCwgdGhhdCBvYmplY3QncyAnbWluJyBhbmQgJ21heCcgdmFsdWVzIHdpbGwgYmUgcmV0dXJuZWQuXG4gICAqXG4gICAqIEBwYXJhbSB2YWx1ZXNBcnJheSAtIEFuIGFycmF5IHdpdGggb2JqZWN0cywgd2hlcmUgZWFjaCBjb250YWlucyAnbWluJyBhbmQgJ21heCcgcHJvcGVydGllcy5cbiAgICogQHJldHVybnMgQW4gb2JqZWN0IHdpdGggdGhlIHNtYWxsZXN0ICdtaW4nIGFuZCBsYXJnZXN0ICdtYXgnIHZhbHVlcyBmb3VuZCBpbiB0aGUgYXJyYXkuXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGNvbnN0IHZhbHVlcyA9IFtcbiAgICogICB7IG1pbjogMSwgbWF4OiAxMCB9XG4gICAqIF07XG4gICAqXG4gICAqIGNvbnN0IHJlc3VsdCA9IGZpbmRNaW5NYXhWYWx1ZXModmFsdWVzKTtcbiAgICogLy8gcmVzdWx0IGlzIHsgbWluOiAxLCBtYXg6IDEwIH1cbiAgICovXG4gIGZpbmRNaW5NYXhWYWx1ZXModmFsdWVzQXJyYXk6IE1pbk1heFZhbHVlc1tdKTogVmFsdWUgfCBudWxsIHtcbiAgICBjb25zdCB2YWxpZEl0ZW1zID0gdmFsdWVzQXJyYXkuZmlsdGVyKFxuICAgICAgKGl0ZW0pOiBpdGVtIGlzIFZhbHVlID0+IGl0ZW0gJiYgaXRlbS5taW4gIT0gbnVsbCAmJiBpdGVtLm1heCAhPSBudWxsXG4gICAgKTtcblxuICAgIGlmICh2YWxpZEl0ZW1zLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgY29uc3QgaW5pdGlhbFZhbHVlOiBWYWx1ZSA9IHsgbWluOiB2YWxpZEl0ZW1zWzBdLm1pbiwgbWF4OiB2YWxpZEl0ZW1zWzBdLm1heCB9O1xuXG4gICAgcmV0dXJuIHZhbGlkSXRlbXMucmVkdWNlPFZhbHVlPihcbiAgICAgIChhY2MsIGl0ZW0pID0+ICh7XG4gICAgICAgIG1pbjogTWF0aC5taW4oYWNjLm1pbiwgaXRlbS5taW4pLFxuICAgICAgICBtYXg6IE1hdGgubWF4KGFjYy5tYXgsIGl0ZW0ubWF4KVxuICAgICAgfSksXG4gICAgICBpbml0aWFsVmFsdWVcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEdyb3VwcyBhIGxpc3Qgb2YgZGF0YSBwb2ludHMgYnkgZGF0ZSBhbmQgZGV2aWNlLCBiYXNlZCBvbiBnaXZlbiByZWZlcmVuY2VzLlxuICAgKlxuICAgKiBAcGFyYW0gZGF0YUxpc3QgLSBUaGUgbGlzdCBvZiBkYXRhIHBvaW50cyB0byBiZSBncm91cGVkLlxuICAgKiBAcGFyYW0gcmVmZXJlbmNlcyAtIFRoZSBjb2x1bW4gaGVhZGVycyB0aGF0IHNlcnZlIGFzIHJlZmVyZW5jZXMgZm9yIGdyb3VwaW5nLlxuICAgKiBAcmV0dXJucyBBbiBhcnJheSBvZiBncm91cGVkIGRhdGEgcG9pbnRzLCB3aGVyZSBlYWNoIGdyb3VwIGNvcnJlc3BvbmRzIHRvIGEgdW5pcXVlIGRhdGUgYW5kIGRldmljZS5cbiAgICovXG4gIGdyb3VwQnlEYXRlQW5kRGV2aWNlKFxuICAgIGRhdGFMaXN0OiBEYXRhcG9pbnRUYWJsZUl0ZW1bXSxcbiAgICByZWZlcmVuY2VzOiBUYWJsZUNvbHVtbkhlYWRlcltdXG4gICk6IEdyb3VwZWREYXRhcG9pbnRUYWJsZUl0ZW1bXSB7XG4gICAgY29uc3QgbWFwID0gdGhpcy5nZW5lcmF0ZURhdGFQb2ludE1hcChkYXRhTGlzdCwgcmVmZXJlbmNlcyk7XG4gICAgcmV0dXJuIHRoaXMubWVyZ2VEYXRhcG9pbnRzKG1hcCk7XG4gIH1cblxuICAvKipcbiAgICogR2VuZXJhdGVzIGFuZCBwb3B1bGF0ZXMgYSBtYXAgd2l0aCBkYXRhIHBvaW50cy5cbiAgICpcbiAgICogVGhpcyBmdW5jdGlvbiBwcm9jZXNzZXMgdGhlIHByb3ZpZGVkIGRhdGEgcG9pbnRzIGFuZCBvcmdhbml6ZXMgdGhlbSBpbnRvIGEgbWFwIHN0cnVjdHVyZVxuICAgKiB3aGVyZSBlYWNoIGtleSBpcyBhIHVuaXF1ZSBjb21iaW5hdGlvbiBvZiBkYXRlIGFuZCBkZXZpY2UgaWRlbnRpZmllcnMsIGFuZCB0aGUgdmFsdWUgaXMgYW5cbiAgICogYXJyYXkgb2YgZGF0YSBwb2ludHMgKG9yIG51bGwgdmFsdWVzKSBhc3NvY2lhdGVkIHdpdGggdGhhdCBrZXkuIFRoaXMgc3RydWN0dXJlZCBkYXRhIGlzIHRoZW5cbiAgICogdXNlZCBpbiB0aGUgZGF0YSBwb2ludCB0YWJsZSB0byByZW5kZXIgdGhlIGRhdGEgYXBwcm9wcmlhdGVseS5cbiAgICpcbiAgICogQHBhcmFtIGRhdGFMaXN0IC0gVGhlIGxpc3Qgb2YgZGF0YSBwb2ludCB0YWJsZSBpdGVtcyB0byBiZSBwcm9jZXNzZWQuXG4gICAqIEBwYXJhbSBjb2x1bW5zSGVhZGVyc1JlZmVyZW5jZXMgLSBUaGUgbGlzdCBvZiBjb2x1bW4gaGVhZGVycyB1c2VkIHRvIGRldGVybWluZSB0aGUgb3JkZXIgYW5kIHN0cnVjdHVyZSBvZiB0aGUgbWFwIHZhbHVlcy5cbiAgICogQHJldHVybnMgQSBtYXAgd2hlcmUgdGhlIGtleSBpcyBhIGRhdGFwb2ludCBpZGVudGlmaWVyIGNvbnRhaW5pbmcgdGhlIGRhdGUgYW5kIGRldmljZSBuYW1lLCBhbmQgdGhlIHZhbHVlIGlzIGFuIGFycmF5IG9mIGRhdGEgcG9pbnQgdGFibGUgaXRlbXMgb3IgbnVsbC5cbiAgICovXG4gIGdlbmVyYXRlRGF0YVBvaW50TWFwKFxuICAgIGRhdGFMaXN0OiBEYXRhcG9pbnRUYWJsZUl0ZW1bXSxcbiAgICBjb2x1bW5zSGVhZGVyc1JlZmVyZW5jZXM6IFRhYmxlQ29sdW1uSGVhZGVyW11cbiAgKTogRGF0YVBvaW50c1RhYmxlTWFwIHtcbiAgICAvLyBNYXAgdG8gc3RvcmUgdGhlIGRhdGEgcG9pbnRzIGluZGV4ZWQgYnkgYSB1bmlxdWUgaWRlbnRpZmllci5cbiAgICBjb25zdCBtYXA6IERhdGFQb2ludHNUYWJsZU1hcCA9IG5ldyBNYXA8RGF0YXBvaW50VGFibGVNYXBLZXksIChEYXRhcG9pbnRUYWJsZUl0ZW0gfCBudWxsKVtdPigpO1xuXG4gICAgZGF0YUxpc3QuZm9yRWFjaChvYmogPT4ge1xuICAgICAgLy8gR2VuZXJhdGUgYSB1bmlxdWUgaWRlbnRpZmllciBmb3IgZWFjaCBkYXRhIHBvaW50IHVzaW5nIHRoZSBkYXRlIGFuZCBkZXZpY2UgbmFtZS5cbiAgICAgIGNvbnN0IGRhdGVLZXkgPSB0aGlzLnRvSVNPRm9ybWF0KG9iai5kYXRlQW5kVGltZSk7XG4gICAgICBjb25zdCBkYXRhcG9pbnRJZGVudGlmaWVyOiBEYXRhcG9pbnRUYWJsZU1hcEtleSA9IGAke2RhdGVLZXl9XyR7b2JqLmRldmljZU5hbWV9YDtcblxuICAgICAgLy8gSW5pdGlhbGl6ZSB0aGUgbWFwIGVudHJ5IGlmIGl0IGRvZXMgbm90IGV4aXN0IHdpdGggYSB1bmlxdWUgaWRlbnRpZmllci5cbiAgICAgIGlmICghbWFwLmhhcyhkYXRhcG9pbnRJZGVudGlmaWVyKSkge1xuICAgICAgICBtYXAuc2V0KGRhdGFwb2ludElkZW50aWZpZXIsIEFycmF5KGNvbHVtbnNIZWFkZXJzUmVmZXJlbmNlcy5sZW5ndGgpLmZpbGwobnVsbCkpO1xuICAgICAgfVxuXG4gICAgICAvLyBGaW5kIHRoZSBpbmRleCBvZiB0aGUgcmVmZXJlbmNlIHRoYXQgbWF0Y2hlcyB0aGUgY3VycmVudCBkYXRhIHBvaW50LlxuICAgICAgY29uc3QgbWF0Y2hpbmdDb2x1bW5JbmRleCA9IGNvbHVtbnNIZWFkZXJzUmVmZXJlbmNlcy5maW5kSW5kZXgoXG4gICAgICAgIHJlZiA9PiByZWYuZGV2aWNlTmFtZSA9PT0gb2JqLmRldmljZU5hbWUgJiYgcmVmLmxhYmVsID09PSBvYmoubGFiZWxcbiAgICAgICk7XG5cbiAgICAgIC8vIFVwZGF0ZSB0aGUgbWFwIGVudHJ5IHdpdGggdGhlIGRhdGEgcG9pbnQuXG4gICAgICBjb25zdCB0YWJsZUl0ZW06IERhdGFwb2ludFRhYmxlSXRlbVtdID0gbWFwLmdldChkYXRhcG9pbnRJZGVudGlmaWVyKTtcbiAgICAgIGlmICh0YWJsZUl0ZW0pIHtcbiAgICAgICAgdGFibGVJdGVtW21hdGNoaW5nQ29sdW1uSW5kZXhdID0geyAuLi5vYmogfTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHJldHVybiBtYXA7XG4gIH1cblxuICAvKipcbiAgICogTWVyZ2VzIHRoZSBkYXRhIHBvaW50cyBmcm9tIHRoZSBnaXZlbiBtYXAgaW50byBhbiBhcnJheSBvZiBncm91cGVkIGRhdGEgcG9pbnQgdGFibGUgaXRlbXMuXG4gICAqXG4gICAqIEBwYXJhbSBtYXAgLSBUaGUgbWFwIGNvbnRhaW5pbmcgdGhlIGRhdGEgcG9pbnRzIHRvIGJlIG1lcmdlZC5cbiAgICogQHJldHVybnMgQW4gYXJyYXkgb2YgZ3JvdXBlZCBkYXRhIHBvaW50IHRhYmxlIGl0ZW1zLlxuICAgKi9cbiAgbWVyZ2VEYXRhcG9pbnRzKG1hcDogRGF0YVBvaW50c1RhYmxlTWFwKTogR3JvdXBlZERhdGFwb2ludFRhYmxlSXRlbVtdIHtcbiAgICBjb25zdCBtZXJnZWREYXRhOiBHcm91cGVkRGF0YXBvaW50VGFibGVJdGVtW10gPSBbXTtcblxuICAgIG1hcC5mb3JFYWNoKCh2YWx1ZXMsIGRhdGFwb2ludElkZW50aWZpZXIpID0+IHtcbiAgICAgIGNvbnN0IFtkYXRlS2V5LCBkZXZpY2VOYW1lXSA9IGRhdGFwb2ludElkZW50aWZpZXIuc3BsaXQoJ18nKTtcbiAgICAgIGNvbnN0IHZhbGlkRGF0YVBvaW50ID0gdmFsdWVzLnNvbWUoaXRlbSA9PiBpdGVtICYmIE9iamVjdC5rZXlzKGl0ZW0udmFsdWUpLmxlbmd0aCA+IDApO1xuXG4gICAgICBpZiAodmFsaWREYXRhUG9pbnQpIHtcbiAgICAgICAgbWVyZ2VkRGF0YS5wdXNoKHtcbiAgICAgICAgICBkYXRlQW5kVGltZTogZGF0ZUtleSxcbiAgICAgICAgICBkZXZpY2VOYW1lOiBkZXZpY2VOYW1lLFxuICAgICAgICAgIHJvd0l0ZW1zOiB2YWx1ZXMubWFwKGl0ZW0gPT4gKGl0ZW0gPyB7IC4uLml0ZW0gfSA6IG51bGwpKVxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHJldHVybiBtZXJnZWREYXRhO1xuICB9XG5cbiAgc29ydERhdGFCeURhdGVEZXNjZW5kaW5nKGRhdGE6IEdyb3VwZWREYXRhcG9pbnRUYWJsZUl0ZW1bXSk6IEdyb3VwZWREYXRhcG9pbnRUYWJsZUl0ZW1bXSB7XG4gICAgcmV0dXJuIGRhdGEuc29ydChcbiAgICAgIChhLCBiKSA9PiBuZXcgRGF0ZShiLmRhdGVBbmRUaW1lKS5nZXRUaW1lKCkgLSBuZXcgRGF0ZShhLmRhdGVBbmRUaW1lKS5nZXRUaW1lKClcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIFByZXBhcmVzIHRoZSB1cGRhdGVkIHRpbWUgcmFuZ2UgYmFzZWQgb24gdGhlIHNlbGVjdGVkIGludGVydmFsLlxuICAgKlxuICAgKiBJbiBjYXNlIG9mIGEgJ2N1c3RvbScgaW50ZXJ2YWwgb3Igbm8gcXVhbnRpdHksIHRoZSBvcmlnaW5hbCBkYXRlIHJhbmdlIGlzIHJldHVybmVkLlxuICAgKlxuICAgKiBAcGFyYW0gaW50ZXJ2YWwgLSBUaGUgc2VsZWN0ZWQgaW50ZXJ2YWwgdHlwZS5cbiAgICogQHJldHVybnMgQW4gb2JqZWN0IGNvbnRhaW5pbmcgdGhlIGBkYXRlRnJvbWAgYW5kIGBkYXRlVG9gIGluIElTTyBzdHJpbmcgZm9ybWF0LlxuICAgKi9cbiAgcHJlcGFyZVRpbWVSYW5nZShcbiAgICBpbnRlcnZhbDogSW50ZXJ2YWxbJ2lkJ10sXG4gICAgZGF0ZUZyb21JbnB1dDogc3RyaW5nLFxuICAgIGRhdGVUb0lucHV0OiBzdHJpbmdcbiAgKTogeyBkYXRlRnJvbTogc3RyaW5nOyBkYXRlVG86IHN0cmluZyB9IHtcbiAgICBpZiAoIWludGVydmFsIHx8IGludGVydmFsID09PSBJTlRFUlZBTF9WQUxVRVMuY3VzdG9tKSB7XG4gICAgICByZXR1cm4geyBkYXRlRnJvbTogZGF0ZUZyb21JbnB1dCwgZGF0ZVRvOiBkYXRlVG9JbnB1dCB9O1xuICAgIH1cblxuICAgIGNvbnN0IHNlbGVjdGVkOiBEdXJhdGlvbiA9IERVUkFUSU9OX09QVElPTlMuZmluZChcbiAgICAgIHNlbGVjdGVkRHVyYXRpb24gPT4gc2VsZWN0ZWREdXJhdGlvbi5pZCA9PT0gaW50ZXJ2YWxcbiAgICApO1xuXG4gICAgaWYgKCFzZWxlY3RlZD8uYW1vdW50KSB7XG4gICAgICByZXR1cm4geyBkYXRlRnJvbTogZGF0ZUZyb21JbnB1dCwgZGF0ZVRvOiBkYXRlVG9JbnB1dCB9O1xuICAgIH1cblxuICAgIGNvbnN0IG5vdyA9IG5ldyBEYXRlKCk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgZGF0ZUZyb206IHRoaXMuc3VidHJhY3RUaW1lKG5vdywgc2VsZWN0ZWQuYW1vdW50LCBzZWxlY3RlZC51bml0KS50b0lTT1N0cmluZygpLFxuICAgICAgZGF0ZVRvOiBub3cudG9JU09TdHJpbmcoKVxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogU3VidHJhY3RzIGFuIGFtb3VudCBvZiB0aW1lIGZyb20gYSBnaXZlbiBkYXRlLlxuICAgKlxuICAgKiBAcGFyYW0gZGF0ZSAtIFRoZSBvcmlnaW5hbCBkYXRlLlxuICAgKiBAcGFyYW0gYW1vdW50IC0gVGhlIGFtb3VudCBvZiB0aW1lIHVuaXRzIHRvIHN1YnRyYWN0LlxuICAgKiBAcGFyYW0gdW5pdCAtIFRoZSB1bml0IG9mIHRpbWUgdG8gc3VidHJhY3QgKGUuZy4sIG1pbnV0ZXMsIGhvdXJzLCBkYXlzLCB3ZWVrcywgbW9udGhzKS5cbiAgICogQHJldHVybnMgQSBuZXcgZGF0ZSB3aXRoIHRoZSBzcGVjaWZpZWQgdGltZSBzdWJ0cmFjdGVkLlxuICAgKi9cbiAgc3VidHJhY3RUaW1lKGRhdGU6IERhdGUsIGFtb3VudDogbnVtYmVyLCB1bml0OiBzdHJpbmcpOiBEYXRlIHtcbiAgICBjb25zdCBuZXdEYXRlID0gbmV3IERhdGUoZGF0ZSk7XG5cbiAgICBzd2l0Y2ggKHVuaXQpIHtcbiAgICAgIGNhc2UgSU5URVJWQUxfVkFMVUVTLm1pbnV0ZXM6XG4gICAgICAgIG5ld0RhdGUuc2V0VVRDTWludXRlcyhuZXdEYXRlLmdldFVUQ01pbnV0ZXMoKSAtIGFtb3VudCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBJTlRFUlZBTF9WQUxVRVMuaG91cnM6XG4gICAgICAgIG5ld0RhdGUuc2V0VVRDSG91cnMobmV3RGF0ZS5nZXRVVENIb3VycygpIC0gYW1vdW50KTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIElOVEVSVkFMX1ZBTFVFUy5kYXlzOlxuICAgICAgICBuZXdEYXRlLnNldFVUQ0RhdGUobmV3RGF0ZS5nZXRVVENEYXRlKCkgLSBhbW91bnQpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgSU5URVJWQUxfVkFMVUVTLndlZWtzOlxuICAgICAgICBuZXdEYXRlLnNldFVUQ0RhdGUobmV3RGF0ZS5nZXRVVENEYXRlKCkgLSBhbW91bnQgKiA3KTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIElOVEVSVkFMX1ZBTFVFUy5tb250aHM6XG4gICAgICAgIHRoaXMuc3VidHJhY3RNb250aHNBbmRBZGp1c3REYXkobmV3RGF0ZSwgYW1vdW50KTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICAgIHJldHVybiBuZXdEYXRlO1xuICB9XG5cbiAgZ2V0U2VyaWVzV2l0aG91dFBlcm1pc3Npb25Ub1JlYWQoXG4gICAgYWN0aXZlRGF0YXBvaW50c1Nlcmllc0RhdGE6IE1hcDxzdHJpbmcgfCBudW1iZXIsIElTZXJpZXM+IHwgdW5kZWZpbmVkLFxuICAgIGFjdGl2ZURhdGFwb2ludHNJZHNXaXRoU2VyaWVzOiBEYXRhcG9pbnRzVmFsdWVzRGF0YU1hcFxuICApOiB7IGtleTogU291cmNlSWQ7IHZhbHVlOiBzdHJpbmdbXSB9W10ge1xuICAgIGlmICghYWN0aXZlRGF0YXBvaW50c1Nlcmllc0RhdGEpIHtcbiAgICAgIC8vIFJldHVybnMgYWxsIGFjdGl2ZURhdGFwb2ludHNJZHNXaXRoU2VyaWVzIGVudHJpZXMgaWYgYWN0aXZlRGF0YXBvaW50c1Nlcmllc0RhdGEgaXMgdW5kZWZpbmVkLlxuICAgICAgLy8gSXQgbWVhbnMgdGhhdCB0aGUgdXNlciBkb2VzIG5vdCBoYXZlIHBlcm1pc3Npb24gdG8gc2VlIGFueSBvZiB0aGUgc2VsZWN0ZWQgZGF0YXBvaW50cyBkYXRhLlxuI