@c8y/ngx-components 1023.14.102 → 1023.14.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/echart/index.d.ts CHANGED
@@ -293,14 +293,17 @@ declare class ChartsComponent implements OnChanges, OnInit, OnDestroy {
293
293
  debounce<T extends (...args: any[]) => void>(func: T, wait: number): (...args: Parameters<T>) => void;
294
294
  /**
295
295
  * Toggles the visibility of a series in the chart based on the provided datapoint.
296
+ * Uses explicit legendSelect/legendUnSelect based on __active state.
296
297
  * @param datapoint - The datapoint to toggle visibility for.
297
298
  */
298
299
  toggleDatapointSeriesVisibility(datapoint: DatapointsGraphKPIDetails): void;
299
300
  /**
300
- * Toggles the visibility of a series in the chart based on the provided alarm or event.
301
+ * Toggles the visibility of alarm/event series in the chart.
302
+ * Uses explicit legendSelect/legendUnSelect based on __hidden state.
303
+ * Targets specific device's series using type + deviceId.
301
304
  * @param alarmOrEvent - The alarm or event to toggle visibility for.
302
305
  */
303
- toggleAlarmEventSeriesVisibility(alarmOrEvent: AlarmDetailsExtended | EventDetailsExtended): void;
306
+ toggleAlarmEventSeriesVisibility(alarmOrEvent: AlarmOrEventExtended): void;
304
307
  private getDefaultChartOptions;
305
308
  private getMarkedAreaData;
306
309
  private getMarkedLineData;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sources":["../../echart/models/datapoints-graph-widget.model.ts","../../echart/models/chart.model.ts","../../echart/models/svg-icons.model.ts","../../echart/charts.component.ts","../../echart/services/chart-alarms.service.ts","../../echart/services/chart-events.service.ts","../../echart/services/chart-helpers.service.ts","../../echart/select-aggregated-datapoint/select-aggregated-datapoint.component.ts"],"sourcesContent":[null,null,null,null,null,null,null,null],"names":[],"mappings":";;;;;;;;;;;;;;AAmBM;;;;AAKA;AACJ;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;;;;;;AAOA;;AAEA;AACA;AACA;AACA;;;AAGA;;;;;;;;;AAUI;;;;;;;;;;AAUJ;;AAGI;;AAEJ;AACA;;AAGI;;;AAIN;;AAEG;;AAGG;AAIN;AACE;AACA;AACD;AAED;AACM;;AAEL;AAED;;;;AAIM;;;;;;;AAQL;AAED;;;;;AAUE;;;;;AAGD;;AAGC;;AAEA;;AAEE;;AAEF;;;AACD;;;;;;;AAQC;AACA;AACD;AAEK;AACA;AAEA;AAEA;AAEN;;;;AAKA;;;;;;;;;;;;;AAcM;AACA;AAEA;;;;AAKA;;;;;;;;;;;AAYL;AAED;;;;;;;;AAUE;AACA;AACD;;AC9MD;;;;;;AAMG;AACH;AACE;;;;AAIA;;;AACD;;;AAIA;;;AAIA;AAEK;AACA;AACJ;AACE;AACD;AACF;;AAGC;AACA;AACD;AAED;AACE;AACA;AACA;AACD;;AC3CD;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AAED;;;;;;;;;;;ACwDA;AA4BE;;AAEA;;;;;;;;AAQA;;;;;;AAMK;AACK;AAGA;AACA;AACA;AACA;AACA;AACA;;AAEV;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AAgDA;;;;;AA2IuB;;;AAA4B;AAiDnD;AAOA;AAIA;AAwBA;AA6BA;AAWA;;;AAGG;AACH;AAcA;;;AAGG;AACH;AAmBA;AAeA;AA0BA;;AA8DA;;;AA8MA;AA0DA;AA0CA;AAkBA;AAWA;AAYA;;;AA6BD;;AC97BD;AAEc;AAAQ;AAEpB;;;;;AAKG;AACG;;;AAqCP;;AC/CD;AAEc;AAAQ;AAEpB;;;;;AAKG;AACG;;;AAuBP;;AChCD;AAEE;;;;AAIG;;;;;;;;;;;AAcH;AAcA;;;AAGD;;ACtBD;;AAqBW;AACC;AAEV;;AAGA;;;AAiBA;;;AAgBD;;;"}
1
+ {"version":3,"file":"index.d.ts","sources":["../../echart/models/datapoints-graph-widget.model.ts","../../echart/models/chart.model.ts","../../echart/models/svg-icons.model.ts","../../echart/charts.component.ts","../../echart/services/chart-alarms.service.ts","../../echart/services/chart-events.service.ts","../../echart/services/chart-helpers.service.ts","../../echart/select-aggregated-datapoint/select-aggregated-datapoint.component.ts"],"sourcesContent":[null,null,null,null,null,null,null,null],"names":[],"mappings":";;;;;;;;;;;;;;AAmBM;;;;AAKA;AACJ;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;;;;;;AAOA;;AAEA;AACA;AACA;AACA;;;AAGA;;;;;;;;;AAUI;;;;;;;;;;AAUJ;;AAGI;;AAEJ;AACA;;AAGI;;;AAIN;;AAEG;;AAGG;AAIN;AACE;AACA;AACD;AAED;AACM;;AAEL;AAED;;;;AAIM;;;;;;;AAQL;AAED;;;;;AAUE;;;;;AAGD;;AAGC;;AAEA;;AAEE;;AAEF;;;AACD;;;;;;;AAQC;AACA;AACD;AAEK;AACA;AAEA;AAEA;AAEN;;;;AAKA;;;;;;;;;;;;;AAcM;AACA;AAEA;;;;AAKA;;;;;;;;;;;AAYL;AAED;;;;;;;;AAUE;AACA;AACD;;AC9MD;;;;;;AAMG;AACH;AACE;;;;AAIA;;;AACD;;;AAIA;;;AAIA;AAEK;AACA;AACJ;AACE;AACD;AACF;;AAGC;AACA;AACD;AAED;AACE;AACA;AACA;AACD;;AC3CD;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AAED;;;;;;;;;;;ACwDA;AA4BE;;AAEA;;;;;;;;AAQA;;;;;;AAMK;AACK;AAGA;AACA;AACA;AACA;AACA;AACA;;AAEV;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AAgDA;;;;;AA2IuB;;;AAA4B;AAiDnD;AAOA;AAIA;AAwBA;AA6BA;AAWA;;;;AAIG;AACH;AAcA;;;;;AAKG;AACH;AAgCA;AAeA;AA0BA;;AA8DA;;;AA8MA;AA0DA;AA0CA;AAkBA;AAWA;AAYA;;;AA6BD;;AC98BD;AAEc;AAAQ;AAEpB;;;;;AAKG;AACG;;;AAqCP;;AC/CD;AAEc;AAAQ;AAEpB;;;;;AAKG;AACG;;;AAuBP;;AChCD;AAEE;;;;AAIG;;;;;;;;;;;AAcH;AAcA;;;AAGD;;ACtBD;;AAqBW;AACC;AAEV;;AAGA;;;AAiBA;;;AAgBD;;;"}
@@ -432,6 +432,7 @@ class EchartsOptionsService {
432
432
  this.severityLabelPipe = severityLabelPipe;
433
433
  this.translate = translate;
434
434
  this.router = router;
435
+ this.AGGREGATED_SERIES_TYPE = 'aggr';
435
436
  this.TOOLTIP_WIDTH = 300;
436
437
  }
437
438
  getChartOptions(datapointsWithValues, timeRange, showSplitLines, events, alarms, displayOptions, selectedTimeRange, aggregatedDatapoint, sliderZoomUsed = false) {
@@ -654,11 +655,11 @@ class EchartsOptionsService {
654
655
  const renderType = aggregatedDatapoint?.renderType || 'min';
655
656
  const idx = 0;
656
657
  if (renderType === 'area') {
657
- series.push(this.getSingleSeries(aggregatedDatapoint, 'min', idx, true, displayOptions, 'aggr'));
658
- series.push(this.getSingleSeries(aggregatedDatapoint, 'max', idx, true, displayOptions, 'aggr'));
658
+ series.push(this.getSingleSeries(aggregatedDatapoint, 'min', idx, true, displayOptions, this.AGGREGATED_SERIES_TYPE));
659
+ series.push(this.getSingleSeries(aggregatedDatapoint, 'max', idx, true, displayOptions, this.AGGREGATED_SERIES_TYPE));
659
660
  }
660
661
  else {
661
- series.push(this.getSingleSeries(aggregatedDatapoint, renderType, idx, false, displayOptions, 'aggr'));
662
+ series.push(this.getSingleSeries(aggregatedDatapoint, renderType, idx, false, displayOptions, this.AGGREGATED_SERIES_TYPE));
662
663
  }
663
664
  series.forEach((s) => {
664
665
  s.datapointId = 'aggregated';
@@ -736,8 +737,8 @@ class EchartsOptionsService {
736
737
  }, []);
737
738
  // Construct series with markPoint
738
739
  const seriesWithMarkPoint = {
739
- id: `${type}/${dp.__target?.id}+${id ? id : ''}-markPoint`,
740
- name: `${type}-markPoint`,
740
+ id: `${type}/${dp.__target?.id}${id ? '+' + id : ''}-markPoint`,
741
+ name: `${type}/${dp.__target?.id}-markPoint`,
741
742
  typeOfSeries: itemType,
742
743
  data: mainData,
743
744
  isDpTemplateSelected,
@@ -754,8 +755,8 @@ class EchartsOptionsService {
754
755
  const markLineData = this.createMarkLine(itemsOfType, itemType);
755
756
  // Construct series with markLine
756
757
  const seriesWithMarkLine = {
757
- id: `${type}/${dp.__target?.id}+${id ? id : ''}-markLine`,
758
- name: `${type}-markLine`,
758
+ id: `${type}/${dp.__target?.id}${id ? '+' + id : ''}-markLine`,
759
+ name: `${type}/${dp.__target?.id}-markLine`,
759
760
  typeOfSeries: itemType,
760
761
  isDpTemplateSelected,
761
762
  data: mainData,
@@ -1393,6 +1394,13 @@ class EchartsOptionsService {
1393
1394
  }
1394
1395
  getSingleSeries(dp, renderType, idx, isMinMaxChart = false, displayOptions, seriesType = '') {
1395
1396
  const datapointId = dp.__target?.id + dp.fragment + dp.series;
1397
+ const isAggr = seriesType === this.AGGREGATED_SERIES_TYPE;
1398
+ const data = isAggr && Object.keys(dp.values).length <= 1
1399
+ ? []
1400
+ : Object.entries(dp.values).map(([dateString, values]) => [
1401
+ dateString,
1402
+ values[0][renderType]
1403
+ ]);
1396
1404
  return {
1397
1405
  datapointId,
1398
1406
  datapointUnit: dp.unit || '',
@@ -1401,11 +1409,8 @@ class EchartsOptionsService {
1401
1409
  ? `${datapointId}/${renderType}${seriesType}`
1402
1410
  : `${datapointId}${seriesType}`,
1403
1411
  name: `${dp.label} (${dp.__target?.['name']})`,
1404
- // datapointLabel used to proper display of tooltip
1405
1412
  datapointLabel: dp.label || '',
1406
- data: Object.entries(dp.values).map(([dateString, values]) => {
1407
- return [dateString, values[0][renderType]];
1408
- }),
1413
+ data,
1409
1414
  ...(displayOptions.forceMergeDatapoints ? {} : { yAxisIndex: idx }),
1410
1415
  ...this.chartTypesService.getSeriesOptions(dp, isMinMaxChart, renderType)
1411
1416
  };
@@ -1511,9 +1516,11 @@ class ChartRealtimeService {
1511
1516
  this.pendingAlarmsOrEvents = new Map();
1512
1517
  this.currentAlarms = [];
1513
1518
  this.currentEvents = [];
1519
+ this.activeDatapoints = [];
1514
1520
  }
1515
1521
  startRealtime(echartsInstance, datapoints, timeRange, datapointOutOfSyncCallback, timeRangeChangedCallback, alarmOrEventConfig = [], displayOptions, alarms, events) {
1516
1522
  this.echartsInstance = echartsInstance;
1523
+ this.activeDatapoints = datapoints;
1517
1524
  this.currentTimeRange = {
1518
1525
  dateFrom: new Date(timeRange.dateFrom),
1519
1526
  dateTo: new Date(timeRange.dateTo)
@@ -1602,7 +1609,9 @@ class ChartRealtimeService {
1602
1609
  });
1603
1610
  this.realtimeSubscriptionAlarmsEvents = allAlarmsAndEvents$
1604
1611
  .pipe(map(alarmOrEvent => {
1605
- const foundAlarmOrEvent = activeAlarmsOrEvents.find(aOrE => aOrE.filters.type === alarmOrEvent.type);
1612
+ // Match by both type AND device source
1613
+ const foundAlarmOrEvent = activeAlarmsOrEvents.find(aOrE => aOrE.filters.type === alarmOrEvent.type &&
1614
+ aOrE.__target?.id === alarmOrEvent.source?.id);
1606
1615
  if (foundAlarmOrEvent) {
1607
1616
  alarmOrEvent['color'] = foundAlarmOrEvent.color;
1608
1617
  alarmOrEvent['selectedDatapoint'] = foundAlarmOrEvent.selectedDatapoint;
@@ -1670,6 +1679,7 @@ class ChartRealtimeService {
1670
1679
  seriesDataToUpdate.get(datapoint)?.push(measurement);
1671
1680
  });
1672
1681
  let allDataSeries = this.echartsInstance?.getOption()['series'];
1682
+ // Process measurements for each datapoint
1673
1683
  seriesDataToUpdate.forEach((measurements, datapoint) => {
1674
1684
  const newValues = measurements.map(m => [
1675
1685
  m.time,
@@ -1683,46 +1693,80 @@ class ChartRealtimeService {
1683
1693
  const seriesDataToUpdate = seriesMatchingDatapoint['data'];
1684
1694
  seriesDataToUpdate.push(...newValues);
1685
1695
  seriesMatchingDatapoint['data'] = this.removeValuesBeforeTimeRange(seriesMatchingDatapoint);
1686
- if (alarmOrEvent) {
1696
+ this.checkForValuesAfterTimeRange(seriesMatchingDatapoint['data'], datapoint, datapointOutOfSyncCallback);
1697
+ });
1698
+ // Process alarm/event OUTSIDE the measurement loop.
1699
+ // Previously this was inside seriesDataToUpdate.forEach(), which meant alarms/events
1700
+ // were only processed when measurements existed for that datapoint. This caused
1701
+ // alarms/events to not appear on the chart when no measurements arrived in the interval.
1702
+ if (alarmOrEvent) {
1703
+ // The alarm config's selectedDatapoint is a partial object { target, fragment, series },
1704
+ // not a full DatapointsGraphKPIDetails. We need to find the matching full datapoint.
1705
+ const selectedDp = alarmOrEvent['selectedDatapoint'];
1706
+ // Find the correct datapoint to position this alarm/event on the chart:
1707
+ // 1. Use selectedDatapoint from alarm config (user-configured)
1708
+ // 2. Match by alarm's source device ID
1709
+ // 3. Fall back to first datapoint
1710
+ let datapoint = selectedDp
1711
+ ? this.activeDatapoints.find(dp => dp.__target?.id === selectedDp.target &&
1712
+ dp.fragment === selectedDp.fragment &&
1713
+ dp.series === selectedDp.series)
1714
+ : undefined;
1715
+ if (!datapoint && alarmOrEvent['source']?.id) {
1716
+ datapoint = this.activeDatapoints.find(dp => dp.__target?.id === alarmOrEvent['source'].id);
1717
+ }
1718
+ if (!datapoint) {
1719
+ datapoint = this.activeDatapoints[0];
1720
+ }
1721
+ if (datapoint) {
1722
+ const datapointId = datapoint.__target?.id + datapoint.fragment + datapoint.series;
1723
+ const matchingSeries = allDataSeries.find(s => s['datapointId'] === datapointId);
1724
+ const seriesData = matchingSeries ? matchingSeries['data'] : [];
1725
+ // Convert ECharts series data format to the format expected by getAlarmOrEventSeries.
1726
+ // ECharts: [[timestamp, value], ...] → Expected: { [timestamp]: [{min, max}] }
1727
+ const values = {};
1728
+ seriesData.forEach(([timestamp, value]) => {
1729
+ values[timestamp] = [{ min: value, max: value }];
1730
+ });
1687
1731
  const renderType = datapoint.renderType || 'min';
1688
1732
  const dp = {
1689
1733
  ...datapoint,
1690
- values: seriesMatchingDatapoint['data']
1734
+ values
1691
1735
  };
1692
1736
  if (isEvent(alarmOrEvent)) {
1693
- // if event series with the same id already exists, return
1694
- const eventExists = allDataSeries.some(series => series['data'].some(data => data[0] === alarmOrEvent.creationTime));
1737
+ // Check if event already exists on chart to avoid duplicates
1738
+ const eventExists = allDataSeries.some(series => series['data']?.some(data => data[0] === alarmOrEvent.creationTime));
1695
1739
  if (eventExists) {
1696
1740
  return;
1697
1741
  }
1742
+ // For events, pass creationTime as unique id to create unique series per event.
1743
+ // This allows multiple events of the same type/device to coexist on the chart.
1744
+ // Series ID will be: eventType/deviceId+creationTime-markPoint
1698
1745
  const newEventSeries = this.echartsOptionsService.getAlarmOrEventSeries(dp, renderType, false, [alarmOrEvent], 'event', displayOptions, alarmOrEvent.creationTime, null, true);
1699
1746
  allDataSeries.push(...newEventSeries);
1700
1747
  }
1701
1748
  else if (isAlarm(alarmOrEvent)) {
1702
- const alarmExists = allDataSeries.some((series) => {
1703
- const seriesData = series['data'];
1704
- return seriesData.some((data) => data[0] === alarmOrEvent.creationTime);
1749
+ // For alarms, remove existing series for this alarm type + device before adding.
1750
+ // This ensures alarm status changes (e.g., ACTIVE → CLEARED) are reflected.
1751
+ // Series ID format: ${alarmType}/${deviceId}-markPoint/markLine
1752
+ const alarm = alarmOrEvent;
1753
+ const alarmType = alarm.type;
1754
+ const datapointTargetId = datapoint.__target?.id;
1755
+ allDataSeries = allDataSeries.filter((series) => {
1756
+ const seriesId = series['id'];
1757
+ return !seriesId?.startsWith(`${alarmType}/${datapointTargetId}`);
1705
1758
  });
1706
- if (alarmExists) {
1707
- const alarmSeries = allDataSeries.filter((series) => {
1708
- const seriesData = series['data'];
1709
- return seriesData.some((data) => data[0] === alarmOrEvent.creationTime);
1710
- });
1711
- // remove all matching alarm series which are in the array
1712
- alarmSeries.forEach(series => {
1713
- allDataSeries = allDataSeries.filter(s => s['id'] !== series['id']);
1714
- });
1715
- const newAlarmSeries = this.echartsOptionsService.getAlarmOrEventSeries(dp, renderType, false, [alarmOrEvent], 'alarm', displayOptions, alarmOrEvent.creationTime, null, true);
1716
- allDataSeries.push(...newAlarmSeries);
1717
- }
1718
- else {
1719
- const newAlarmSeries = this.echartsOptionsService.getAlarmOrEventSeries(dp, renderType, false, [alarmOrEvent], 'alarm', displayOptions, alarmOrEvent.id, null, true);
1720
- allDataSeries.push(...newAlarmSeries);
1721
- }
1759
+ // For alarms, pass null as id - we want one series per alarm type/device.
1760
+ // Series ID will be: alarmType/deviceId-markPoint (no unique suffix).
1761
+ // We already removed the old series above, so this replaces it.
1762
+ const newAlarmSeries = this.echartsOptionsService.getAlarmOrEventSeries(dp, renderType, false, [alarm], 'alarm', displayOptions, null, null, true);
1763
+ allDataSeries.push(...newAlarmSeries);
1722
1764
  }
1723
1765
  }
1724
- this.checkForValuesAfterTimeRange(seriesMatchingDatapoint['data'], datapoint, datapointOutOfSyncCallback);
1725
- });
1766
+ }
1767
+ // Use replaceMerge for series to ensure removed alarm series don't persist.
1768
+ // Without this, ECharts merges the new series array with existing ones,
1769
+ // which can leave stale alarm/event series on the chart.
1726
1770
  this.echartsInstance?.setOption({
1727
1771
  dataZoom: [
1728
1772
  {
@@ -1735,7 +1779,7 @@ class ChartRealtimeService {
1735
1779
  max: this.currentTimeRange?.dateTo
1736
1780
  },
1737
1781
  series: allDataSeries
1738
- });
1782
+ }, { replaceMerge: ['series'] });
1739
1783
  }
1740
1784
  /**
1741
1785
  * Detects if a datapoint has measurements with future timestamps (out-of-sync).
@@ -2247,6 +2291,7 @@ class ChartsComponent {
2247
2291
  }
2248
2292
  /**
2249
2293
  * Toggles the visibility of a series in the chart based on the provided datapoint.
2294
+ * Uses explicit legendSelect/legendUnSelect based on __active state.
2250
2295
  * @param datapoint - The datapoint to toggle visibility for.
2251
2296
  */
2252
2297
  toggleDatapointSeriesVisibility(datapoint) {
@@ -2254,26 +2299,40 @@ class ChartsComponent {
2254
2299
  const seriesToUpdate = series.find(s => s.datapointId === `${datapoint.__target?.id}${datapoint.fragment}${datapoint.series}`);
2255
2300
  if (seriesToUpdate) {
2256
2301
  this.echartsInstance.dispatchAction({
2257
- type: 'legendToggleSelect',
2302
+ type: datapoint.__active ? 'legendSelect' : 'legendUnSelect',
2258
2303
  name: seriesToUpdate.name
2259
2304
  });
2260
2305
  }
2261
2306
  }
2262
2307
  /**
2263
- * Toggles the visibility of a series in the chart based on the provided alarm or event.
2308
+ * Toggles the visibility of alarm/event series in the chart.
2309
+ * Uses explicit legendSelect/legendUnSelect based on __hidden state.
2310
+ * Targets specific device's series using type + deviceId.
2264
2311
  * @param alarmOrEvent - The alarm or event to toggle visibility for.
2265
2312
  */
2266
2313
  toggleAlarmEventSeriesVisibility(alarmOrEvent) {
2267
- const series = this.echartsInstance.getOption().series;
2268
- const seriesToUpdate = series.filter(s => s.name === `${alarmOrEvent.label}-markPoint` || s.name === `${alarmOrEvent.label}-markLine`);
2269
- if (seriesToUpdate) {
2270
- seriesToUpdate.forEach(s => {
2271
- this.echartsInstance.dispatchAction({
2272
- type: 'legendToggleSelect',
2273
- name: s.name
2274
- });
2275
- });
2314
+ if (!this.echartsInstance) {
2315
+ return;
2276
2316
  }
2317
+ const options = this.echartsInstance.getOption();
2318
+ const allSeries = options.series;
2319
+ if (!allSeries || allSeries.length === 0) {
2320
+ return;
2321
+ }
2322
+ const type = alarmOrEvent.filters.type;
2323
+ const deviceId = alarmOrEvent.__target?.id;
2324
+ const actionType = alarmOrEvent.__hidden ? 'legendUnSelect' : 'legendSelect';
2325
+ // Series names are: ${type}/${deviceId}-markPoint and ${type}/${deviceId}-markLine
2326
+ const markPointName = `${type}/${deviceId}-markPoint`;
2327
+ const markLineName = `${type}/${deviceId}-markLine`;
2328
+ // Find series matching this specific alarm type + device
2329
+ const matchingSeries = allSeries.filter(s => s.name === markPointName || s.name === markLineName);
2330
+ matchingSeries.forEach(series => {
2331
+ this.echartsInstance.dispatchAction({
2332
+ type: actionType,
2333
+ name: series.name
2334
+ });
2335
+ });
2277
2336
  }
2278
2337
  getDefaultChartOptions() {
2279
2338
  return {