From ddfe06c72fc409fc9dfbb4c1360788128a3a91eb Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Sun, 14 Apr 2024 01:03:12 +0800 Subject: [PATCH] Chore: Apply suggestions --- src/components/PingChart.vue | 471 ++++++++++++++++++----------------- 1 file changed, 245 insertions(+), 226 deletions(-) diff --git a/src/components/PingChart.vue b/src/components/PingChart.vue index a4e44c1d..1ab753f2 100644 --- a/src/components/PingChart.vue +++ b/src/components/PingChart.vue @@ -47,7 +47,7 @@ export default { loading: false, // Time period for the chart to display, in hours - chartPeriodHrs: "0", + chartPeriodHrs: "24", chartPeriodOptions: { 0: this.$t("recent"), @@ -163,229 +163,10 @@ export default { }; }, chartData() { - let lastHeartbeatTime; - const monitorInterval = this.$root.monitorList[this.monitorId]?.interval; - if (this.chartPeriodHrs === "0") { - // Render chart using heartbeatList - let pingData = []; // Ping Data for Line Chart, y-axis contains ping time - let downData = []; // Down Data for Bar Chart, y-axis is 1 if target is down (red color), under maintenance (blue color) or pending (orange color), 0 if target is up - let colorData = []; // Color Data for Bar Chart - - let heartbeatList = (this.monitorId in this.$root.heartbeatList && this.$root.heartbeatList[this.monitorId]) || []; - - heartbeatList - .map((beat) => { - const beatTime = this.$root.toDayjs(beat.time); - const x = beatTime.format("YYYY-MM-DD HH:mm:ss"); - - // Insert empty datapoint to separate big gaps - if (lastHeartbeatTime && monitorInterval) { - const diff = Math.abs(beatTime.diff(lastHeartbeatTime)); - if (diff > monitorInterval * 1000 * 10) { - // Big gap detected - const gapX = [ - lastHeartbeatTime.add(monitorInterval, "second").format("YYYY-MM-DD HH:mm:ss"), - beatTime.subtract(monitorInterval, "second").format("YYYY-MM-DD HH:mm:ss") - ]; - - for (const x of gapX) { - pingData.push({ - x, - y: null, - }); - downData.push({ - x, - y: null, - }); - colorData.push("#000"); - } - - } - } - - pingData.push({ - x, - y: beat.status === UP ? beat.ping : null, - }); - downData.push({ - x, - y: (beat.status === DOWN || beat.status === MAINTENANCE || beat.status === PENDING) ? 1 : 0, - }); - colorData.push((beat.status === MAINTENANCE) ? "rgba(23,71,245,0.41)" : ((beat.status === PENDING) ? "rgba(245,182,23,0.41)" : "#DC354568")); - - lastHeartbeatTime = beatTime; - }); - - return { - datasets: [ - { - // Line Chart - data: pingData, - fill: "origin", - tension: 0.2, - borderColor: "#5CDD8B", - backgroundColor: "#5CDD8B38", - yAxisID: "y", - label: "ping", - }, - { - // Bar Chart - type: "bar", - data: downData, - borderColor: "#00000000", - backgroundColor: colorData, - yAxisID: "y1", - barThickness: "flex", - barPercentage: 1, - categoryPercentage: 1, - inflateAmount: 0.05, - label: "status", - }, - ], - }; + return this.getChartDatapointsFromHeartbeatList(); } else { - // Render chart using UptimeCalculator data - let avgPingData = []; // Ping Data for Line Chart, y-axis contains ping time - let minPingData = []; // Ping Data for Line Chart, y-axis contains ping time - let maxPingData = []; // Ping Data for Line Chart, y-axis contains ping time - let downData = []; // Down Data for Bar Chart, y-axis is number of down datapoints in this period - let colorData = []; // Color Data for Bar Chart - - const period = parseInt(this.chartPeriodHrs); - let aggregatePoints = period > 6 ? 12 : 4; - - let aggregateBuffer = []; - - if (this.chartRawData) { - for (const datapoint of this.chartRawData) { - // Empty datapoints are ignored - if (datapoint.up === 0 && datapoint.down === 0 && datapoint.maintenance === 0) { - continue; - } - - const beatTime = this.$root.unixToDayjs(datapoint.timestamp); - - // Insert empty datapoint to separate big gaps - if (lastHeartbeatTime && monitorInterval) { - const diff = Math.abs(beatTime.diff(lastHeartbeatTime)); - if ((period <= 24 && diff > Math.max(1000 * 60 * 10, monitorInterval * 1000 * 10)) || - (period > 24 && diff > Math.max(1000 * 60 * 60 * 10, monitorInterval * 1000 * 10)) ) { - // Big gap detected - // Clear the aggregate buffer - if (aggregateBuffer.length > 0) { - const average = this.getAverage(aggregateBuffer); - this.pushDatapoint(average, avgPingData, minPingData, maxPingData, downData, colorData); - aggregateBuffer = []; - } - - const gapX = [ - lastHeartbeatTime.subtract(monitorInterval, "second").format("YYYY-MM-DD HH:mm:ss"), - this.$root.unixToDateTime(datapoint.timestamp + 60), - ]; - - for (const x of gapX) { - avgPingData.push({ - x, - y: null, - }); - minPingData.push({ - x, - y: null, - }); - maxPingData.push({ - x, - y: null, - }); - downData.push({ - x, - y: null, - }); - colorData.push("#000"); - } - - } - } - - if (datapoint.up > 0 && this.chartRawData.length > aggregatePoints * 2) { - // Aggregate Up data using a sliding window - aggregateBuffer.push(datapoint); - - if (aggregateBuffer.length === aggregatePoints) { - const average = this.getAverage(aggregateBuffer); - this.pushDatapoint(average, avgPingData, minPingData, maxPingData, downData, colorData); - // Remove the first half of the buffer - aggregateBuffer = aggregateBuffer.slice(Math.floor(aggregatePoints / 2)); - } - } else { - // datapoint is fully down or too few datapoints, no need to aggregate - // Clear the aggregate buffer - if (aggregateBuffer.length > 0) { - const average = this.getAverage(aggregateBuffer); - this.pushDatapoint(average, avgPingData, minPingData, maxPingData, downData, colorData); - aggregateBuffer = []; - } - - this.pushDatapoint(datapoint, avgPingData, minPingData, maxPingData, downData, colorData); - } - - lastHeartbeatTime = beatTime; - } - // Clear the aggregate buffer if there are still datapoints - if (aggregateBuffer.length > 0) { - const average = this.getAverage(aggregateBuffer); - this.pushDatapoint(average, avgPingData, minPingData, maxPingData, downData, colorData); - aggregateBuffer = []; - } - } - - return { - datasets: [ - { - // average ping chart - data: avgPingData, - fill: "origin", - tension: 0.2, - borderColor: "#5CDD8B", - backgroundColor: "#5CDD8B06", - yAxisID: "y", - label: "avg-ping", - }, - { - // minimum ping chart - data: minPingData, - fill: "origin", - tension: 0.2, - borderColor: "#3CBD6B38", - backgroundColor: "#5CDD8B06", - yAxisID: "y", - label: "min-ping", - }, - { - // maximum ping chart - data: maxPingData, - fill: "origin", - tension: 0.2, - borderColor: "#7CBD6B38", - backgroundColor: "#5CDD8B06", - yAxisID: "y", - label: "max-ping", - }, - { - // Bar Chart - type: "bar", - data: downData, - borderColor: "#00000000", - backgroundColor: colorData, - yAxisID: "y1", - barThickness: "flex", - barPercentage: 1, - categoryPercentage: 1, - inflateAmount: 0.05, - label: "status", - }, - ], - }; + return this.getChartDatapointsFromStats(); } }, }, @@ -457,14 +238,14 @@ export default { // Target is in maintenance return "rgba(23,71,245,0.41)"; } else if (datapoint.down === 0) { - // Target is up - return "#FFFFFFFF"; + // Target is up, no need to display a bar + return "#000"; } else if (datapoint.up === 0) { // Target is down - return "#DC354568"; + return "rgba(220, 53, 69, 0.41)"; } else { // Show yellow for mixed status - return "rgba(245,182,23,0.41)"; + return "rgba(245, 182, 23, 0.41)"; } }, // push datapoint to chartData @@ -513,6 +294,244 @@ export default { maxPing, }; }, + getChartDatapointsFromHeartbeatList() { + // Render chart using heartbeatList + let lastHeartbeatTime; + const monitorInterval = this.$root.monitorList[this.monitorId]?.interval; + let pingData = []; // Ping Data for Line Chart, y-axis contains ping time + let downData = []; // Down Data for Bar Chart, y-axis is 1 if target is down (red color), under maintenance (blue color) or pending (orange color), 0 if target is up + let colorData = []; // Color Data for Bar Chart + + let heartbeatList = (this.monitorId in this.$root.heartbeatList && this.$root.heartbeatList[this.monitorId]) || []; + + for (const beat of heartbeatList) { + const beatTime = this.$root.toDayjs(beat.time); + const x = beatTime.format("YYYY-MM-DD HH:mm:ss"); + + // Insert empty datapoint to separate big gaps + if (lastHeartbeatTime && monitorInterval) { + const diff = Math.abs(beatTime.diff(lastHeartbeatTime)); + if (diff > monitorInterval * 1000 * 10) { + // Big gap detected + const gapX = [ + lastHeartbeatTime.add(monitorInterval, "second").format("YYYY-MM-DD HH:mm:ss"), + beatTime.subtract(monitorInterval, "second").format("YYYY-MM-DD HH:mm:ss") + ]; + + for (const x of gapX) { + pingData.push({ + x, + y: null, + }); + downData.push({ + x, + y: null, + }); + colorData.push("#000"); + } + + } + } + + pingData.push({ + x, + y: beat.status === UP ? beat.ping : null, + }); + downData.push({ + x, + y: (beat.status === DOWN || beat.status === MAINTENANCE || beat.status === PENDING) ? 1 : 0, + }); + switch (beat.status) { + case MAINTENANCE: + colorData.push("rgba(23 ,71, 245, 0.41)"); + break; + case PENDING: + colorData.push("rgba(245, 182, 23, 0.41)"); + break; + default: + colorData.push("rgba(220, 53, 69, 0.41)"); + } + + lastHeartbeatTime = beatTime; + } + + return { + datasets: [ + { + // Line Chart + data: pingData, + fill: "origin", + tension: 0.2, + borderColor: "#5CDD8B", + backgroundColor: "#5CDD8B38", + yAxisID: "y", + label: "ping", + }, + { + // Bar Chart + type: "bar", + data: downData, + borderColor: "#00000000", + backgroundColor: colorData, + yAxisID: "y1", + barThickness: "flex", + barPercentage: 1, + categoryPercentage: 1, + inflateAmount: 0.05, + label: "status", + }, + ], + }; + }, + getChartDatapointsFromStats() { + // Render chart using UptimeCalculator data + let lastHeartbeatTime; + const monitorInterval = this.$root.monitorList[this.monitorId]?.interval; + + let avgPingData = []; // Ping Data for Line Chart, y-axis contains ping time + let minPingData = []; // Ping Data for Line Chart, y-axis contains ping time + let maxPingData = []; // Ping Data for Line Chart, y-axis contains ping time + let downData = []; // Down Data for Bar Chart, y-axis is number of down datapoints in this period + let colorData = []; // Color Data for Bar Chart + + const period = parseInt(this.chartPeriodHrs); + let aggregatePoints = period > 6 ? 12 : 4; + + let aggregateBuffer = []; + + if (this.chartRawData) { + for (const datapoint of this.chartRawData) { + // Empty datapoints are ignored + if (datapoint.up === 0 && datapoint.down === 0 && datapoint.maintenance === 0) { + continue; + } + + const beatTime = this.$root.unixToDayjs(datapoint.timestamp); + + // Insert empty datapoint to separate big gaps + if (lastHeartbeatTime && monitorInterval) { + const diff = Math.abs(beatTime.diff(lastHeartbeatTime)); + const oneSecond = 1000; + const oneMinute = oneSecond * 60; + const oneHour = oneMinute * 60; + if ((period <= 24 && diff > Math.max(oneMinute * 10, monitorInterval * oneSecond * 10)) || + (period > 24 && diff > Math.max(oneHour * 10, monitorInterval * oneSecond * 10))) { + // Big gap detected + // Clear the aggregate buffer + if (aggregateBuffer.length > 0) { + const average = this.getAverage(aggregateBuffer); + this.pushDatapoint(average, avgPingData, minPingData, maxPingData, downData, colorData); + aggregateBuffer = []; + } + + const gapX = [ + lastHeartbeatTime.subtract(monitorInterval, "second").format("YYYY-MM-DD HH:mm:ss"), + this.$root.unixToDateTime(datapoint.timestamp + 60), + ]; + + for (const x of gapX) { + avgPingData.push({ + x, + y: null, + }); + minPingData.push({ + x, + y: null, + }); + maxPingData.push({ + x, + y: null, + }); + downData.push({ + x, + y: null, + }); + colorData.push("#000"); + } + + } + } + + if (datapoint.up > 0 && this.chartRawData.length > aggregatePoints * 2) { + // Aggregate Up data using a sliding window + aggregateBuffer.push(datapoint); + + if (aggregateBuffer.length === aggregatePoints) { + const average = this.getAverage(aggregateBuffer); + this.pushDatapoint(average, avgPingData, minPingData, maxPingData, downData, colorData); + // Remove the first half of the buffer + aggregateBuffer = aggregateBuffer.slice(Math.floor(aggregatePoints / 2)); + } + } else { + // datapoint is fully down or too few datapoints, no need to aggregate + // Clear the aggregate buffer + if (aggregateBuffer.length > 0) { + const average = this.getAverage(aggregateBuffer); + this.pushDatapoint(average, avgPingData, minPingData, maxPingData, downData, colorData); + aggregateBuffer = []; + } + + this.pushDatapoint(datapoint, avgPingData, minPingData, maxPingData, downData, colorData); + } + + lastHeartbeatTime = beatTime; + } + // Clear the aggregate buffer if there are still datapoints + if (aggregateBuffer.length > 0) { + const average = this.getAverage(aggregateBuffer); + this.pushDatapoint(average, avgPingData, minPingData, maxPingData, downData, colorData); + aggregateBuffer = []; + } + } + + return { + datasets: [ + { + // average ping chart + data: avgPingData, + fill: "origin", + tension: 0.2, + borderColor: "#5CDD8B", + backgroundColor: "#5CDD8B06", + yAxisID: "y", + label: "avg-ping", + }, + { + // minimum ping chart + data: minPingData, + fill: "origin", + tension: 0.2, + borderColor: "#3CBD6B38", + backgroundColor: "#5CDD8B06", + yAxisID: "y", + label: "min-ping", + }, + { + // maximum ping chart + data: maxPingData, + fill: "origin", + tension: 0.2, + borderColor: "#7CBD6B38", + backgroundColor: "#5CDD8B06", + yAxisID: "y", + label: "max-ping", + }, + { + // Bar Chart + type: "bar", + data: downData, + borderColor: "#00000000", + backgroundColor: colorData, + yAxisID: "y1", + barThickness: "flex", + barPercentage: 1, + categoryPercentage: 1, + inflateAmount: 0.05, + label: "status", + }, + ], + }; + }, } };