Untitled

mail@pastecode.io avatar
unknown
plain_text
a year ago
42 kB
6
Indexable
Never
import {
  AfterViewInit,
  Component,
  ElementRef,
  OnInit,
  ViewChild,
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import * as moment from 'moment';
import { ExternalReadtimeseriesService } from 'src/app/api/services';
import { dashboardsDetails } from 'src/app/constants/dashboard';
import {
  AxisType,
  ChartConfiguration,
  ChartData,
  DateRangePickerConfiguration,
  FormControlConfiguration,
  FormSelectOption,
  intervalType,
  LegendPosition,
  LineTypeCrossHair,
  ModeTypeZoom,
  PaginatorConfiguration,
  ShapeLegend,
  TabConfiguration,
  TabGroupConfiguration,
  TableConfiguration,
  TypeChart,
} from 'widgets';
import { ChartService } from 'src/app/services/chart.service';
import { DashboardDetailDto } from 'src/app/models/dashboard/dashboard-detail-dto';
import { catchError, forkJoin, of } from 'rxjs';
import { TelemetryStruct } from 'src/app/models/dashboard/telemetry_struct';
import { TranslateService } from '@ngx-translate/core';
import { MatAccordion } from '@angular/material/expansion';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import {
  formsInputConfiguration,
  formsName,
} from 'src/app/constants/formConfiguration';
import { FormService } from 'src/app/services/utilities/form/form.service';
import { DeviceForTelemetry } from 'src/app/models/dashboard/device-for-telemetry';
import { TableService } from 'src/app/services/utilities/table/table.service';
import { tableConfigurationName } from 'src/app/constants/tableConfiguration';
import { TsValuePair } from 'src/app/api/models';
@Component({
  selector: 'app-dashboard-detail',
  templateUrl: './dashboard-detail.component.html',
  styleUrls: ['./dashboard-detail.component.scss'],
})
export class DashboardDetailComponent implements OnInit, AfterViewInit {
  Object = Object;
  moment = moment;
  loadingData = false;

  //dashboard and charts
  dashboard?: DashboardDetailDto;
  _chartsData?: Map<
    string,
    | {
        chartConfiguration?: ChartConfiguration;
        telemetries?: ChartData;
        telemetryKeys?: string[];
        allDeviceNames?: string[];
        form?: FormGroup;
        formInputs?: FormControlConfiguration[];
        predictiveForm?: FormGroup;
        predictiveFormInputs?: FormControlConfiguration[];
        expanded?: boolean;
        currentTableDevice?: DeviceForTelemetry;
        tableData?: {
          devices: {
            data: {
              telemetryTs?: number;
              telemetryValue?: number;
              anomalyValue?: number;
              predicitedTelemetryValue?: number;
              predicitedFutureTelemetryValue?: number;
            }[];
            currentPageData: {
              telemetryTs?: number;
              telemetryValue?: number;
              anomalyValue?: number;
              predicitedTelemetryValue?: number;
              predicitedFutureTelemetryValue?: number;
            }[];
            device: DeviceForTelemetry;
            pageNumber: number;
            pageSize: number;
            totalNumber: number;
            configurationPaginator: PaginatorConfiguration;
          }[];
          form: FormGroup;
          formInputs: FormControlConfiguration[];
          unit: string;
          configurationTabGroup: TabGroupConfiguration;
        };
      }
    | undefined
  >;
  configurationTable!: TableConfiguration;
  configurationPaginator!: PaginatorConfiguration;
  configurationTabGroup: TabGroupConfiguration = { configuration: [] };
  tableDataMap?: Map<
    string,
    {
      devices: {
        data: {
          telemetryTs?: number;
          telemetryValue?: number;
          anomalyValue?: number;
        }[];
        currentPageData: {
          telemetryTs?: number;
          telemetryValue?: number;
          anomalyValue?: number;
        }[];
        device: DeviceForTelemetry;
        pageNumber: number;
        pageSize: number;
        totalNumber: number;
        configurationPaginator: PaginatorConfiguration;
      }[];
      form: FormGroup;
      formInputs: FormControlConfiguration[];
      unit: string;
      configurationTabGroup: TabGroupConfiguration;
    }
  >;
  get chartsData() {
    return this._chartsData;
  }
  set chartsData(value) {
    this.loadingData = false;
    this._chartsData = value;
  }
  get telemetryKeyList(): string[] {
    return this.telemetriesList.map((e) => e.value);
  }
  //// accordion
  @ViewChild(MatAccordion) accordion!: MatAccordion;

  /// multiselect
  telemetriesList: FormSelectOption[] = [];
  ficTL!: FormControlConfiguration[];
  formTL!: FormGroup;
  showSelection: string[] = [];

  //// date
  rangeSelected: { startDate: Date; endDate: Date } = {
    startDate: new Date(),
    endDate: new Date(),
  };
  aggregationInterval?: any;
  dateControl = new FormControl(null);
  @ViewChild('datePickerRange') dateRangePicker?: any;

  get dateRangePickerConfiguration(): DateRangePickerConfiguration {
    return {
      placeholder: 'Seleziona un range',
      format: 'dd/MM/yyyy HH:mm',
      control: this.dateControl,
      locale: 'it',
      ranges: [
        {
          label: 'Ultime 24h',
          start: new Date(
            new Date().getFullYear(),
            new Date().getMonth(),
            new Date().getDate(),
            new Date().getHours() - 24,
            new Date().getMinutes()
          ),
          end: new Date(),
        },
        {
          label: 'Ultima settimana',
          start: new Date(
            new Date().getFullYear(),
            new Date().getMonth(),
            new Date().getDate() - new Date().getDay() - 6,
            new Date().getHours(),
            new Date().getMinutes()
          ),
          end: new Date(),
        },
        {
          label: 'Ultimo Mese',
          start: new Date(new Date().setMonth(new Date().getMonth() - 1)),
          end: new Date(),
        },
        {
          label: 'Ultimo Anno',
          start: new Date(new Date().setMonth(new Date().getMonth() - 12)),
          end: new Date(),
        },
      ],
    };
  }
  get inputDateRangePicker(): HTMLInputElement | null {
    return this.elementRef.nativeElement.getElementsByClassName(
      'e-input e-lib e-keyboard'
    ) &&
      this.elementRef.nativeElement.getElementsByClassName(
        'e-input e-lib e-keyboard'
      ).length
      ? this.elementRef.nativeElement.getElementsByClassName(
          'e-input e-lib e-keyboard'
        )[0]
      : null;
  }
  get openedTelemetryKeys(): string[] {
    const keys: string[] = [];
    this.chartsData?.forEach((value, key) => {
      if (value?.expanded) {
        keys.push(key);
      }
    });
    return keys;
  }
  get anomalyTelemetryMap() {
    if (this.chartsData) {
      const temp = new Map(this.chartsData);
      Array.from(temp.keys()).forEach((key) => {
        if (!key.includes('anomaly')) temp.delete(key);
      });
      return temp;
    } else return undefined;
  }
  get predictableTelemetryKeys(): string[] {
    const predictableTelemetryKeys: string[] = [];
    if (this.dashboard && this.dashboard.telemetry_device) {
      this.dashboard.telemetry_device.forEach((telemetryInfo) => {
        if (
          telemetryInfo &&
          telemetryInfo.telemetry &&
          telemetryInfo.telemetry.name.includes('predicted')
        ) {
          const splittedByUnderscore = telemetryInfo.telemetry.name.split('_');
          const telemetryNameParts = splittedByUnderscore.filter(
            (val, index) =>
              ![
                'predicted',
                'avg',
                'f3600s',
                'f60s',
                'ARIMA',
                'LSTM',
                'prev',
                'future',
                '60min',
                '10min',
              ].includes(val)
          );
          let telemetryKey = '';
          telemetryNameParts.forEach((val, index) => {
            if (index === 0) {
              telemetryKey += val;
            } else {
              telemetryKey += '_' + val;
            }
          });
          if (!predictableTelemetryKeys.find((key) => key === telemetryKey)) {
            predictableTelemetryKeys.push(telemetryKey);
          }
        }
      });
    }
    return predictableTelemetryKeys;
  }

  constructor(
    private route: ActivatedRoute,
    private router: Router,
    private readTelemetryService: ExternalReadtimeseriesService,
    private chartService: ChartService,
    private translateService: TranslateService,
    private formService: FormService,
    private elementRef: ElementRef,
    private tableService: TableService
  ) {
    const dashboardId = this.route.snapshot.paramMap.get('id');
    if (dashboardId) {
      // MOCK
      const dashboard = dashboardsDetails.find(
        (dashbaord) => dashbaord.id.toString() === dashboardId
      );
      if (dashboard) this.dashboard = dashboard;
    }
    const tableConfiguration = this.tableService.buildTable(
      tableConfigurationName.ANOMALY_DASHBOARD
    );
    this.configurationTable = tableConfiguration.configurationTable;
    this.configurationPaginator = tableConfiguration.configurationPaginator;
  }

  ngOnInit(): void {
    this.getChartsData();
  }

  //// CHART CONF

  getChartsData() {
    const telemetryMap: any = {};
    if (
      this.dashboard &&
      this.dashboard.telemetry_device &&
      this.dashboard.telemetry_device.length
    ) {
      this.loadingData = true;
      this.dashboard.telemetry_device.forEach((telemetryDevice) => {
        telemetryMap[telemetryDevice.telemetry!.name!] = {
          expanded: false,
          formInputs: JSON.parse(
            JSON.stringify(
              this.isTelemetryPredictable(telemetryDevice.telemetry!.name!)
                ? formsInputConfiguration[formsName.DASHBOARD_FILTERS_FORM]
                : formsInputConfiguration[
                    formsName.DASHBOARD_FILTERS_FORM_NO_PREDICTABLE
                  ]
            )
          ),
          predictiveFormInputs: JSON.parse(
            JSON.stringify(
              formsInputConfiguration[
                formsName.DASHBOARD_PREDICTIVE_FILTERS_FORM
              ]
            )
          ),
          chartConfiguration: this.getChartConfiguration(
            telemetryDevice.telemetry!
          ),
          telemetryKeys: telemetryDevice.devices?.map((dev) => dev.name),
          allDeviceNames: telemetryDevice.devices?.map((dev) => dev.name),
          telemetries: {},
        };
      });
      Object.keys(telemetryMap).forEach((key) => {
        telemetryMap[key]['form'] = this.formService.buildForm(
          telemetryMap[key]['formInputs']
        );
        telemetryMap[key]['predictiveForm'] = this.formService.buildForm(
          telemetryMap[key]['predictiveFormInputs']
        );
        const options = telemetryMap[key]['predictiveFormInputs'].find(
          (input: FormControlConfiguration) =>
            input.name === 'predictiveTimeValue'
        )?.options;

        const optionToDisable = options.find(
          (option: FormSelectOption) => option.value === 'prev_10min'
        );
        if (optionToDisable) optionToDisable.disabled = true;

        telemetryMap[key]['form']
          .get('predicted')
          ?.valueChanges.subscribe((val: boolean) => {
            if (val) {
              // TODO: ottimizzare  codice ripetuto predictiveinfo
              const predictiveInfo: {
                predictiveCaptureInterval: string;
                predictiveModel: string;
                predictiveTimeValue: string;
              } = telemetryMap[key]['predictiveForm'].value;
              let predictedTelemetry = `predicted_avg_${predictiveInfo.predictiveCaptureInterval}_${key}_${predictiveInfo.predictiveModel}`;
              if (predictiveInfo.predictiveTimeValue !== 'present') {
                predictedTelemetry += `_${predictiveInfo.predictiveTimeValue}`;
              }
              this.getTelemetriesDataByTelemetryKey(key, predictedTelemetry);
            } else {
              const tempTelemetries = {
                ...this.chartsData!.get(key)?.telemetries,
              };
              this.chartsData!.set(key, {
                ...this.chartsData!.get(key),
                telemetryKeys: this.chartsData!.get(key)?.telemetryKeys?.filter(
                  (tempKey: string) => !tempKey.includes('predicted')
                ),
                telemetries: {},
              });
              this.getChartDataByTelemetryKey(key, val);
            }
          });
        // resetto terzo input quando cambia il secondo e disabilito opzione non possibile
        telemetryMap[key]['predictiveForm'].controls[
          'predictiveCaptureInterval'
        ].valueChanges.subscribe((val: any) => {
          if (val) {
            const options = telemetryMap[key]['predictiveFormInputs'].find(
              (input: FormControlConfiguration) =>
                input.name === 'predictiveTimeValue'
            )?.options;
            if (options) {
              if (val === 'f60s') {
                const optionToDisable = options.find(
                  (option: FormSelectOption) => option.value === 'prev_60min'
                );
                const option = options.find(
                  (option: FormSelectOption) => option.value === 'prev_10min'
                );
                if (
                  telemetryMap[key]['predictiveForm'].controls[
                    'predictiveTimeValue'
                  ].value === 'prev_60min'
                ) {
                  telemetryMap[key]['predictiveForm'].controls[
                    'predictiveTimeValue'
                  ].setValue('prev_10min');
                }
                if (optionToDisable) optionToDisable.disabled = true;
                if (option) option.disabled = false;
              } else if (val === 'f3600s') {
                const optionToDisable = options.find(
                  (option: FormSelectOption) => option.value === 'prev_10min'
                );
                const option = options.find(
                  (option: FormSelectOption) => option.value === 'prev_60min'
                );
                if (
                  telemetryMap[key]['predictiveForm'].controls[
                    'predictiveTimeValue'
                  ].value === 'prev_10min'
                ) {
                  telemetryMap[key]['predictiveForm'].controls[
                    'predictiveTimeValue'
                  ].setValue('prev_60min');
                }
                if (optionToDisable) optionToDisable.disabled = true;
                if (option) option.disabled = false;
              }
            }
          }
        });
        telemetryMap[key]['predictiveForm']?.valueChanges.subscribe(
          (val: any) => {
            this.getTelemetriesDataByTelemetryKey(
              key,
              this.getPredictedTelemetryByKey(key),
              true
            );
          }
        );

        const telemetryInfo = this.dashboard?.telemetry_device?.find(
          (elem) => elem.telemetry?.name === key
        );
        if (telemetryInfo && telemetryInfo.telemetry && telemetryInfo.devices) {
          const deviceInput = telemetryMap[key]['formInputs'].find(
            (input: any) => input.name === 'device'
          );
          if (deviceInput) {
            deviceInput.options = telemetryInfo.devices.map((device) => {
              return {
                value: device.uuid,
                translate: device.name,
                tooltip: device.description,
              };
            });
            // set default value
            const defaultVal = [...deviceInput.options];
            defaultVal.length >= deviceInput.maxLength
              ? telemetryMap[key]['form']
                  .get(deviceInput.name)
                  .setValue(
                    defaultVal.filter(
                      (option, index) => index < deviceInput.maxLength
                    )
                  )
              : telemetryMap[key]['form']
                  .get(deviceInput.name)
                  .setValue(defaultVal);
            telemetryMap[key]['telemetryKeys'] = defaultVal.map(
              (value) => value.translate
            );
            // Quando si cambia insieme di device triggera nuova chiamata per il grafico
            telemetryMap[key]['form']
              .get(deviceInput.name)
              .valueChanges.subscribe((val: any) => {
                this.chartsData!.set(key, {
                  ...this.chartsData!.get(key),
                  telemetryKeys: val.map((elem: any) => elem.translate),
                  telemetries: {},
                });
                this.getChartDataByTelemetryKey(
                  key,
                  this.chartsData!.get(key)?.form?.get('predicted')?.value
                );
              });
            deviceInput.compareWithFunc = (a: any, b: any) => {
              // funzione di comparazione valore settato programmmaticamente con opzioni disponibili
              return (
                a === b.value || a === b || a.value === b || a.value === b.value
              );
            };
          }
        }
      });
      this.chartsData = new Map(Object.entries(telemetryMap));

      // setto expanded di default a true per i rimi due elementi
      Array.from(this.chartsData.keys()).forEach((key, index) => {
        if (index < 2) {
          this.chartsData!.set(key, {
            ...this.chartsData!.get(key),
            expanded: true,
          });
        }
      });

      this.setTableDataMap();
      this.buildFormTelFilter();
    }
  }

  getChartDataByTelemetryKey(key: string, getPredicted: boolean) {
    this.getChartDataByTelemetryKeyObservable(key)
      ?.pipe(catchError((error: any) => of(error)))
      .subscribe({
        next: (res: any) => {
          let tempTelemtries: any = {}; // variabile di appoggio in cui verrà salvata la struttura: {nome_device:  dati telemetria parsed}
          Object.keys(res).forEach((deviceName) => {
            let temp: any = {}; // variabile di appoggio in cui salvo la struttura { nome_device: dati telemtria non parsed}
            let temp1: any = {}; // variabile di appoggio in cui salvo i dati di telemetria parsed per il lib-chart component
            if (res[deviceName].telemetry) {
              temp[deviceName] = res[deviceName].telemetry[key];
              temp1 = this.chartService.parseData([deviceName], temp);
              tempTelemtries[deviceName] = temp1[deviceName];
            }
          });
          this.chartsData!.set(key, {
            ...this.chartsData!.get(key),
            telemetries: tempTelemtries,
          });
          this.getAnomalyData(key);
          if (getPredicted) {
            const predictiveInfo: {
              predictiveCaptureInterval: string;
              predictiveModel: string;
              predictiveTimeValue: string;
            } = this.chartsData!.get(key)?.predictiveForm?.value;
            let predictedTelemetry = `predicted_avg_${predictiveInfo.predictiveCaptureInterval}_${key}_${predictiveInfo.predictiveModel}`;
            if (predictiveInfo.predictiveTimeValue !== 'present') {
              predictedTelemetry += `_${predictiveInfo.predictiveTimeValue}`;
            }
            this.getTelemetriesDataByTelemetryKey(key, predictedTelemetry);
          }
        },
        error: (err: any) => {
          console.error(err);
        },
      });
  }

  getChartDataByTelemetryKeyObservable(telemetryKey: string) {
    const deviceTelemetry = this.dashboard?.telemetry_device?.find(
      (elem) => elem.telemetry?.name === telemetryKey
    );
    if (deviceTelemetry) {
      let devices = deviceTelemetry.devices;
      if (this.chartsData) {
        devices = devices?.filter((dev) =>
          this.chartsData!.get(telemetryKey)?.telemetryKeys?.find(
            (devKey) => dev.name === devKey
          )
        );
      }
      if (devices && devices.length) {
        const devicesTelemetryMap: any = {};
        devices.forEach((device) => {
          devicesTelemetryMap[device.name!] =
            this.readTelemetryService.getEntityHistoricalTimeseriesUsingPost({
              uuidDevice: device.uuid!,
              body: {
                startTs: moment(this.rangeSelected.startDate).valueOf(),
                endTs: moment(this.rangeSelected.endDate).valueOf(),
                telemetryKeys: [telemetryKey],
                aggregationInterval:
                  moment(this.rangeSelected.endDate).valueOf() -
                  moment(this.rangeSelected.startDate).valueOf(),
              },
            });
        });
        return forkJoin(devicesTelemetryMap);
      } else return null;
    } else return null;
  }

  getTelemetriesDataByTelemetryKey(
    telemetryKey: string,
    predictedTelemetryKey: string,
    changedPredictedInfo?: boolean
  ) {
    this.getTelemetriesDataByTelemetryKeyObservable(predictedTelemetryKey)
      ?.pipe(catchError((error: any) => of(error)))
      .subscribe({
        next: (res: any) => {
          let tempTelemtries: any = {};
          let tempTelemetryKeys: string[] = []; // variabile di appoggio in cui verrà salvata la struttura: {nome_device:  dati telemetria parsed}
          Object.keys(res).forEach((deviceName) => {
            let temp: any = {}; // variabile di appoggio in cui salvo la struttura { nome_device: dati telemtria non parsed}
            let temp1: any = {}; // variabile di appoggio in cui salvo i dati di telemetria parsed per il lib-chart component
            if (
              res[deviceName].telemetry &&
              this.chartsData!.get(telemetryKey)
                ?.form?.get('device')
                ?.value?.find((dev: FormSelectOption) =>
                  deviceName.includes(dev.translate)
                )
            ) {
              temp[deviceName] =
                res[deviceName].telemetry[predictedTelemetryKey];
              temp1 = this.chartService.parseData([deviceName], temp);
              tempTelemtries[deviceName] = temp1[deviceName];
              tempTelemetryKeys.push(deviceName);
            }
          });
          const tempChartsData = { ...this.chartsData?.get(telemetryKey) };
          // rimuovo eventuali telemetrie di predizione precedenti
          tempChartsData.telemetryKeys = tempChartsData.telemetryKeys?.filter(
            (key) => !key.includes('predicted')
          );
          Object.keys(tempChartsData.telemetries!).forEach((key) => {
            if (key.includes('predicted'))
              delete tempChartsData.telemetries![key];
          });
          this.chartsData!.set(telemetryKey, undefined);

          setTimeout(() => {
            this.chartsData!.set(telemetryKey, {
              ...tempChartsData,
              telemetries: { ...tempChartsData.telemetries, ...tempTelemtries },
              telemetryKeys:
                tempChartsData.telemetryKeys?.concat(tempTelemetryKeys),
            });
          }, 50);
        },
        error: (err: any) => {
          console.error(err);
        },
      });
  }

  getTelemetriesDataByTelemetryKeyObservable(telemetryKey: string) {
    const deviceTelemetry = this.dashboard?.telemetry_device?.find(
      (elem) => elem.telemetry?.name === telemetryKey
    );
    if (deviceTelemetry) {
      let devices = deviceTelemetry.devices;
      if (devices && devices.length) {
        const devicesTelemetryMap: any = {};
        devices.forEach((device) => {
          devicesTelemetryMap[telemetryKey + '_' + device.name!] =
            this.readTelemetryService.getEntityHistoricalTimeseriesUsingPost({
              uuidDevice: device.uuid!,
              body: {
                startTs: moment(this.rangeSelected.startDate).valueOf(),
                endTs: moment(this.rangeSelected.endDate).valueOf(),
                telemetryKeys: [telemetryKey],
                aggregationInterval:
                  moment(this.rangeSelected.endDate).valueOf() -
                  moment(this.rangeSelected.startDate).valueOf(),
              },
            });
        });
        return forkJoin(devicesTelemetryMap);
      } else return null;
    } else return null;
  }

  getChartConfiguration(telemetryInfo: TelemetryStruct) {
    return new ChartConfiguration(
      telemetryInfo.name,
      TypeChart.LINE,
      null,
      {
        valueType: AxisType.DateTime,
        intervalType: intervalType.AUTO,
      },
      {
        title: this.translateService.instant(telemetryInfo.name),
        labelFormat: `{value} ${this.translateService.instant(
          telemetryInfo.unit
        )}`,
      },
      {
        enable: true,
        shared: true,
        format: '${series.name} : ${point.x} : ${point.y}',
      },
      {
        enable: true,
        lineType: LineTypeCrossHair.VERTICAL,
      },
      null,
      {
        enableMouseWheelZooming: true,
        enablePinchZooming: true,
        enableSelectionZooming: true,
        enablePan: true,
        mode: ModeTypeZoom.X,
      },
      {
        visible: true,
        position: LegendPosition.BOTTOM,
        shape: ShapeLegend.HORIZONTAL_LINE,
      },
      [
        '#4273db',
        '#42db6f',
        '#802c1f',
        '#d942db',
        '#d2ff00',
        '#d942db',
        '#ffc4bd',
        '#ff533d',
      ]
    );
  }

  getPredictedInfoByTelemetryKey(key: string): {
    predictiveCaptureInterval: string;
    predictiveModel: string;
    predictiveTimeValue: string;
  } {
    return this.chartsData!.get(key)?.predictiveForm?.value;
  }

  getPredictedTelemetryByKey(key: string): string {
    const predictiveInfo = this.getPredictedInfoByTelemetryKey(key);
    let predictedTelemetry = `predicted_avg_${predictiveInfo.predictiveCaptureInterval}_${key}_${predictiveInfo.predictiveModel}`;
    if (predictiveInfo.predictiveTimeValue !== 'present') {
      predictedTelemetry += `_${predictiveInfo.predictiveTimeValue}`;
    }
    return predictedTelemetry;
  }

  getAnomalyInfo(key: string):
    | {
        predictiveCaptureInterval: string;
        predictiveModel: string;
      }
    | undefined {
    return this.chartsData?.get(key)?.tableData?.form?.value;
  }

  getAnomalyTelemetryKeys(key: string): string[] {
    return [
      this.getAnomalyTelemetryKey(key),
      this.getAnomalyTelemetryValueKey(key),
      this.getAnomalyTelemetryPredictedValueKey(key),
      this.getAnomalyTelemetryPredictedFutureValueKey(key),
    ];
  }

  getAnomalyTelemetryKey(key: string): string {
    const anomalyInfo = this.getAnomalyInfo(key);
    if (anomalyInfo) {
      return `anomaly_avg_${anomalyInfo.predictiveCaptureInterval}_${key}_${anomalyInfo.predictiveModel}`;
    } else return '';
  }

  getAnomalyTelemetryValueKey(key: string): string {
    const anomalyTelemetryKey = this.getAnomalyTelemetryKey(key);
    if (anomalyTelemetryKey) {
      const splittedByUnderscoreValues = anomalyTelemetryKey.split('_');
      if (splittedByUnderscoreValues.length >= 3) {
        return (
          splittedByUnderscoreValues[1] +
          '_' +
          splittedByUnderscoreValues[2] +
          '_' +
          key
        );
      } else return '';
    } else return '';
  }

  getAnomalyTelemetryPredictedValueKey(key: string): string {
    return `predicted_avg_${
      this.getAnomalyInfo(key)?.predictiveCaptureInterval
    }_${key}_${this.getAnomalyInfo(key)?.predictiveModel}`;
  }

  getAnomalyTelemetryPredictedFutureValueKey(key: string): string {
    return `predicted_avg_${
      this.getAnomalyInfo(key)?.predictiveCaptureInterval
    }_${key}_${this.getAnomalyInfo(key)?.predictiveModel}_future_60min`;
  }

  getCurrentTableDataByTelemetryKey(key: string) {
    if (
      this.chartsData &&
      this.chartsData.get(key) &&
      this.chartsData.get(key)!.tableData &&
      this.chartsData.get(key)!.tableData!.devices
    ) {
      return this.chartsData!.get(key)!.tableData!.devices.find(
        (elem) =>
          elem.device.uuid ===
          this.chartsData!.get(key)!.currentTableDevice?.uuid
      );
    } else return undefined;
  }

  isTelemetryPredictable(key: string) {
    return this.predictableTelemetryKeys.find(
      (telemetryKey) => telemetryKey === key
    );
  }

  onTelemetryOpened(key: string) {
    this.chartsData!.set(key, { ...this.chartsData!.get(key), expanded: true });
    this.getChartDataByTelemetryKeyObservable(key)
      ?.pipe(catchError((error: any) => of(error)))
      .subscribe({
        next: (res: any) => {
          let tempTelemetries: any = {}; // variabile di appoggio in cui verrà salvata la struttura: {nome_device:  dati telemetria parsed}
          Object.keys(res).forEach((deviceName) => {
            let temp: any = {}; // variabile di appoggio in cui salvo la struttura { nome_device: dati telemtria non parsed}
            let temp1: any = {}; // variabile di appoggio in cui salvo i dati di telemetria parsed per il lib-chart component
            if (res[deviceName].telemetry) {
              temp[deviceName] = res[deviceName].telemetry[key];
              temp1 = this.chartService.parseData([deviceName], temp);
              tempTelemetries[deviceName] = temp1[deviceName];
            } else {
              // se non ci sono telemetrie per il device
              tempTelemetries[deviceName] = {
                data: [],
              };
            }
          });
          this.chartsData!.set(key, {
            ...this.chartsData!.get(key),
            telemetries: tempTelemetries,
          });
          this.getAnomalyData(key);
        },
        error: (err: any) => {
          console.error(err);
        },
      });
  }

  onTelemetryClosed(key: string) {
    this.chartsData!.set(key, {
      ...this.chartsData!.get(key),
      expanded: false,
    });
  }

  setTableDataMap() {
    let mapTemp: any = {};
    if (this.dashboard && this.chartsData && this.anomalyTelemetryMap) {
      Array.from(this.chartsData.keys()).forEach((key) => {
        if (
          !key.includes('predicted') &&
          !key.includes('avg') &&
          Array.from(this.anomalyTelemetryMap!.keys()).find((telemetryKey) =>
            telemetryKey.includes(key)
          )
        ) {
          if (this.dashboard && this.dashboard.telemetry_device) {
            const telemetryDevice = this.dashboard.telemetry_device.find(
              (elem) => elem.telemetry?.name === key
            );
            if (telemetryDevice && telemetryDevice.devices) {
              mapTemp[key] = {
                unit: telemetryDevice.telemetry?.unit,
                devices: telemetryDevice.devices.map((device) => {
                  return {
                    device,
                    data: [],
                    totalNumber: 0,
                    pageSize: 10,
                    pageNumber: 0,
                    currentPageData: [],
                    configurationPaginator: { ...this.configurationPaginator },
                  };
                }),
                configurationTabGroup: {
                  configuration: telemetryDevice.devices.map((device) => {
                    const tabConf: TabConfiguration = {
                      id: device.uuid ?? '',
                      key: device.name ?? '',
                      tab_label: {
                        label: device.name ?? '',
                        tooltip: device.description ?? '',
                      },
                      component: ['anomalyTable'],
                    };
                    return tabConf;
                  }),
                },
                formInputs: JSON.parse(
                  JSON.stringify(
                    formsInputConfiguration[formsName.DASHBOARD_ANOMALY_FORM]
                  )
                ),
              };
              mapTemp[key]['form'] = this.formService.buildForm(
                mapTemp[key]['formInputs']
              );
            }
          }
        }
      });
      this.tableDataMap = new Map(Object.entries(mapTemp));

      this.tableDataMap.forEach((tableValue, tableKey) => {
        tableValue.form.valueChanges.subscribe(
          (value: {
            predictiveModel: string;
            predictiveCaptureInterval: string;
          }) => {
            this.getAnomalyData(tableKey);
          }
        );
      });
      this.chartsData.forEach((chartDataValue, chartDataKey) => {
        if (this.tableDataMap!.has(chartDataKey)) {
          this.chartsData?.set(chartDataKey, {
            ...chartDataValue,
            tableData: this.tableDataMap!.get(chartDataKey),
            currentTableDevice:
              this.tableDataMap!.get(chartDataKey)?.devices[0].device,
          });
        }
      });
      // inizializzo dati tabella anomalie
      this.chartsData.forEach((chartDataValue, chartDataKey) => {
        if (chartDataValue?.expanded && chartDataValue.tableData) {
          this.getAnomalyData(chartDataKey);
        }
      });
    }
  }

  onAnomalyTabChangedByTelemetryKey(
    telemetryKey: string,
    tab: TabConfiguration
  ) {
    if (this.chartsData?.get(telemetryKey)) {
      this.chartsData!.get(telemetryKey)!.currentTableDevice = {
        name: tab.key,
        uuid: tab.id,
      };
    }
    this.getAnomalyData(telemetryKey);
  }

  loadTableAnomalyData(event: any, key: string) {
    const tableData = this.getCurrentTableDataByTelemetryKey(key);
    if (tableData) {
      tableData!.totalNumber = tableData!.data.length;
      tableData!.pageNumber = event.pageCount;
      tableData!.pageSize = event.pageSize;
      tableData!.currentPageData = tableData!.data.slice(
        0 + tableData!.pageNumber * tableData!.pageSize,
        0 + tableData!.pageNumber * tableData!.pageSize + tableData!.pageSize
      );
    }
  }

  goBack() {
    this.router.navigateByUrl('dashboard');
  }

  //// SELECTION CONF
  buildFormTelFilter() {
    [...this.chartsData!.keys()].forEach((e) => {
      // escludo telemetrie di predizone, anomalia e mediaa
      if (
        !e.includes('predicted') &&
        !e.includes('anomaly') &&
        !e.includes('avg')
      ) {
        this.telemetriesList.push({ value: e, translate: e });
      }
    });
    this.ficTL = [
      {
        name: 'TelemetryList',
        type: 'select_multiple',
        default: '',
        validators: [Validators.nullValidator],
        width: 'col-12',
        options: this.telemetriesList,
        translate: {
          label: 'Filtro Telemetrie',
        },
      },
    ];
    this.formTL = this.formService.buildForm(this.ficTL);
    this.formTL?.controls['TelemetryList'].valueChanges.subscribe(
      (value: string[]) => {
        this.showSelection = value;
      }
    );
    /// preseleziona i valori
    this.formTL?.get('TelemetryList')?.setValue([...this.chartsData!.keys()]);
  }

  ngAfterViewInit(): void {
    // elemento input del date range picker
    if (this.inputDateRangePicker) this.inputDateRangePicker.readOnly = true; //setto a readonly per poterne cambiare il valore solo da popup
    // inizializzo (ultime 24h)
    this.dateControl.setValue([
      new Date(
        new Date().getFullYear(),
        new Date().getMonth(),
        new Date().getDate(),
        new Date().getHours() - 24,
        new Date().getMinutes()
      ),
      new Date(),
    ]);
    this.rangeSelected.startDate = this.dateControl.value[0];
    this.rangeSelected.endDate = this.dateControl.value[1];
    this.dateRangePicker.activeIndex = 0;
    this.dateControl.valueChanges.subscribe((val: Date[]) => {
      this.rangeSelected.startDate = val[0];
      this.rangeSelected.endDate = val[1];
    });
    // apro di default i primi due accordion
    if (this.accordion && this.accordion._headers) {
      Array.from(this.accordion._headers).forEach((accordionHeader, index) => {
        if (index < 2) {
          accordionHeader.panel.open();
        }
      });
    }
  }

  onClose(event: any) {
    if (this.dateRangePicker.activeIndex <= 3) {
      setTimeout(() => {
        this.dateRangePicker.value =
          this.dateRangePickerConfiguration.ranges.map(
            (range: { start: any; end: any }) => {
              return [range.start, range.end];
            }
          )[this.dateRangePicker.activeIndex];
        this.openedTelemetryKeys.forEach((key) => {
          this.getChartDataByTelemetryKey(
            key,
            this.chartsData?.get(key)?.form?.get('predicted')?.value
          );
        });
      }, 5);
    }
  }

  getAnomalyData(key: string) {
    const currentTableDevice =
      this.chartsData &&
      this.chartsData.get(key) &&
      this.chartsData.get(key)!.currentTableDevice
        ? this.chartsData.get(key)!.currentTableDevice
        : undefined;
    const tableData = this.getCurrentTableDataByTelemetryKey(key);
    if (currentTableDevice) {
      this.readTelemetryService
        .getEntityHistoricalTimeseriesUsingPost({
          uuidDevice: currentTableDevice.uuid!,
          body: {
            startTs: moment(this.rangeSelected.startDate).valueOf(),
            endTs: moment(this.rangeSelected.endDate).valueOf(),
            telemetryKeys: this.getAnomalyTelemetryKeys(key),
            aggregationInterval:
              moment(this.rangeSelected.endDate).valueOf() -
              moment(this.rangeSelected.startDate).valueOf(),
          },
        })
        .subscribe({
          next: (res) => {
            if (res?.telemetry) {
              if (tableData) {
                tableData.data = this.getAnomalyTableData(res.telemetry!);
                // info paginazione
                tableData!.totalNumber = tableData!.data.length;
                tableData!.currentPageData = tableData!.data.slice(
                  0 + tableData!.pageNumber * tableData!.pageSize,
                  0 +
                    tableData!.pageNumber * tableData!.pageSize +
                    tableData!.pageSize
                );

                tableData!.configurationPaginator.length =
                  tableData!.totalNumber;
              }
            } else {
              if (tableData) {
                tableData!.data = [];
                tableData!.currentPageData = [];
                tableData!.configurationPaginator.length = 0;
              }
            }
          },
          error: (err) => {
            console.error(err);
          },
        });
    }
  }
  /* prendo la chiave di telemetria per la quale il numero di dati in risposta è minore,
    a partire da questa scrorro tutte le altre per trovare i timestamp in comune e poi uso una mappa dove ad ogni timestamp
    associo i valori di tutte e 4 le telemetrie. A partire da questa mappa poi mi creo l'aray di dati che andrà a popolare la
    tabella di anomalie*/
  getAnomalyTableData(telemetryMap: { [key: string]: TsValuePair[] }): {
    anomalyValue?: number;
    telemetryPredictedValue?: number;
    telemetryPredictedFutureValue?: number;
    telemetryValue?: number;
    telemetryTs: number;
  }[] {
    let data: {
      anomalyValue?: number;
      telemetryPredictedValue?: number;
      telemetryPredictedFutureValue?: number;
      telemetryValue?: number;
      telemetryTs: number;
    }[] = [];
    const telemeryKeys = Object.keys(telemetryMap);
    let shorterKey = telemeryKeys[0];
    telemeryKeys.forEach((valueKey) => {
      if (telemetryMap[valueKey].length < telemetryMap[shorterKey].length) {
        shorterKey = valueKey;
      }
    });
    const mergedElementsByTS: any = {};
    telemetryMap[shorterKey].forEach((value) => {
      mergedElementsByTS[value.telemetryTs!] = {};
      mergedElementsByTS[value.telemetryTs!][shorterKey] = value.telemetryValue;
      telemeryKeys
        .filter((valueKey) => valueKey !== shorterKey)
        .forEach((val) => {
          const found = telemetryMap[val].find((elem) => {
            return elem.telemetryTs === value.telemetryTs;
          });
          if (found) {
            mergedElementsByTS[value.telemetryTs!][val] = found.telemetryValue;
          }
        });
    });
    Object.keys(mergedElementsByTS).forEach((_key) => {
      const valueToAdd: any = {
        telemetryTs: Number(_key),
      };
      Object.keys(mergedElementsByTS[_key]).forEach((innerKey) => {
        valueToAdd[innerKey] = mergedElementsByTS[_key][innerKey];
        data.push(valueToAdd);
      });
    });
    data = data.map((elem: any) => {
      const mappedValue: {
        anomalyValue?: number;
        telemetryPredictedValue?: number;
        telemetryPredictedFutureValue?: number;
        telemetryValue?: number;
        telemetryTs: number;
      } = { telemetryTs: elem.telemetryTs };
      Object.keys(elem).forEach((objectKey) => {
        if (objectKey !== 'telemetryTs') {
          if (objectKey.includes('anomaly')) {
            // anomalyValue
            mappedValue.anomalyValue = elem[objectKey];
          } else if (
            objectKey.includes('predicted') &&
            objectKey.includes('future_60min')
          ) {
            // telemetryPredictedFutureValue
            mappedValue.telemetryPredictedFutureValue = elem[objectKey];
          } else if (objectKey.includes('predicted')) {
            //  telemetryPredictedValue
            mappedValue.telemetryPredictedValue = elem[objectKey];
          } else {
            // telemetryValue
            mappedValue.telemetryValue = elem[objectKey];
          }
        }
      });
      return mappedValue;
    });
    return data;
  }

  updateDashboard() {
    if (this.chartsData) {
      Array.from(this.chartsData!.keys())
        .filter((key) => {
          return this.chartsData!.get(key)?.expanded;
        })
        .forEach((key) => {
          this.getChartDataByTelemetryKey(
            key,
            this.chartsData?.get(key)?.form?.get('predicted')?.value &&
              this.isTelemetryPredictable(key)
          );
        });
    }

    //window.location.reload();
  }
}