@serve.zone/dcrouter 17.1.8 → 17.2.1

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.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '17.1.8',
6
+ version: '17.2.1',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }
@@ -19,6 +19,8 @@ export interface INetworkState {
19
19
  ipIntelligence: interfaces.data.IIpIntelligenceRecord[];
20
20
  domainActivity: interfaces.data.IDomainActivity[];
21
21
  throughputHistory: Array<{ timestamp: number; in: number; out: number }>;
22
+ frontendConnectionHistory: interfaces.data.IProtocolConnectionHistoryPoint[];
23
+ backendConnectionHistory: interfaces.data.IProtocolConnectionHistoryPoint[];
22
24
  requestsPerSecond: number;
23
25
  requestsTotal: number;
24
26
  backends: interfaces.data.IBackendInfo[];
@@ -43,6 +45,8 @@ export const networkStatePart = await appState.getStatePart<INetworkState>(
43
45
  ipIntelligence: [],
44
46
  domainActivity: [],
45
47
  throughputHistory: [],
48
+ frontendConnectionHistory: [],
49
+ backendConnectionHistory: [],
46
50
  requestsPerSecond: 0,
47
51
  requestsTotal: 0,
48
52
  backends: [],
@@ -150,6 +154,8 @@ export const fetchNetworkStatsAction = networkStatePart.createAction(async (stat
150
154
  ipIntelligence: currentState.ipIntelligence,
151
155
  domainActivity: networkStatsResponse.domainActivity || [],
152
156
  throughputHistory: networkStatsResponse.throughputHistory || [],
157
+ frontendConnectionHistory: networkStatsResponse.frontendConnectionHistory || [],
158
+ backendConnectionHistory: networkStatsResponse.backendConnectionHistory || [],
153
159
  requestsPerSecond: networkStatsResponse.requestsPerSecond || 0,
154
160
  requestsTotal: networkStatsResponse.requestsTotal || 0,
155
161
  backends: networkStatsResponse.backends || [],
@@ -340,6 +340,8 @@ async function dispatchCombinedRefreshActionInner() {
340
340
  throughputByIP: network.topEndpoints.map(e => ({ ip: e.endpoint, in: e.bandwidth?.in || 0, out: e.bandwidth?.out || 0 })),
341
341
  domainActivity: network.domainActivity || [],
342
342
  throughputHistory: network.throughputHistory || [],
343
+ frontendConnectionHistory: network.frontendConnectionHistory || [],
344
+ backendConnectionHistory: network.backendConnectionHistory || [],
343
345
  requestsPerSecond: network.requestsPerSecond || 0,
344
346
  requestsTotal: network.requestsTotal || 0,
345
347
  backends: network.backends || [],
@@ -0,0 +1,126 @@
1
+ export interface IChartPoint {
2
+ x: string | number;
3
+ y: number;
4
+ }
5
+
6
+ export interface IProtocolConnectionHistoryPointLike {
7
+ timestamp: number;
8
+ total: number;
9
+ protocols: Record<string, number>;
10
+ }
11
+
12
+ export type TProtocolChartSeries = Array<{
13
+ name: string;
14
+ data: IChartPoint[];
15
+ }>;
16
+
17
+ export const PROTOCOL_ORDER = ['HTTP/1.1', 'HTTP/2', 'HTTP/3', 'WebSocket', 'Other'] as const;
18
+ export const PROTOCOL_SERIES_ORDER = ['Total Active', ...PROTOCOL_ORDER] as const;
19
+
20
+ const toEpochSecond = (timestampArg: string | number): number => {
21
+ const timestampMs = typeof timestampArg === 'number'
22
+ ? timestampArg
23
+ : new Date(timestampArg).getTime();
24
+ return Math.floor(timestampMs / 1000);
25
+ };
26
+
27
+ export const getLatestHistoryEpochSecond = (
28
+ historyArg: Array<{ timestamp: number }>,
29
+ ): number => historyArg.reduce(
30
+ (latest, point) => Math.max(latest, Math.floor(point.timestamp / 1000)),
31
+ 0,
32
+ );
33
+
34
+ export const mergeChartPoints = (
35
+ localPointsArg: IChartPoint[],
36
+ canonicalPointsArg: IChartPoint[],
37
+ maximumPointsArg: number,
38
+ ): IChartPoint[] => {
39
+ const pointsBySecond = new Map<number, IChartPoint>();
40
+ for (const point of localPointsArg) {
41
+ const epochSecond = toEpochSecond(point.x);
42
+ if (Number.isFinite(epochSecond)) {
43
+ pointsBySecond.set(epochSecond, point);
44
+ }
45
+ }
46
+ // Canonical engine samples win when a locally repeated cached sample used the same second.
47
+ for (const point of canonicalPointsArg) {
48
+ const epochSecond = toEpochSecond(point.x);
49
+ if (Number.isFinite(epochSecond)) {
50
+ pointsBySecond.set(epochSecond, point);
51
+ }
52
+ }
53
+
54
+ return [...pointsBySecond.entries()]
55
+ .sort(([secondA], [secondB]) => secondA - secondB)
56
+ .map(([, point]) => point)
57
+ .slice(-maximumPointsArg);
58
+ };
59
+
60
+ export const createProtocolConnectionSeries = (
61
+ historyArg: IProtocolConnectionHistoryPointLike[] = [],
62
+ ): TProtocolChartSeries => PROTOCOL_SERIES_ORDER.map((seriesName) => ({
63
+ name: seriesName,
64
+ data: historyArg.map((point) => ({
65
+ x: new Date(point.timestamp).toISOString(),
66
+ y: seriesName === 'Total Active'
67
+ ? point.total
68
+ : (point.protocols[seriesName] ?? 0),
69
+ })),
70
+ }));
71
+
72
+ export const mergeProtocolConnectionHistory = (
73
+ localSeriesArg: TProtocolChartSeries,
74
+ canonicalHistoryArg: IProtocolConnectionHistoryPointLike[],
75
+ maximumPointsArg: number,
76
+ ): TProtocolChartSeries => {
77
+ const localSeriesByName = new Map(localSeriesArg.map((series) => [series.name, series]));
78
+ const canonicalSeriesByName = new Map(
79
+ createProtocolConnectionSeries(canonicalHistoryArg)
80
+ .map((series) => [series.name, series]),
81
+ );
82
+
83
+ return PROTOCOL_SERIES_ORDER.map((seriesName) => ({
84
+ name: seriesName,
85
+ data: mergeChartPoints(
86
+ localSeriesByName.get(seriesName)?.data ?? [],
87
+ canonicalSeriesByName.get(seriesName)?.data ?? [],
88
+ maximumPointsArg,
89
+ ),
90
+ }));
91
+ };
92
+
93
+ export const getProtocolConnectionTotal = (
94
+ protocolsArg: Record<string, number>,
95
+ ): number => (protocolsArg['HTTP/1.1'] ?? 0)
96
+ + (protocolsArg['HTTP/2'] ?? 0)
97
+ + (protocolsArg['HTTP/3'] ?? 0)
98
+ + (protocolsArg.Other ?? 0);
99
+
100
+ export const appendProtocolConnectionSample = (
101
+ localSeriesArg: TProtocolChartSeries,
102
+ timestampArg: string | number,
103
+ protocolsArg: Record<string, number>,
104
+ maximumPointsArg: number,
105
+ ): TProtocolChartSeries => {
106
+ const localSeriesByName = new Map(localSeriesArg.map((series) => [series.name, series]));
107
+ return PROTOCOL_SERIES_ORDER.map((seriesName) => ({
108
+ name: seriesName,
109
+ data: mergeChartPoints(
110
+ localSeriesByName.get(seriesName)?.data ?? [],
111
+ [{
112
+ x: timestampArg,
113
+ y: seriesName === 'Total Active'
114
+ ? getProtocolConnectionTotal(protocolsArg)
115
+ : (protocolsArg[seriesName] ?? 0),
116
+ }],
117
+ maximumPointsArg,
118
+ ),
119
+ }));
120
+ };
121
+
122
+ export const shouldRestartNetworkActivityTimer = (
123
+ isConnectedArg: boolean,
124
+ documentHiddenArg: boolean,
125
+ ): boolean => isConnectedArg && !documentHiddenArg;
126
+
@@ -4,6 +4,25 @@ import * as interfaces from '../../../dist_ts_interfaces/index.js';
4
4
  import { viewHostCss } from '../shared/css.js';
5
5
  import { type IStatsTile } from '@design.estate/dees-catalog';
6
6
 
7
+ import {
8
+ appendProtocolConnectionSample,
9
+ createProtocolConnectionSeries,
10
+ getLatestHistoryEpochSecond,
11
+ mergeChartPoints,
12
+ mergeProtocolConnectionHistory,
13
+ shouldRestartNetworkActivityTimer,
14
+ type IChartPoint,
15
+ type TProtocolChartSeries,
16
+ } from './network-activity-history.js';
17
+
18
+ const PROTOCOL_COLORS: Record<string, string> = {
19
+ 'HTTP/1.1': '#1976d2',
20
+ 'HTTP/2': '#388e3c',
21
+ 'HTTP/3': '#7b1fa2',
22
+ 'WebSocket': '#f57c00',
23
+ 'Other': '#757575',
24
+ };
25
+
7
26
  declare global {
8
27
  interface HTMLElementTagNameMap {
9
28
  'ops-view-network-activity': OpsViewNetworkActivity;
@@ -12,8 +31,8 @@ declare global {
12
31
 
13
32
  @customElement('ops-view-network-activity')
14
33
  export class OpsViewNetworkActivity extends DeesElement {
15
- /** How far back the traffic chart shows */
16
- private static readonly CHART_WINDOW_MS = 5 * 60 * 1000; // 5 minutes
34
+ /** How far back the realtime charts show */
35
+ private static readonly CHART_WINDOW_MS = 10 * 60 * 1000; // 10 minutes
17
36
  /** How often a new data point is added */
18
37
  private static readonly UPDATE_INTERVAL_MS = 1000; // 1 second
19
38
  /** Derived: max data points the buffer holds */
@@ -27,10 +46,16 @@ export class OpsViewNetworkActivity extends DeesElement {
27
46
 
28
47
 
29
48
  @state()
30
- accessor trafficDataIn: Array<{ x: string | number; y: number }> = [];
49
+ accessor trafficDataIn: IChartPoint[] = [];
50
+
51
+ @state()
52
+ accessor trafficDataOut: IChartPoint[] = [];
53
+
54
+ @state()
55
+ accessor frontendConnectionSeries: TProtocolChartSeries = createProtocolConnectionSeries();
31
56
 
32
57
  @state()
33
- accessor trafficDataOut: Array<{ x: string | number; y: number }> = [];
58
+ accessor backendConnectionSeries: TProtocolChartSeries = createProtocolConnectionSeries();
34
59
 
35
60
  // Track if we need to update the chart to avoid unnecessary re-renders
36
61
  private lastChartUpdate = 0;
@@ -38,8 +63,10 @@ export class OpsViewNetworkActivity extends DeesElement {
38
63
 
39
64
  private trafficUpdateTimer: any = null;
40
65
  private requestsPerSecHistory: number[] = []; // Track requests/sec over time for trend
41
- private historyLoaded = false; // Whether server-side throughput history has been loaded
42
66
  private visibilityHandler: (() => void) | null = null;
67
+ private lastThroughputHistoryEpochSecond = 0;
68
+ private lastFrontendHistoryEpochSecond = 0;
69
+ private lastBackendHistoryEpochSecond = 0;
43
70
 
44
71
  constructor() {
45
72
  super();
@@ -57,7 +84,16 @@ export class OpsViewNetworkActivity extends DeesElement {
57
84
  if (document.hidden) {
58
85
  this.stopTrafficUpdateTimer();
59
86
  } else {
60
- this.startTrafficUpdateTimer();
87
+ void appstate.networkStatePart
88
+ .dispatchAction(appstate.fetchNetworkStatsAction, null)
89
+ .catch((error) => {
90
+ console.error('Failed to refresh network history after visibility resume:', error);
91
+ })
92
+ .finally(() => {
93
+ if (shouldRestartNetworkActivityTimer(this.isConnected, document.hidden)) {
94
+ this.startTrafficUpdateTimer();
95
+ }
96
+ });
61
97
  }
62
98
  };
63
99
  document.addEventListener('visibilitychange', this.visibilityHandler);
@@ -106,51 +142,92 @@ export class OpsViewNetworkActivity extends DeesElement {
106
142
  this.trafficDataOut = emptyData.map(point => ({ ...point }));
107
143
  }
108
144
 
109
- /**
110
- * Load server-side throughput history into the chart.
111
- * Called once when history data first arrives from the Rust engine.
112
- * This pre-populates the chart so users see historical data immediately
113
- * instead of starting from all zeros.
114
- */
115
- private loadThroughputHistory() {
116
- const history = this.networkState.throughputHistory;
117
- if (!history || history.length === 0) return;
118
-
119
- this.historyLoaded = true;
120
-
121
- // Convert history points to chart data format (bytes/sec → Mbit/s)
122
- const historyIn = history.map(p => ({
123
- x: new Date(p.timestamp).toISOString(),
124
- y: Math.round((p.in * 8) / 1000000 * 10) / 10,
145
+ private mergeThroughputHistory(): void {
146
+ const history = this.networkState.throughputHistory || [];
147
+ const latestHistoryEpochSecond = getLatestHistoryEpochSecond(history);
148
+ if (latestHistoryEpochSecond <= this.lastThroughputHistoryEpochSecond) return;
149
+
150
+ const canonicalIn = history.map((point) => ({
151
+ x: new Date(point.timestamp).toISOString(),
152
+ y: Math.round(((point.in * 8) / 1000000) * 10) / 10,
125
153
  }));
126
- const historyOut = history.map(p => ({
127
- x: new Date(p.timestamp).toISOString(),
128
- y: Math.round((p.out * 8) / 1000000 * 10) / 10,
154
+ const canonicalOut = history.map((point) => ({
155
+ x: new Date(point.timestamp).toISOString(),
156
+ y: Math.round(((point.out * 8) / 1000000) * 10) / 10,
129
157
  }));
130
158
 
131
- const { MAX_DATA_POINTS, UPDATE_INTERVAL_MS } = OpsViewNetworkActivity;
159
+ this.trafficDataIn = mergeChartPoints(
160
+ this.trafficDataIn,
161
+ canonicalIn,
162
+ OpsViewNetworkActivity.MAX_DATA_POINTS,
163
+ );
164
+ this.trafficDataOut = mergeChartPoints(
165
+ this.trafficDataOut,
166
+ canonicalOut,
167
+ OpsViewNetworkActivity.MAX_DATA_POINTS,
168
+ );
169
+ this.lastThroughputHistoryEpochSecond = latestHistoryEpochSecond;
170
+ }
132
171
 
133
- // Use history as the chart data, keeping the most recent points within the window
134
- const sliceStart = Math.max(0, historyIn.length - MAX_DATA_POINTS);
135
- this.trafficDataIn = historyIn.slice(sliceStart);
136
- this.trafficDataOut = historyOut.slice(sliceStart);
137
-
138
- // If fewer than MAX_DATA_POINTS, pad the front with zeros
139
- if (this.trafficDataIn.length < MAX_DATA_POINTS) {
140
- const now = Date.now();
141
- const padCount = MAX_DATA_POINTS - this.trafficDataIn.length;
142
- const firstTimestamp = this.trafficDataIn.length > 0
143
- ? new Date(this.trafficDataIn[0].x).getTime()
144
- : now;
145
-
146
- const padIn = Array.from({ length: padCount }, (_, i) => ({
147
- x: new Date(firstTimestamp - ((padCount - i) * UPDATE_INTERVAL_MS)).toISOString(),
148
- y: 0,
149
- }));
150
- const padOut = padIn.map(p => ({ ...p }));
172
+ private getProtocolCounts(
173
+ distributionArg: interfaces.data.IProtocolDistribution,
174
+ ): Record<string, number> {
175
+ return {
176
+ 'HTTP/1.1': distributionArg.h1Active,
177
+ 'HTTP/2': distributionArg.h2Active,
178
+ 'HTTP/3': distributionArg.h3Active,
179
+ 'WebSocket': distributionArg.wsActive,
180
+ 'Other': distributionArg.otherActive,
181
+ };
182
+ }
151
183
 
152
- this.trafficDataIn = [...padIn, ...this.trafficDataIn];
153
- this.trafficDataOut = [...padOut, ...this.trafficDataOut];
184
+ private getProtocolSeriesWithColors(seriesArg: TProtocolChartSeries) {
185
+ return seriesArg.map((series) => ({
186
+ ...series,
187
+ color: series.name === 'Total Active' ? '#ef4444' : (PROTOCOL_COLORS[series.name] ?? '#757575'),
188
+ }));
189
+ }
190
+
191
+ private mergeProtocolConnectionHistories(): void {
192
+ const frontendHistory = this.networkState.frontendConnectionHistory || [];
193
+ const latestFrontendEpochSecond = getLatestHistoryEpochSecond(frontendHistory);
194
+ if (latestFrontendEpochSecond > this.lastFrontendHistoryEpochSecond) {
195
+ this.frontendConnectionSeries = mergeProtocolConnectionHistory(
196
+ this.frontendConnectionSeries,
197
+ frontendHistory,
198
+ OpsViewNetworkActivity.MAX_DATA_POINTS,
199
+ );
200
+ this.lastFrontendHistoryEpochSecond = latestFrontendEpochSecond;
201
+ }
202
+
203
+ const backendHistory = this.networkState.backendConnectionHistory || [];
204
+ const latestBackendEpochSecond = getLatestHistoryEpochSecond(backendHistory);
205
+ if (latestBackendEpochSecond > this.lastBackendHistoryEpochSecond) {
206
+ this.backendConnectionSeries = mergeProtocolConnectionHistory(
207
+ this.backendConnectionSeries,
208
+ backendHistory,
209
+ OpsViewNetworkActivity.MAX_DATA_POINTS,
210
+ );
211
+ this.lastBackendHistoryEpochSecond = latestBackendEpochSecond;
212
+ }
213
+ }
214
+
215
+ private addProtocolConnectionDataPoints(timestampArg: string): void {
216
+ if (this.networkState.frontendProtocols) {
217
+ this.frontendConnectionSeries = appendProtocolConnectionSample(
218
+ this.frontendConnectionSeries,
219
+ timestampArg,
220
+ this.getProtocolCounts(this.networkState.frontendProtocols),
221
+ OpsViewNetworkActivity.MAX_DATA_POINTS,
222
+ );
223
+ }
224
+ if (this.networkState.backendProtocols) {
225
+ this.backendConnectionSeries = appendProtocolConnectionSample(
226
+ this.backendConnectionSeries,
227
+ timestampArg,
228
+ this.getProtocolCounts(this.networkState.backendProtocols),
229
+ OpsViewNetworkActivity.MAX_DATA_POINTS,
230
+ );
154
231
  }
155
232
  }
156
233
 
@@ -268,9 +345,17 @@ export class OpsViewNetworkActivity extends DeesElement {
268
345
 
269
346
  .protocolChartGrid {
270
347
  display: grid;
271
- grid-template-columns: repeat(2, 1fr);
348
+ grid-template-columns: 1fr;
349
+ gap: 16px;
350
+ }
351
+
352
+ .protocolChartColumn {
353
+ display: flex;
354
+ flex-direction: column;
272
355
  gap: 16px;
356
+ min-width: 0;
273
357
  }
358
+
274
359
  `,
275
360
  ];
276
361
 
@@ -557,13 +642,7 @@ export class OpsViewNetworkActivity extends DeesElement {
557
642
  const fp = this.networkState.frontendProtocols;
558
643
  const bp = this.networkState.backendProtocols;
559
644
 
560
- const protoColors: Record<string, string> = {
561
- 'HTTP/1.1': '#1976d2',
562
- 'HTTP/2': '#388e3c',
563
- 'HTTP/3': '#7b1fa2',
564
- 'WebSocket': '#f57c00',
565
- 'Other': '#757575',
566
- };
645
+ const protoColors = PROTOCOL_COLORS;
567
646
 
568
647
  const buildDonutData = (dist: interfaces.data.IProtocolDistribution | null) => {
569
648
  if (!dist) return [];
@@ -581,22 +660,40 @@ export class OpsViewNetworkActivity extends DeesElement {
581
660
 
582
661
  return html`
583
662
  <div class="protocolChartGrid">
584
- <dees-chart-donut
585
- .label=${'Frontend Protocols'}
586
- .data=${frontendData.length > 0 ? frontendData : [{ name: 'No Traffic', value: 1, color: '#757575' }]}
587
- .showLegend=${true}
588
- .showLabels=${true}
589
- .innerRadiusPercent=${'55%'}
590
- .valueFormatter=${(val: number) => `${val} active`}
591
- ></dees-chart-donut>
592
- <dees-chart-donut
593
- .label=${'Backend Protocols'}
594
- .data=${backendData.length > 0 ? backendData : [{ name: 'No Traffic', value: 1, color: '#757575' }]}
595
- .showLegend=${true}
596
- .showLabels=${true}
597
- .innerRadiusPercent=${'55%'}
598
- .valueFormatter=${(val: number) => `${val} active`}
599
- ></dees-chart-donut>
663
+ <div class="protocolChartColumn">
664
+ <dees-chart-donut
665
+ .label=${'Frontend Protocols'}
666
+ .data=${frontendData.length > 0 ? frontendData : [{ name: 'No Traffic', value: 1, color: '#757575' }]}
667
+ .showLegend=${true}
668
+ .showLabels=${true}
669
+ .innerRadiusPercent=${'55%'}
670
+ .valueFormatter=${(val: number) => `${val} active`}
671
+ ></dees-chart-donut>
672
+ <dees-chart-area
673
+ .label=${'Frontend Active Connections'}
674
+ .series=${this.getProtocolSeriesWithColors(this.frontendConnectionSeries)}
675
+ .realtimeMode=${true}
676
+ .rollingWindow=${OpsViewNetworkActivity.CHART_WINDOW_MS}
677
+ .yAxisFormatter=${(val: number) => `${Math.round(val)} active`}
678
+ ></dees-chart-area>
679
+ </div>
680
+ <div class="protocolChartColumn">
681
+ <dees-chart-donut
682
+ .label=${'Backend Protocols'}
683
+ .data=${backendData.length > 0 ? backendData : [{ name: 'No Traffic', value: 1, color: '#757575' }]}
684
+ .showLegend=${true}
685
+ .showLabels=${true}
686
+ .innerRadiusPercent=${'55%'}
687
+ .valueFormatter=${(val: number) => `${val} active`}
688
+ ></dees-chart-donut>
689
+ <dees-chart-area
690
+ .label=${'Backend Active Connections'}
691
+ .series=${this.getProtocolSeriesWithColors(this.backendConnectionSeries)}
692
+ .realtimeMode=${true}
693
+ .rollingWindow=${OpsViewNetworkActivity.CHART_WINDOW_MS}
694
+ .yAxisFormatter=${(val: number) => `${Math.round(val)} active`}
695
+ ></dees-chart-area>
696
+ </div>
600
697
  </div>
601
698
  `;
602
699
  }
@@ -962,10 +1059,8 @@ export class OpsViewNetworkActivity extends DeesElement {
962
1059
  this.requestsPerSecHistory.shift();
963
1060
  }
964
1061
 
965
- // Load server-side throughput history into chart (once)
966
- if (!this.historyLoaded && this.networkState.throughputHistory && this.networkState.throughputHistory.length > 0) {
967
- this.loadThroughputHistory();
968
- }
1062
+ this.mergeThroughputHistory();
1063
+ this.mergeProtocolConnectionHistories();
969
1064
  }
970
1065
 
971
1066
  private startTrafficUpdateTimer() {
@@ -1005,13 +1100,17 @@ export class OpsViewNetworkActivity extends DeesElement {
1005
1100
  y: Math.round(throughputOutMbps * 10) / 10
1006
1101
  };
1007
1102
 
1008
- // In-place mutation then reassign for Lit reactivity (avoids 4 intermediate arrays)
1009
- if (this.trafficDataIn.length >= OpsViewNetworkActivity.MAX_DATA_POINTS) {
1010
- this.trafficDataIn.shift();
1011
- this.trafficDataOut.shift();
1012
- }
1013
- this.trafficDataIn = [...this.trafficDataIn, newDataPointIn];
1014
- this.trafficDataOut = [...this.trafficDataOut, newDataPointOut];
1103
+ this.trafficDataIn = mergeChartPoints(
1104
+ this.trafficDataIn,
1105
+ [newDataPointIn],
1106
+ OpsViewNetworkActivity.MAX_DATA_POINTS,
1107
+ );
1108
+ this.trafficDataOut = mergeChartPoints(
1109
+ this.trafficDataOut,
1110
+ [newDataPointOut],
1111
+ OpsViewNetworkActivity.MAX_DATA_POINTS,
1112
+ );
1113
+ this.addProtocolConnectionDataPoints(timestamp);
1015
1114
 
1016
1115
  this.lastChartUpdate = now;
1017
1116
  }