@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
105 lines • 32.6 kB
JavaScript
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { OperationService } from '@c8y/client';
import { AlertService, gettext, ManagedObjectRealtimeService, OperationRealtimeService } from '@c8y/ngx-components';
import { includes, isEmpty } from 'lodash-es';
import { BsModalService } from 'ngx-bootstrap/modal';
import { BehaviorSubject, of, pipe, Subject } from 'rxjs';
import { filter, map, startWith, takeUntil } from 'rxjs/operators';
import { CommandTemplatesComponent } from '../command-templates';
import { CommandDeliveryType, DeviceShellService } from '../shared';
import * as i0 from "@angular/core";
import * as i1 from "../shared";
import * as i2 from "@c8y/ngx-components";
import * as i3 from "@c8y/client";
import * as i4 from "@angular/router";
import * as i5 from "ngx-bootstrap/modal";
import * as i6 from "@angular/common";
import * as i7 from "@angular/forms";
import * as i8 from "@c8y/ngx-components/operations/operations-timeline";
export class DeviceShellComponent {
constructor(service, operationRealtime, moRealtime, operationService, route, modalService, alertService) {
this.service = service;
this.operationRealtime = operationRealtime;
this.moRealtime = moRealtime;
this.operationService = operationService;
this.route = route;
this.modalService = modalService;
this.alertService = alertService;
this.device = this.route.snapshot.parent.data.contextData;
this.device$ = of({});
this.deliveryTypes = (this.service.getDeliveryTypes() || []).map(deliveryType => ({ ...deliveryType, isSupportedByCommand: true }));
this.command = {};
this.smsEnabled = false;
this.sendingCommand$ = new BehaviorSubject(false);
this.filterPipe = pipe(map((operations) => (operations || []).filter((operation) => !!operation.c8y_Command)));
this.executeViaLabel = gettext('Execute via ({{deliveryType}})');
this.destroyed$ = new Subject();
}
async ngOnInit() {
this.smsEnabled = await this.service.canSendCommandsViaSMS();
this.device$ = this.moRealtime.onUpdate$(this.device.id).pipe(startWith(this.device));
this.operations = await this.operationService.list({
deviceId: this.device.id,
fragmentType: 'c8y_Command',
dateFrom: new Date(0).toISOString(),
dateTo: new Date().toISOString(),
pageSize: 50,
withTotalPages: true,
revert: true
});
this.operationRealtime
.onCreate$(this.device.id)
.pipe(takeUntil(this.destroyed$))
.subscribe(() => this.alertService.success(gettext('Command sent.')));
this.operationRealtime
.onUpdate$(this.device.id)
.pipe(filter(op => op.failureReason !== 'Operation cancelled by user.'), // avoid duplicate alerts
takeUntil(this.destroyed$))
.subscribe(() => this.alertService.success(gettext('Command status updated.')));
}
getPredefinedCommand() {
const modal = this.modalService.show(CommandTemplatesComponent, {
ariaDescribedby: 'modal-body',
ariaLabelledBy: 'modal-title'
});
modal.content.onTemplateSelected.pipe(takeUntil(this.destroyed$)).subscribe(result => {
this.command = { ...result.commandTemplate };
this.deliveryTypes = this.deliveryTypes.map(deliveryType => ({
...deliveryType,
isSupportedByCommand: isEmpty(this.command.deliveryTypes) ||
includes(this.command.deliveryTypes, deliveryType.name)
}));
});
}
resetSupportedDeliveryTypes() {
this.deliveryTypes = (this.service.getDeliveryTypes() || []).map(deliveryType => ({
...deliveryType,
isSupportedByCommand: true
}));
}
async execute(commandDeliveryType) {
const useSMS = commandDeliveryType === CommandDeliveryType.SMS;
if (useSMS && !this.smsEnabled) {
this.alertService.warning(gettext('SMS transport is not configured.'));
return;
}
this.sendingCommand$.next(true);
await this.service.createCommandOperation(this.device.id, this.command, commandDeliveryType);
this.command.text = '';
this.command.name = '';
this.resetSupportedDeliveryTypes();
this.sendingCommand$.next(false);
}
ngOnDestroy() {
this.destroyed$.next();
this.destroyed$.complete();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DeviceShellComponent, deps: [{ token: i1.DeviceShellService }, { token: i2.OperationRealtimeService }, { token: i2.ManagedObjectRealtimeService }, { token: i3.OperationService }, { token: i4.ActivatedRoute }, { token: i5.BsModalService }, { token: i2.AlertService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DeviceShellComponent, selector: "c8y-device-shell", providers: [OperationRealtimeService, ManagedObjectRealtimeService], ngImport: i0, template: "<c8y-action-bar-item [placement]=\"'right'\">\n <c8y-realtime-btn [service]=\"operationRealtime\"></c8y-realtime-btn>\n</c8y-action-bar-item>\n\n<div class=\"card content-fullpage d-grid grid__col--6-6--md\">\n <div class=\"inner-scroll d-flex d-col bg-level-0\">\n <div class=\"card-header large-padding separator sticky-top\">\n <div class=\"card-title\">\n {{ 'Command' | translate }}\n </div>\n </div>\n\n <div class=\"card-block d-flex d-col flex-grow large-padding\">\n <div class=\"d-flex p-b-16\">\n <button\n class=\"btn btn-default btn-sm\"\n type=\"button\"\n (click)=\"getPredefinedCommand()\"\n [title]=\"'Display a list of predefined commands' | translate\"\n data-cy=\"shell--predefined-commands\"\n >\n {{ 'Predefined commands' | translate }}\n </button>\n\n <div class=\"m-l-auto\">\n <device-status [mo]=\"device$ | async\"></device-status>\n </div>\n </div>\n <textarea\n [attr.aria-label]=\"'Commands' | translate\"\n class=\"form-control inner-scroll flex-grow bg-level-2 text-monospace\"\n [(ngModel)]=\"command.text\"\n data-cy=\"shell-component--commands\"\n (ngModelChange)=\"$event || resetSupportedDeliveryTypes()\"\n placeholder=\"{{ 'Add commands or use predefined commands above.' | translate }}\"\n ></textarea>\n </div>\n\n <div class=\"card-footer large-padding separator\">\n <ng-container *ngFor=\"let deliveryType of deliveryTypes\">\n <button\n class=\"btn btn-primary\"\n type=\"button\"\n *ngIf=\"deliveryType.isSupportedByCommand\"\n [disabled]=\"!command?.text || (sendingCommand$ | async)\"\n (click)=\"execute(deliveryType.name)\"\n >\n <span\n [title]=\"\n deliveryType.default\n ? ('Execute' | translate)\n : (executeViaLabel | translate: { deliveryType: deliveryType.name })\n \"\n >\n {{\n deliveryType.default\n ? ('Execute' | translate)\n : (executeViaLabel | translate: { deliveryType: deliveryType.name })\n }}\n </span>\n </button>\n </ng-container>\n </div>\n </div>\n <div class=\"inner-scroll bg-level-1\">\n <div class=\"card-header large-padding separator sticky-top\">\n <div class=\"card-title\">\n {{ 'Operations' | translate }}\n </div>\n </div>\n <div class=\"card-block large-padding\">\n <c8y-operations-timeline\n [operations]=\"operations\"\n [sourceId]=\"device.id\"\n [filterPipe]=\"filterPipe\"\n [bodyTemplate]=\"timelineItemBody\"\n [footerTemplates]=\"[timelineItemFooter]\"\n [propertiesToHide]=\"['c8y_Command']\"\n ></c8y-operations-timeline>\n <ng-template #timelineItemBody let-operation>\n <small>{{ operation.c8y_Command?.text || operation.description }}</small>\n </ng-template>\n <ng-template #timelineItemFooter let-operation>\n <div *ngIf=\"operation.c8y_Command?.text\">\n <div class=\"legend form-block\" translate>Command</div>\n <!-- Keep this in a single line since `pre-text` will preserve all whitespaces in the `pre` tag.-->\n <pre class=\"p-8 text-pre\"><code>{{operation.c8y_Command.text}}</code></pre>\n </div>\n <div *ngIf=\"operation.c8y_Command?.result\">\n <div class=\"legend form-block\" translate>Response</div>\n <!-- Keep this in a single line since `pre-text` will preserve all whitespaces in the `pre` tag.-->\n <pre class=\"p-8 text-pre\"><code>{{operation.c8y_Command.result}}</code></pre>\n </div>\n </ng-template>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i6.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2.ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i7.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i7.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i2.DeviceStatusComponent, selector: "device-status, c8y-device-status", inputs: ["mo", "size"] }, { kind: "component", type: i2.RealtimeButtonComponent, selector: "c8y-realtime-btn", inputs: ["service", "label", "title", "disabled"], outputs: ["onToggle"] }, { kind: "component", type: i8.OperationsTimelineComponent, selector: "c8y-operations-timeline", inputs: ["operations", "sourceId", "filterPipe", "bodyTemplate", "footerTemplates", "propertiesToHide"] }, { kind: "pipe", type: i6.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DeviceShellComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-device-shell', providers: [OperationRealtimeService, ManagedObjectRealtimeService], template: "<c8y-action-bar-item [placement]=\"'right'\">\n <c8y-realtime-btn [service]=\"operationRealtime\"></c8y-realtime-btn>\n</c8y-action-bar-item>\n\n<div class=\"card content-fullpage d-grid grid__col--6-6--md\">\n <div class=\"inner-scroll d-flex d-col bg-level-0\">\n <div class=\"card-header large-padding separator sticky-top\">\n <div class=\"card-title\">\n {{ 'Command' | translate }}\n </div>\n </div>\n\n <div class=\"card-block d-flex d-col flex-grow large-padding\">\n <div class=\"d-flex p-b-16\">\n <button\n class=\"btn btn-default btn-sm\"\n type=\"button\"\n (click)=\"getPredefinedCommand()\"\n [title]=\"'Display a list of predefined commands' | translate\"\n data-cy=\"shell--predefined-commands\"\n >\n {{ 'Predefined commands' | translate }}\n </button>\n\n <div class=\"m-l-auto\">\n <device-status [mo]=\"device$ | async\"></device-status>\n </div>\n </div>\n <textarea\n [attr.aria-label]=\"'Commands' | translate\"\n class=\"form-control inner-scroll flex-grow bg-level-2 text-monospace\"\n [(ngModel)]=\"command.text\"\n data-cy=\"shell-component--commands\"\n (ngModelChange)=\"$event || resetSupportedDeliveryTypes()\"\n placeholder=\"{{ 'Add commands or use predefined commands above.' | translate }}\"\n ></textarea>\n </div>\n\n <div class=\"card-footer large-padding separator\">\n <ng-container *ngFor=\"let deliveryType of deliveryTypes\">\n <button\n class=\"btn btn-primary\"\n type=\"button\"\n *ngIf=\"deliveryType.isSupportedByCommand\"\n [disabled]=\"!command?.text || (sendingCommand$ | async)\"\n (click)=\"execute(deliveryType.name)\"\n >\n <span\n [title]=\"\n deliveryType.default\n ? ('Execute' | translate)\n : (executeViaLabel | translate: { deliveryType: deliveryType.name })\n \"\n >\n {{\n deliveryType.default\n ? ('Execute' | translate)\n : (executeViaLabel | translate: { deliveryType: deliveryType.name })\n }}\n </span>\n </button>\n </ng-container>\n </div>\n </div>\n <div class=\"inner-scroll bg-level-1\">\n <div class=\"card-header large-padding separator sticky-top\">\n <div class=\"card-title\">\n {{ 'Operations' | translate }}\n </div>\n </div>\n <div class=\"card-block large-padding\">\n <c8y-operations-timeline\n [operations]=\"operations\"\n [sourceId]=\"device.id\"\n [filterPipe]=\"filterPipe\"\n [bodyTemplate]=\"timelineItemBody\"\n [footerTemplates]=\"[timelineItemFooter]\"\n [propertiesToHide]=\"['c8y_Command']\"\n ></c8y-operations-timeline>\n <ng-template #timelineItemBody let-operation>\n <small>{{ operation.c8y_Command?.text || operation.description }}</small>\n </ng-template>\n <ng-template #timelineItemFooter let-operation>\n <div *ngIf=\"operation.c8y_Command?.text\">\n <div class=\"legend form-block\" translate>Command</div>\n <!-- Keep this in a single line since `pre-text` will preserve all whitespaces in the `pre` tag.-->\n <pre class=\"p-8 text-pre\"><code>{{operation.c8y_Command.text}}</code></pre>\n </div>\n <div *ngIf=\"operation.c8y_Command?.result\">\n <div class=\"legend form-block\" translate>Response</div>\n <!-- Keep this in a single line since `pre-text` will preserve all whitespaces in the `pre` tag.-->\n <pre class=\"p-8 text-pre\"><code>{{operation.c8y_Command.result}}</code></pre>\n </div>\n </ng-template>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.DeviceShellService }, { type: i2.OperationRealtimeService }, { type: i2.ManagedObjectRealtimeService }, { type: i3.OperationService }, { type: i4.ActivatedRoute }, { type: i5.BsModalService }, { type: i2.AlertService }] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2hlbGwuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vZGV2aWNlLXNoZWxsL3NoZWxsL3NoZWxsLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uL2RldmljZS1zaGVsbC9zaGVsbC9zaGVsbC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFxQixNQUFNLGVBQWUsQ0FBQztBQUM3RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDakQsT0FBTyxFQUEyQyxnQkFBZ0IsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUN4RixPQUFPLEVBQ0wsWUFBWSxFQUNaLE9BQU8sRUFDUCw0QkFBNEIsRUFDNUIsd0JBQXdCLEVBQ3pCLE1BQU0scUJBQXFCLENBQUM7QUFDN0IsT0FBTyxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDOUMsT0FBTyxFQUFjLGNBQWMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxlQUFlLEVBQWMsRUFBRSxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDdEUsT0FBTyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ25FLE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ2pFLE9BQU8sRUFBVyxtQkFBbUIsRUFBZ0Isa0JBQWtCLEVBQUUsTUFBTSxXQUFXLENBQUM7Ozs7Ozs7Ozs7QUFPM0YsTUFBTSxPQUFPLG9CQUFvQjtJQW9CL0IsWUFDUyxPQUEyQixFQUMzQixpQkFBMkMsRUFDMUMsVUFBd0MsRUFDeEMsZ0JBQWtDLEVBQ2xDLEtBQXFCLEVBQ3JCLFlBQTRCLEVBQzVCLFlBQTBCO1FBTjNCLFlBQU8sR0FBUCxPQUFPLENBQW9CO1FBQzNC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QUFNLE1BQU0sR0FBRyxtQkFBbUIsS0FBSyxtQkFBbUIsQ0FBQyxHQUFHLENBQUM7UUFDL0QsSUFBSSxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDL0IsSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLGtDQUFrQyxDQUFDLENBQUMsQ0FBQztZQUN2RSxPQUFPO1FBQ1QsQ0FBQztRQUNELElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBRWhDLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxzQkFBc0IsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsRUFBRSxJQUFJLENBQUMsT0FBTyxFQUFFLG1CQUFtQixDQUFDLENBQUM7UUFFN0YsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUN2QixJQUFJLENBQUMsMkJBQTJCLEVBQUUsQ0FBQztRQUNuQyxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNuQyxDQUFDO0lBRUQsV0FBVztRQUNULElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDdkIsSUFBSSxDQUFDLFVBQVUsQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUM3QixDQUFDOytHQXhHVSxvQkFBb0I7bUdBQXBCLG9CQUFvQiwyQ0FGcEIsQ0FBQyx3QkFBd0IsRUFBRSw0QkFBNEIsQ0FBQywwQkNuQnJFLG15SEFpR0E7OzRGRDVFYSxvQkFBb0I7a0JBTGhDLFNBQVM7K0JBQ0Usa0JBQWtCLGFBRWpCLENBQUMsd0JBQXdCLEVBQUUsNEJBQTRCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIE9uRGVzdHJveSwgT25Jbml0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBBY3RpdmF0ZWRSb3V0ZSB9IGZyb20gJ0Bhbmd1bGFyL3JvdXRlcic7XG5pbXBvcnQgeyBJTWFuYWdlZE9iamVjdCwgSU9wZXJhdGlvbiwgSVJlc3VsdExpc3QsIE9wZXJhdGlvblNlcnZpY2UgfSBmcm9tICdAYzh5L2NsaWVudCc7XG5pbXBvcnQge1xuICBBbGVydFNlcnZpY2UsXG4gIGdldHRleHQsXG4gIE1hbmFnZWRPYmplY3RSZWFsdGltZVNlcnZpY2UsXG4gIE9wZXJhdGlvblJlYWx0aW1lU2VydmljZVxufSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzJztcbmltcG9ydCB7IGluY2x1ZGVzLCBpc0VtcHR5IH0gZnJvbSAnbG9kYXNoLWVzJztcbmltcG9ydCB7IEJzTW9kYWxSZWYsIEJzTW9kYWxTZXJ2aWNlIH0gZnJvbSAnbmd4LWJvb3RzdHJhcC9tb2RhbCc7XG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QsIE9ic2VydmFibGUsIG9mLCBwaXBlLCBTdWJqZWN0IH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBmaWx0ZXIsIG1hcCwgc3RhcnRXaXRoLCB0YWtlVW50aWwgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBDb21tYW5kVGVtcGxhdGVzQ29tcG9uZW50IH0gZnJvbSAnLi4vY29tbWFuZC10ZW1wbGF0ZXMnO1xuaW1wb3J0IHsgQ29tbWFuZCwgQ29tbWFuZERlbGl2ZXJ5VHlwZSwgRGVsaXZlcnlUeXBlLCBEZXZpY2VTaGVsbFNlcnZpY2UgfSBmcm9tICcuLi9zaGFyZWQnO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdjOHktZGV2aWNlLXNoZWxsJyxcbiAgdGVtcGxhdGVVcmw6ICdzaGVsbC5jb21wb25lbnQuaHRtbCcsXG4gIHByb3ZpZGVyczogW09wZXJhdGlvblJlYWx0aW1lU2VydmljZSwgTWFuYWdlZE9iamVjdFJlYWx0aW1lU2VydmljZV1cbn0pXG5leHBvcnQgY2xhc3MgRGV2aWNlU2hlbGxDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSB7XG4gIGRldmljZTogSU1hbmFnZWRPYmplY3QgPSB0aGlzLnJvdXRlLnNuYXBzaG90LnBhcmVudC5kYXRhLmNvbnRleHREYXRhO1xuICBkZXZpY2UkOiBPYnNlcnZhYmxlPElNYW5hZ2VkT2JqZWN0PiA9IG9mKHt9IGFzIGFueSk7XG4gIGRlbGl2ZXJ5VHlwZXM6IEFycmF5PERlbGl2ZXJ5VHlwZSAmIHsgaXNTdXBwb3J0ZWRCeUNvbW1hbmQ/OiBib29sZWFuIH0+ID0gKFxuICAgIHRoaXMuc2VydmljZS5nZXREZWxpdmVyeVR5cGVzKCkgfHwgW11cbiAgKS5tYXAoZGVsaXZlcnlUeXBlID0+ICh7IC4uLmRlbGl2ZXJ5VHlwZSwgaXNTdXBwb3J0ZWRCeUNvbW1hbmQ6IHRydWUgfSkpO1xuICBjb21tYW5kOiBDb21tYW5kID0ge30gYXMgQ29tbWFuZDtcbiAgc21zRW5hYmxlZCA9IGZhbHNlO1xuICBzZW5kaW5nQ29tbWFuZCQ6IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPiA9IG5ldyBCZWhhdmlvclN1YmplY3QoZmFsc2UpO1xuICBvcGVyYXRpb25zOiBJUmVzdWx0TGlzdDxJT3BlcmF0aW9uPjtcbiAgZmlsdGVyUGlwZSA9IHBpcGUoXG4gICAgbWFwKChvcGVyYXRpb25zOiBJT3BlcmF0aW9uW10pID0+XG4gICAgICAob3BlcmF0aW9ucyB8fCBbXSkuZmlsdGVyKChvcGVyYXRpb246IElPcGVyYXRpb24pID0+ICEhb3BlcmF0aW9uLmM4eV9Db21tYW5kKVxuICAgIClcbiAgKTtcblxuICBleGVjdXRlVmlhTGFiZWwgPSBnZXR0ZXh0KCdFeGVjdXRlIHZpYSAoe3tkZWxpdmVyeVR5cGV9fSknKTtcblxuICBwcml2YXRlIGRlc3Ryb3llZCQ6IFN1YmplY3Q8dm9pZD4gPSBuZXcgU3ViamVjdCgpO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHB1YmxpYyBzZXJ2aWNlOiBEZXZpY2VTaGVsbFNlcnZpY2UsXG4gICAgcHVibGljIG9wZXJhdGlvblJlYWx0aW1lOiBPcGVyYXRpb25SZWFsdGltZVNlcnZpY2UsXG4gICAgcHJpdmF0ZSBtb1JlYWx0aW1lOiBNYW5hZ2VkT2JqZWN0UmVhbHRpbWVTZXJ2aWNlLFxuICAgIHByaXZhdGUgb3BlcmF0aW9uU2VydmljZTogT3BlcmF0aW9uU2VydmljZSxcbiAgICBwcml2YXRlIHJvdXRlOiBBY3RpdmF0ZWRSb3V0ZSxcbiAgICBwcml2YXRlIG1vZGFsU2VydmljZTogQnNNb2RhbFNlcnZpY2UsXG4gICAgcHJpdmF0ZSBhbGVydFNlcnZpY2U6IEFsZXJ0U2VydmljZVxuICApIHt9XG5cbiAgYXN5bmMgbmdPbkluaXQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5zbXNFbmFibGVkID0gYXdhaXQgdGhpcy5zZXJ2aWNlLmNhblNlbmRDb21tYW5kc1ZpYVNNUygpO1xuXG4gICAgdGhpcy5kZXZpY2UkID0gdGhpcy5tb1JlYWx0aW1lLm9uVXBkYXRlJCh0aGlzLmRldmljZS5pZCkucGlwZShzdGFydFdpdGgodGhpcy5kZXZpY2UpKTtcblxuICAgIHRoaXMub3BlcmF0aW9ucyA9IGF3YWl0IHRoaXMub3BlcmF0aW9uU2VydmljZS5saXN0KHtcbiAgICAgIGRldmljZUlkOiB0aGlzLmRldmljZS5pZCxcbiAgICAgIGZyYWdtZW50VHlwZTogJ2M4eV9Db21tYW5kJyxcbiAgICAgIGRhdGVGcm9tOiBuZXcgRGF0ZSgwKS50b0lTT1N0cmluZygpLFxuICAgICAgZGF0ZVRvOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICBwYWdlU2l6ZTogNTAsXG4gICAgICB3aXRoVG90YWxQYWdlczogdHJ1ZSxcbiAgICAgIHJldmVydDogdHJ1ZVxuICAgIH0pO1xuXG4gICAgdGhpcy5vcGVyYXRpb25SZWFsdGltZVxuICAgICAgLm9uQ3JlYXRlJCh0aGlzLmRldmljZS5pZClcbiAgICAgIC5waXBlKHRha2VVbnRpbCh0aGlzLmRlc3Ryb3llZCQpKVxuICAgICAgLnN1YnNjcmliZSgoKSA9PiB0aGlzLmFsZXJ0U2VydmljZS5zdWNjZXNzKGdldHRleHQoJ0NvbW1hbmQgc2VudC4nKSkpO1xuXG4gICAgdGhpcy5vcGVyYXRpb25SZWFsdGltZVxuICAgICAgLm9uVXBkYXRlJCh0aGlzLmRldmljZS5pZClcbiAgICAgIC5waXBlKFxuICAgICAgICBmaWx0ZXIob3AgPT4gb3AuZmFpbHVyZVJlYXNvbiAhPT0gJ09wZXJhdGlvbiBjYW5jZWxsZWQgYnkgdXNlci4nKSwgLy8gYXZvaWQgZHVwbGljYXRlIGFsZXJ0c1xuICAgICAgICB0YWtlVW50aWwodGhpcy5kZXN0cm95ZWQkKVxuICAgICAgKVxuICAgICAgLnN1YnNjcmliZSgoKSA9PiB0aGlzLmFsZXJ0U2VydmljZS5zdWNjZXNzKGdldHRleHQoJ0NvbW1hbmQgc3RhdHVzIHVwZGF0ZWQuJykpKTtcbiAgfVxuXG4gIGdldFByZWRlZmluZWRDb21tYW5kKCkge1xuICAgIGNvbnN0IG1vZGFsOiBCc01vZGFsUmVmPENvbW1hbmRUZW1wbGF0ZXNDb21wb25lbnQ+ID0gdGhpcy5tb2RhbFNlcnZpY2Uuc2hvdyhcbiAgICAgIENvbW1hbmRUZW1wbGF0ZXNDb21wb25lbnQsXG4gICAgICB7XG4gICAgICAgIGFyaWFEZXNjcmliZWRieTogJ21vZGFsLWJvZHknLFxuICAgICAgICBhcmlhTGFiZWxsZWRCeTogJ21vZGFsLXRpdGxlJ1xuICAgICAgfVxuICAgICk7XG4gICAgbW9kYWwuY29udGVudC5vblRlbXBsYXRlU2VsZWN0ZWQucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95ZWQkKSkuc3Vic2NyaWJlKHJlc3VsdCA9PiB7XG4gICAgICB0aGlzLmNvbW1hbmQgPSB7IC4uLihyZXN1bHQgYXMgYW55KS5jb21tYW5kVGVtcGxhdGUgfSBhcyBDb21tYW5kO1xuICAgICAgdGhpcy5kZWxpdmVyeVR5cGVzID0gdGhpcy5kZWxpdmVyeVR5cGVzLm1hcChkZWxpdmVyeVR5cGUgPT4gKHtcbiAgICAgICAgLi4uZGVsaXZlcnlUeXBlLFxuICAgICAgICBpc1N1cHBvcnRlZEJ5Q29tbWFuZDpcbiAgICAgICAgICBpc0VtcHR5KHRoaXMuY29tbWFuZC5kZWxpdmVyeVR5cGVzKSB8fFxuICAgICAgICAgIGluY2x1ZGVzKHRoaXMuY29tbWFuZC5kZWxpdmVyeVR5cGVzLCBkZWxpdmVyeVR5cGUubmFtZSlcbiAgICAgIH0pKTtcbiAgICB9KTtcbiAgfVxuXG4gIHJlc2V0U3VwcG9ydGVkRGVsaXZlcnlUeXBlcygpOiB2b2lkIHtcbiAgICB0aGlzLmRlbGl2ZXJ5VHlwZXMgPSAodGhpcy5zZXJ2aWNlLmdldERlbGl2ZXJ5VHlwZXMoKSB8fCBbXSkubWFwKGRlbGl2ZXJ5VHlwZSA9PiAoe1xuICAgICAgLi4uZGVsaXZlcnlUeXBlLFxuICAgICAgaXNTdXBwb3J0ZWRCeUNvbW1hbmQ6IHRydWVcbiAgICB9KSk7XG4gIH1cblxuICBhc3luYyBleGVjdXRlKGNvbW1hbmREZWxpdmVyeVR5cGUpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB1c2VTTVMgPSBjb21tYW5kRGVsaXZlcnlUeXBlID09PSBDb21tYW5kRGVsaXZlcnlUeXBlLlNNUztcbiAgICBpZiAodXNlU01TICYmICF0aGlzLnNtc0VuYWJsZWQpIHtcbiAgICAgIHRoaXMuYWxlcnRTZXJ2aWNlLndhcm5pbmcoZ2V0dGV4dCgnU01TIHRyYW5zcG9ydCBpcyBub3QgY29uZmlndXJlZC4nKSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuc2VuZGluZ0NvbW1hbmQkLm5leHQodHJ1ZSk7XG5cbiAgICBhd2FpdCB0aGlzLnNlcnZpY2UuY3JlYXRlQ29tbWFuZE9wZXJhdGlvbih0aGlzLmRldmljZS5pZCwgdGhpcy5jb21tYW5kLCBjb21tYW5kRGVsaXZlcnlUeXBlKTtcblxuICAgIHRoaXMuY29tbWFuZC50ZXh0ID0gJyc7XG4gICAgdGhpcy5jb21tYW5kLm5hbWUgPSAnJztcbiAgICB0aGlzLnJlc2V0U3VwcG9ydGVkRGVsaXZlcnlUeXBlcygpO1xuICAgIHRoaXMuc2VuZGluZ0NvbW1hbmQkLm5leHQoZmFsc2UpO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgdGhpcy5kZXN0cm95ZWQkLm5leHQoKTtcbiAgICB0aGlzLmRlc3Ryb3llZCQuY29tcGxldGUoKTtcbiAgfVxufVxuIiwiPGM4eS1hY3Rpb24tYmFyLWl0ZW0gW3BsYWNlbWVudF09XCIncmlnaHQnXCI+XG4gIDxjOHktcmVhbHRpbWUtYnRuIFtzZXJ2aWNlXT1cIm9wZXJhdGlvblJlYWx0aW1lXCI+PC9jOHktcmVhbHRpbWUtYnRuPlxuPC9jOHktYWN0aW9uLWJhci1pdGVtPlxuXG48ZGl2IGNsYXNzPVwiY2FyZCBjb250ZW50LWZ1bGxwYWdlIGQtZ3JpZCBncmlkX19jb2wtLTYtNi0tbWRcIj5cbiAgPGRpdiBjbGFzcz1cImlubmVyLXNjcm9sbCBkLWZsZXggZC1jb2wgYmctbGV2ZWwtMFwiPlxuICAgIDxkaXYgY2xhc3M9XCJjYXJkLWhlYWRlciBsYXJnZS1wYWRkaW5nIHNlcGFyYXRvciBzdGlja3ktdG9wXCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiY2FyZC10aXRsZVwiPlxuICAgICAgICB7eyAnQ29tbWFuZCcgfCB0cmFuc2xhdGUgfX1cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuXG4gICAgPGRpdiBjbGFzcz1cImNhcmQtYmxvY2sgZC1mbGV4IGQtY29sIGZsZXgtZ3JvdyBsYXJnZS1wYWRkaW5nXCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiZC1mbGV4IHAtYi0xNlwiPlxuICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgY2xhc3M9XCJidG4gYnRuLWRlZmF1bHQgYnRuLXNtXCJcbiAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAoY2xpY2spPVwiZ2V0UHJlZGVmaW5lZENvbW1hbmQoKVwiXG4gICAgICAgICAgW3RpdGxlXT1cIidEaXNwbGF5IGEgbGlzdCBvZiBwcmVkZWZpbmVkIGNvbW1hbmRzJyB8IHRyYW5zbGF0ZVwiXG4gICAgICAgICAgZGF0YS1jeT1cInNoZWxsLS1wcmVkZWZpbmVkLWNvbW1hbmRzXCJcbiAgICAgICAgPlxuICAgICAgICAgIHt7ICdQcmVkZWZpbmVkIGNvbW1hbmRzJyB8IHRyYW5zbGF0ZSB9fVxuICAgICAgICA8L2J1dHRvbj5cblxuICAgICAgICA8ZGl2IGNsYXNzPVwibS1sLWF1dG9cIj5cbiAgICAgICAgICA8ZGV2aWNlLXN0YXR1cyBbbW9dPVwiZGV2aWNlJCB8IGFzeW5jXCI+PC9kZXZpY2Utc3RhdHVzPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgICAgPHRleHRhcmVhXG4gICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiJ0NvbW1hbmRzJyB8IHRyYW5zbGF0ZVwiXG4gICAgICAgIGNsYXNzPVwiZm9ybS1jb250cm9sIGlubmVyLXNjcm9sbCBmbGV4LWdyb3cgYmctbGV2ZWwtMiB0ZXh0LW1vbm9zcGFjZVwiXG4gICAgICAgIFsobmdNb2RlbCldPVwiY29tbWFuZC50ZXh0XCJcbiAgICAgICAgZGF0YS1jeT1cInNoZWxsLWNvbXBvbmVudC0tY29tbWFuZHNcIlxuICAgICAgICAobmdNb2RlbENoYW5nZSk9XCIkZXZlbnQgfHwgcmVzZXRTdXBwb3J0ZWREZWxpdmVyeVR5cGVzKClcIlxuICAgICAgICBwbGFjZWhvbGRlcj1cInt7ICdBZGQgY29tbWFuZHMgb3IgdXNlIHByZWRlZmluZWQgY29tbWFuZHMgYWJvdmUuJyB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICA+PC90ZXh0YXJlYT5cbiAgICA8L2Rpdj5cblxuICAgIDxkaXYgY2xhc3M9XCJjYXJkLWZvb3RlciBsYXJnZS1wYWRkaW5nIHNlcGFyYXRvclwiPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdGb3I9XCJsZXQgZGVsaXZlcnlUeXBlIG9mIGRlbGl2ZXJ5VHlwZXNcIj5cbiAgICAgICAgPGJ1dHRvblxuICAgICAgICAgIGNsYXNzPVwiYnRuIGJ0bi1wcmltYXJ5XCJcbiAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAqbmdJZj1cImRlbGl2ZXJ5VHlwZS5pc1N1cHBvcnRlZEJ5Q29tbWFuZFwiXG4gICAgICAgICAgW2Rpc2FibGVkXT1cIiFjb21tYW5kPy50ZXh0IHx8IChzZW5kaW5nQ29tbWFuZCQgfCBhc3luYylcIlxuICAgICAgICAgIChjbGljayk9XCJleGVjdXRlKGRlbGl2ZXJ5VHlwZS5uYW1lKVwiXG4gICAgICAgID5cbiAgICAgICAgICA8c3BhblxuICAgICAgICAgICAgW3RpdGxlXT1cIlxuICAgICAgICAgICAgICBkZWxpdmVyeVR5cGUuZGVmYXVsdFxuICAgICAgICAgICAgICAgID8gKCdFeGVjdXRlJyB8IHRyYW5zbGF0ZSlcbiAgICAgICAgICAgICAgICA6IChleGVjdXRlVmlhTGFiZWwgfCB0cmFuc2xhdGU6IHsgZGVsaXZlcnlUeXBlOiBkZWxpdmVyeVR5cGUubmFtZSB9KVxuICAgICAgICAgICAgXCJcbiAgICAgICAgICA+XG4gICAgICAgICAgICB7e1xuICAgICAgICAgICAgICBkZWxpdmVyeVR5cGUuZGVmYXVsdFxuICAgICAgICAgICAgICAgID8gKCdFeGVjdXRlJyB8IHRyYW5zbGF0ZSlcbiAgICAgICAgICAgICAgICA6IChleGVjdXRlVmlhTGFiZWwgfCB0cmFuc2xhdGU6IHsgZGVsaXZlcnlUeXBlOiBkZWxpdmVyeVR5cGUubmFtZSB9KVxuICAgICAgICAgICAgfX1cbiAgICAgICAgICA8L3NwYW4+XG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuICA8ZGl2IGNsYXNzPVwiaW5uZXItc2Nyb2xsIGJnLWxldmVsLTFcIj5cbiAgICA8ZGl2IGNsYXNzPVwiY2FyZC1oZWFkZXIgbGFyZ2UtcGFkZGluZyBzZXBhcmF0b3Igc3RpY2t5LXRvcFwiPlxuICAgICAgPGRpdiBjbGFzcz1cImNhcmQtdGl0bGVcIj5cbiAgICAgICAge3sgJ09wZXJhdGlvbnMnIHwgdHJhbnNsYXRlIH19XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwiY2FyZC1ibG9jayBsYXJnZS1wYWRkaW5nXCI+XG4gICAgICA8Yzh5LW9wZXJhdGlvbnMtdGltZWxpbmVcbiAgICAgICAgW29wZXJhdGlvbnNdPVwib3BlcmF0aW9uc1wiXG4gICAgICAgIFtzb3VyY2VJZF09XCJkZXZpY2UuaWRcIlxuICAgICAgICBbZmlsdGVyUGlwZV09XCJmaWx0ZXJQaXBlXCJcbiAgICAgICAgW2JvZHlUZW1wbGF0ZV09XCJ0aW1lbGluZUl0ZW1Cb2R5XCJcbiAgICAgICAgW2Zvb3RlclRlbXBsYXRlc109XCJbdGltZWxpbmVJdGVtRm9vdGVyXVwiXG4gICAgICAgIFtwcm9wZXJ0aWVzVG9IaWRlXT1cIlsnYzh5X0NvbW1hbmQnXVwiXG4gICAgICA+PC9jOHktb3BlcmF0aW9ucy10aW1lbGluZT5cbiAgICAgIDxuZy10ZW1wbGF0ZSAjdGltZWxpbmVJdGVtQm9keSBsZXQtb3BlcmF0aW9uPlxuICAgICAgICA8c21hbGw+e3sgb3BlcmF0aW9uLmM4eV9Db21tYW5kPy50ZXh0IHx8IG9wZXJhdGlvbi5kZXNjcmlwdGlvbiB9fTwvc21hbGw+XG4gICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgPG5nLXRlbXBsYXRlICN0aW1lbGluZUl0ZW1Gb290ZXIgbGV0LW9wZXJhdGlvbj5cbiAgICAgICAgPGRpdiAqbmdJZj1cIm9wZXJhdGlvbi5jOHlfQ29tbWFuZD8udGV4dFwiPlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJsZWdlbmQgZm9ybS1ibG9ja1wiIHRyYW5zbGF0ZT5Db21tYW5kPC9kaXY+XG4gICAgICAgICAgPCEtLSBLZWVwIHRoaXMgaW4gYSBzaW5nbGUgbGluZSBzaW5jZSBgcHJlLXRleHRgIHdpbGwgcHJlc2VydmUgYWxsIHdoaXRlc3BhY2VzIGluIHRoZSBgcHJlYCB0YWcuLS0+XG4gICAgICAgICAgPHByZSBjbGFzcz1cInAtOCB0ZXh0LXByZVwiPjxjb2RlPnt7b3BlcmF0aW9uLmM4eV9Db21tYW5kLnRleHR9fTwvY29kZT48L3ByZT5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxkaXYgKm5nSWY9XCJvcGVyYXRpb24uYzh5X0NvbW1hbmQ/LnJlc3VsdFwiPlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJsZWdlbmQgZm9ybS1ibG9ja1wiIHRyYW5zbGF0ZT5SZXNwb25zZTwvZGl2PlxuICAgICAgICAgIDwhLS0gS2VlcCB0aGlzIGluIGEgc2luZ2xlIGxpbmUgc2luY2UgYHByZS10ZXh0YCB3aWxsIHByZXNlcnZlIGFsbCB3aGl0ZXNwYWNlcyBpbiB0aGUgYHByZWAgdGFnLi0tPlxuICAgICAgICAgIDxwcmUgY2xhc3M9XCJwLTggdGV4dC1wcmVcIj48Y29kZT57e29wZXJhdGlvbi5jOHlfQ29tbWFuZC5yZXN1bHR9fTwvY29kZT48L3ByZT5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L25nLXRlbXBsYXRlPlxuICAgIDwvZGl2PlxuICA8L2Rpdj5cbjwvZGl2PlxuIl19