From 4c25455ee7bdd7b2078cd9cc450c634e736b7cf4 Mon Sep 17 00:00:00 2001 From: Luca Haid Date: Sun, 29 Jun 2025 20:33:54 +0200 Subject: [PATCH] added zabbix integration for statistics --- Layout/default/Device/LiveGraph.php | 112 +++++++ application/Device/DeviceController.php | 5 + .../DeviceMonitoringController.php | 179 +++++++++++ application/Graphing/Graphing.php | 9 - application/Graphing/GraphingController.php | 64 ---- lib/Zabbix/Zabbix.php | 9 +- public/js/pages/Device/Device.css | 21 ++ public/js/pages/Device/Device.js | 101 +++--- public/js/pages/Device/DeviceMonitoring.js | 297 ++++++++++++++++++ .../js/pages/DeviceGraphing/DeviceGraphing.js | 133 -------- 10 files changed, 685 insertions(+), 245 deletions(-) create mode 100644 Layout/default/Device/LiveGraph.php create mode 100644 application/DeviceMonitoring/DeviceMonitoringController.php delete mode 100644 application/Graphing/Graphing.php delete mode 100644 application/Graphing/GraphingController.php create mode 100644 public/js/pages/Device/Device.css create mode 100644 public/js/pages/Device/DeviceMonitoring.js delete mode 100644 public/js/pages/DeviceGraphing/DeviceGraphing.js diff --git a/Layout/default/Device/LiveGraph.php b/Layout/default/Device/LiveGraph.php new file mode 100644 index 000000000..4d30c92b1 --- /dev/null +++ b/Layout/default/Device/LiveGraph.php @@ -0,0 +1,112 @@ + + + + + + Live-Graph + + + +
+ +
+

Lade initiale Daten...

+
+
+ + + + + + + + + \ No newline at end of file diff --git a/application/Device/DeviceController.php b/application/Device/DeviceController.php index 7c467bfdd..9492dedba 100644 --- a/application/Device/DeviceController.php +++ b/application/Device/DeviceController.php @@ -49,6 +49,11 @@ class DeviceController extends mfBaseController ]; }, DevicetypeModel::getAll()); + $this->layout()->set('additionalJS', [ + "plugins/chart.js/chart.4.4.6.js", + "plugins/chart.js/chartjs-adapter-moment.min.js" + ]); + $JSGlobals = ["BASE_URL" => self::getUrl(""), "DASHBOARD_URL" => self::getUrl("Dashboard"), "MFAPPNAME" => MFAPPNAME_SLUG, diff --git a/application/DeviceMonitoring/DeviceMonitoringController.php b/application/DeviceMonitoring/DeviceMonitoringController.php new file mode 100644 index 000000000..873c8479b --- /dev/null +++ b/application/DeviceMonitoring/DeviceMonitoringController.php @@ -0,0 +1,179 @@ +needlogin = true; + $me = new User(); + $me->loadMe(); + $this->layout()->set("me", $me); + $this->me = $me; + + if (!$this->me->is("Admin")) { + http_response_code(403); + self::returnJson(['success' => false, 'message' => 'Permission denied']); + die(); + } + + if (defined('ZABBIX_API_URL') && defined('ZABBIX_API_KEY') && ZABBIX_API_URL && ZABBIX_API_KEY) { + $this->zabbix = new Zabbix($this->ZABBIX_API_URL, $this->ZABBIX_API_KEY); + } else { + http_response_code(500); + self::returnJson(['error' => 'Zabbix API is not configured on the server.']); + die(); + } + $this->postData = json_decode(file_get_contents('php://input'), true) ?? []; + } + + /** + * Gets a list of all available interfaces, grouping Sent/Received items. + */ + protected function listInterfacesAction() + { + $hostId = $this->request->hostId; + $items = $this->zabbix->getHostInterfaceItems($hostId); + $interfaces = []; + + foreach ($items as $item) { + $baseName = preg_replace('/:\s*Bits\s*(sent|received)$/i', '', $item['name']); + $direction = str_contains(strtolower($item['name']), 'received') ? 'rx' : 'tx'; + + if (!isset($interfaces[$baseName])) { + $interfaces[$baseName] = ['name' => $baseName, 'rx' => null, 'tx' => null]; + } + $interfaces[$baseName][$direction] = $item; + } + + $sortedInterfaces = array_values($interfaces); + usort($sortedInterfaces, fn($a, $b) => strcmp($a['name'], $b['name'])); + self::returnJson($sortedInterfaces); + } + + /** + * Gets historical data for a specific list of item IDs. + */ + protected function interfaceDataAction() + { + $itemIds = $this->postData['itemIds'] ?? []; + if (empty($itemIds)) { + self::returnJson([]); + return; + } + $timeRange = $this->postData['timeRange'] ?? '24h'; + + $time_from = strtotime('-' . str_replace(['m', 'h', 'd'], [' minutes', ' hours', ' days'], $timeRange)); + + $params = [ + 'itemids' => $itemIds, + 'output' => 'extend', + 'history' => 3, // Numeric (unsigned) + 'sortfield' => 'clock', + 'sortorder' => 'ASC', + 'time_from' => $time_from, + ]; + $history = $this->zabbix->zabbixRequest('history.get', $params)['result'] ?? []; + + $historyByItemId = []; + foreach ($history as $point) { + $historyByItemId[$point['itemid']][] = [ + 'x' => intval($point['clock']) * 1000, + 'y' => round(floatval($point['value']) / 1000000, 2) + ]; + } + + self::returnJson($historyByItemId); + } + + /** + * Gets general monitoring data (Uptime, Ping, Temp). + */ + protected function generalDataAction() { + $hostId = $this->request->hostId; + + $itemsToFetch = [ + 'ping' => $this->zabbix->getICMPItems($hostId), + 'uptime' => $this->zabbix->getUptimeItems($hostId), + ]; + + $itemIds = []; + $itemMap = []; + foreach ($itemsToFetch as $type => $items) { + if (!empty($items)) { + foreach($items as $item) { + $itemIds[] = $item['itemid']; + $itemMap[$item['itemid']] = ['type' => $type, 'name' => $item['name'], 'units' => $item['units']]; + } + } + } + + $values = []; + if(!empty($itemIds)) { + $history = $this->zabbix->getItemValues($itemIds, 1); + foreach($history as $h) { + $info = $itemMap[$h['itemid']]; + $values[$info['type']][] = ['name' => $info['name'], 'value' => $h['value'], 'clock' => $h['clock'], 'units' => $info['units']]; + } + } + + self::returnJson($values); + } + + /** + * Gets Zabbix problems (triggers) for the host. + */ + protected function getProblemsAction() { + $hostId = $this->request->hostId; + $problems = $this->zabbix->zabbixRequest('problem.get', [ + 'hostids' => $hostId, + 'output' => 'extend', + 'recent' => true, // Use boolean true + 'sortfield' => ['eventid'], + 'sortorder' => 'DESC' + ])['result'] ?? []; + + self::returnJson($problems); + } + + /** + * Forces a Zabbix item check and returns the latest value for live graphs. + */ + protected function liveDataAction() { + $itemId = $this->request->itemId; + if(empty($itemId)) { + http_response_code(400); + self::returnJson(['error' => 'Item ID is required.']); + return; + } + + $this->zabbix->createTask($itemId); + sleep(3); + $history = $this->zabbix->getItemValues([$itemId], 1); + + if(empty($history)) { + self::returnJson(null); + return; + } + + $point = $history[0]; + $formattedPoint = [ + 'x' => intval($point['clock']) * 1000, + 'y' => round(floatval($point['value']) / 1000000, 2) + ]; + + self::returnJson($formattedPoint); + } + + /** + * Renders a dedicated HTML page for the live graph popup. + */ + public function liveGraphPageAction() { + $this->layout(false); + $this->layout()->set('API_BASE_URL', self::getUrl("DeviceMonitoring")); + $this->layout()->setTemplate('Device/LiveGraph'); + } +} \ No newline at end of file diff --git a/application/Graphing/Graphing.php b/application/Graphing/Graphing.php deleted file mode 100644 index 845b3ea26..000000000 --- a/application/Graphing/Graphing.php +++ /dev/null @@ -1,9 +0,0 @@ -loadMe(); - $this->layout()->set("me", $me); - $this->me = $me; - - if (!$this->me->isAdmin()) { - $this->redirect("dashboard"); - } - - $this->zabbix = new Zabbix($this->ZABBIX_API_URL, $this->ZABBIX_API_KEY); - } - - protected function indexAction() { - $this->layout()->set('additionalJS', ["plugins/chart.js/chart.4.4.6.js", "plugins/chart.js/chartjs-adapter-moment.min.js"]); - Helper::renderVue($this, "DeviceGraphing", $this->mod, []); - } - - - protected function dataAction() { - header('Content-Type: application/json'); - $hostId = $this->request->hostId; - $hostInterfaceItems = $this->zabbix->getHostInterfaceItems($hostId, ''); - // limit to 25 items - $hostInterfaceItems = array_slice($hostInterfaceItems, 0, 25); - - $itemIds = array_map(function($item) { - return $item['itemid']; - }, $hostInterfaceItems); - - - $itemValues = $this->zabbix->getItemValues($itemIds, 1000); - - $out = []; - foreach ($hostInterfaceItems as $item) { - $out[$item['itemid']] = [ - 'name' => str_replace('Bits', 'Mbps', $item['name']), - 'units' => $item['units'], - 'values' => [] - ]; - } - - foreach ($itemValues as $itemValue) { - $out[$itemValue['itemid']]['values'][] = [ - 'clock' => $itemValue['clock'], - 'value' => $itemValue['value'] / 1000000 - ]; - } - - // sort by name - uasort($out, function($a, $b) { - return strcmp($a['name'], $b['name']); - }); - - - die(json_encode($out)); - } -} \ No newline at end of file diff --git a/lib/Zabbix/Zabbix.php b/lib/Zabbix/Zabbix.php index 31de6a16e..cbad9c98e 100644 --- a/lib/Zabbix/Zabbix.php +++ b/lib/Zabbix/Zabbix.php @@ -9,7 +9,7 @@ class Zabbix { $this->apiKey = $apiKey; } - public function zabbixRequest($method, $params) { + public function zabbixRequest($method, $params, $die = false) { $data = array( 'jsonrpc' => '2.0', 'method' => $method, @@ -26,6 +26,13 @@ class Zabbix { ) ); + // var dump options and data and die + if ($die) { + var_dump($options); + echo json_encode($data); + die(); + } + $context = stream_context_create($options); $result = file_get_contents($this->url, false, $context); diff --git a/public/js/pages/Device/Device.css b/public/js/pages/Device/Device.css new file mode 100644 index 000000000..e7dfdb483 --- /dev/null +++ b/public/js/pages/Device/Device.css @@ -0,0 +1,21 @@ +.monitoring-tabs { display: flex; border-bottom: 1px solid #dee2e6; } +.monitoring-tabs button { background: none; border: none; padding: 10px 15px; cursor: pointer; border-bottom: 3px solid transparent; font-size: 0.9rem; color: #495057; } +.monitoring-tabs button:hover { color: #0056b3; } +.monitoring-tabs button.active { border-bottom-color: #007bff; color: #007bff; font-weight: bold; } +.monitoring-content { padding: 15px; min-height: 400px; } +.overview-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 15px; } +.chart-container { display: grid; grid-template-columns: repeat(auto-fit, minmax(450px, 1fr)); gap: 15px; } +.chart-title { font-size: 0.9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.problems-list { display: flex; flex-direction: column; gap: 10px; } +.problem-card { display: flex; align-items: center; padding: 10px; border-radius: 5px; border-left-width: 5px; border-left-style: solid; background-color: #f8f9fa; } +.problem-icon { font-size: 1.5rem; margin-right: 15px; width: 30px; text-align: center; } +.problem-details { flex-grow: 1; } +.problem-header { display: flex; justify-content: space-between; align-items: baseline; } +.problem-name { font-weight: 500; } +.problem-time { font-size: 0.8rem; color: #6c757d; } +.problem-opdata { font-size: 0.85rem; color: #495057; margin-top: 4px; } +.sev-info { border-left-color: #17a2b8; } .sev-info .problem-icon { color: #17a2b8; } +.sev-warning { border-left-color: #ffc107; } .sev-warning .problem-icon { color: #ffc107; } +.sev-average { border-left-color: #fd7e14; } .sev-average .problem-icon { color: #fd7e14; } +.sev-high { border-left-color: #dc3545; } .sev-high .problem-icon { color: #dc3545; } +.sev-disaster { border-left-color: #7B014C; } .sev-disaster .problem-icon { color: #7B014C; } diff --git a/public/js/pages/Device/Device.js b/public/js/pages/Device/Device.js index ec8159716..3b1c804a5 100644 --- a/public/js/pages/Device/Device.js +++ b/public/js/pages/Device/Device.js @@ -68,45 +68,49 @@ Vue.component('device-view-switch', { Vue.component('DeviceTable', { //language=Vue template: ` - + - + - + - + - - - `, + + `, data() { return { window: window, @@ -249,16 +253,26 @@ Vue.component('DeviceType', { Vue.component('Device', { //language=Vue template: ` - - + + + - - - `, + + + + + `, data() { return { window: window, currentView: 'DeviceTable', + // MODIFIED: Add data properties to manage the modal state + monitoringModalHostId: null, + selectedDeviceName: '', }; }, beforeMount() { @@ -272,4 +286,15 @@ Vue.component('Device', { window.localStorage.setItem('tt-device-view', this.currentView); } }, + // MODIFIED: Add methods to handle modal state + methods: { + showMonitoringModal(deviceRow) { + this.selectedDeviceName = deviceRow.name; + this.monitoringModalHostId = deviceRow.zabbix_host_id; + }, + closeMonitoringModal() { + this.monitoringModalHostId = null; + this.selectedDeviceName = ''; + } + } }); diff --git a/public/js/pages/Device/DeviceMonitoring.js b/public/js/pages/Device/DeviceMonitoring.js new file mode 100644 index 000000000..41307c88b --- /dev/null +++ b/public/js/pages/Device/DeviceMonitoring.js @@ -0,0 +1,297 @@ +Vue.component('device-monitoring-modal', { + //language=Vue + template: ` + + +
+ +
+ +
+
+
+
Keine allgemeinen Monitoring-Daten gefunden.
+
+ + + + + + + +
{{ item.name.replace('ICMP: ', '') }}{{ formatGeneralValue(item) }} {{ item.units }}
+
+ + +
+

{{ formatUptime(item.value) }}

+ Seit {{ moment(Date.now() - item.value * 1000).format('DD.MM.YYYY HH:mm') }} +
+
+
+
+ +
+
+
+ +
+
+ +
+
+
+
Bitte eine oder mehrere Schnittstellen auswählen, um Graphen anzuzeigen.
+
+
+
+
{{ iface.name }}
+ +
+ +
+
+ Empfangen (Mbps) + Min: {{ statistics[iface.name].rx.min }} + Avg: {{ statistics[iface.name].rx.avg }} + Median: {{ statistics[iface.name].rx.median }} + Max: {{ statistics[iface.name].rx.max }} + 95%: {{ statistics[iface.name].rx.p95 }} +
+
+ Gesendet (Mbps) + Min: {{ statistics[iface.name].tx.min }} + Avg: {{ statistics[iface.name].tx.avg }} + Median: {{ statistics[iface.name].tx.median }} + Max: {{ statistics[iface.name].tx.max }} + 95%: {{ statistics[iface.name].tx.p95 }} +
+
+
+
+
+ +
+
+
Keine aktuellen Probleme für dieses Gerät gefunden.
+
+
+
+
+
+ {{ p.name }} + {{ moment(p.clock * 1000).format('DD.MM.YYYY HH:mm:ss') }} +
+
{{ p.opdata }}
+
+
+
+
+
+
+ `, + props: ['hostId', 'deviceName'], + data() { + return { + moment: window.moment, + activeTab: 'overview', + tabs: [ + { id: 'overview', name: 'Übersicht', icon: 'fas fa-tachometer-alt' }, + { id: 'interfaces', name: 'Schnittstellen', icon: 'fas fa-ethernet' }, + { id: 'problems', name: 'Probleme', icon: 'fas fa-exclamation-triangle' }, + ], + loading: { overview: false, interfaces: false, problems: false }, + generalData: null, + problemData: [], + allInterfaces: [], + selectedInterfaces: [], + interfaceTimeRange: '24h', + timeRanges: [ + { text: '6H', value: '6h' }, { text: '24H', value: '24h' }, + { text: '7T', value: '7d' }, { text: '30T', value: '30d' }, + ], + interfaceChartData: {}, + chartInstances: {}, + }; + }, + computed: { + interfaceOptions() { + return this.allInterfaces.map(iface => ({ text: iface.name, value: iface.name })); + }, + selectedInterfacesData() { + return this.allInterfaces.filter(iface => this.selectedInterfaces.includes(iface.name)); + }, + // NEU: Statistik-Berechnung + statistics() { + if (this.selectedInterfaces.length === 0 || Object.keys(this.interfaceChartData).length === 0) return {}; + + const stats = {}; + this.selectedInterfacesData.forEach(iface => { + const calculate = (data) => { + if (!data || data.length === 0) return { min: 'N/A', max: 'N/A', avg: 'N/A', median: 'N/A', p95: 'N/A' }; + const values = data.map(p => p.y); + const sorted = [...values].sort((a, b) => a - b); + const sum = values.reduce((acc, val) => acc + val, 0); + const mid = Math.floor(sorted.length / 2); + const median = sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid]; + return { + min: this.formatStat(sorted[0]), + max: this.formatStat(sorted[sorted.length - 1]), + avg: this.formatStat(sum / values.length), + median: this.formatStat(median), + p95: this.formatStat(sorted[Math.floor(sorted.length * 0.95)]), + }; + }; + stats[iface.name] = { + rx: calculate(this.interfaceChartData[iface.rx?.itemid]), + tx: calculate(this.interfaceChartData[iface.tx?.itemid]), + }; + }); + return stats; + } + }, + async mounted() { + // GEÄNDERT: moment.locale gesetzt + moment.locale('de'); + this.fetchTabData(); + }, + beforeDestroy() { + this.destroyCharts(); + }, + methods: { + // NEU: formatStat-Helfer + formatStat: val => typeof val === 'number' ? val.toFixed(2) : val, + formatUptime: s => `${Math.floor(s/(3600*24))}t ${Math.floor(s%(3600*24)/3600)}h ${Math.floor(s%3600/60)}m`, + formatGeneralValue: item => (item.units === 's') ? parseFloat(item.value).toFixed(3) : (item.units === '%') ? parseFloat(item.value).toFixed(2) : item.value, + getSeverityClass: s => ['sev-info', 'sev-info', 'sev-warning', 'sev-average', 'sev-high', 'sev-disaster'][s] || 'sev-info', + getSeverityIcon: s => ['fa-info-circle', 'fa-info-circle', 'fa-exclamation-circle', 'fa-exclamation-triangle', 'fa-radiation-alt', 'fa-biohazard'][s] || 'fa-info-circle', + async fetchTabData() { + this.destroyCharts(); + const tab = this.activeTab; + if (this.loading[tab]) return; + this.loading[tab] = true; + try { + if (tab === 'overview' && !this.generalData) { + const res = await axios.get(`${window.TT_CONFIG.BASE_URL}/DeviceMonitoring/generalData`, { params: { hostId: this.hostId } }); + this.generalData = res.data; + } else if (tab === 'interfaces' && this.allInterfaces.length === 0) { + const res = await axios.get(`${window.TT_CONFIG.BASE_URL}/DeviceMonitoring/listInterfaces`, { params: { hostId: this.hostId } }); + this.allInterfaces = res.data; + } else if (tab === 'problems' && this.problemData.length === 0) { + const res = await axios.get(`${window.TT_CONFIG.BASE_URL}/DeviceMonitoring/getProblems`, { params: { hostId: this.hostId } }); + this.problemData = res.data; + } + } catch (e) { console.error(`Failed to load data for tab ${tab}`, e); window.notify('error', `Laden von ${tab}-Daten fehlgeschlagen.`); } + finally { this.loading[tab] = false; } + }, + async fetchInterfaceHistory() { + this.destroyCharts(); + if (this.selectedInterfaces.length === 0) return; + const itemIds = this.selectedInterfacesData.flatMap(i => [i.rx?.itemid, i.tx?.itemid]).filter(Boolean); + if (itemIds.length === 0) return; + + this.loading.interfaces = true; + try { + const res = await axios.post(`${window.TT_CONFIG.BASE_URL}/DeviceMonitoring/interfaceData`, { itemIds, timeRange: this.interfaceTimeRange }); + this.interfaceChartData = res.data; + this.$nextTick(() => this.renderAllCharts()); + } catch(e) { console.error('Failed to fetch interface history', e); } + finally { this.loading.interfaces = false; } + }, + renderAllCharts() { + this.selectedInterfacesData.forEach(async (iface) => { + await this.$nextTick(); + const canvas = this.$refs['chartCanvas-' + iface.name]?.[0]; + if (!canvas) return; + + if (this.chartInstances[iface.name]) { + this.chartInstances[iface.name].destroy(); + } + + this.chartInstances[iface.name] = new Chart(canvas.getContext('2d'), { + type: 'line', + data: { + datasets: [ + { + label: 'Empfangen', + data: this.interfaceChartData[iface.rx?.itemid] || [], + borderColor: '#4CAF50', + borderWidth: 0, + barPercentage: 1, + categoryPercentage: 1, + fill: true, + backgroundColor: 'rgba(76, 175, 80, 0.8)', + pointRadius: 0, + tension: 0.1 + }, + { + label: 'Gesendet', + data: this.interfaceChartData[iface.tx?.itemid] || [], + borderColor: '#2196F3', + borderWidth: 0, + barPercentage: 1, + categoryPercentage: 1, + fill: true, + backgroundColor: 'rgba(33, 150, 243, 0.8)', + pointRadius: 0, + tension: 0.1 + } + ] + }, + options: { + responsive: true, maintainAspectRatio: true, + scales: { + x: { + type: 'time', + // GEÄNDERT: Deutsches Datumsformat und Locale + time: { tooltipFormat: 'DD.MM.YYYY HH:mm:ss' }, + adapters: { date: { locale: 'de' } } + }, + y: { + beginAtZero: true, + title: { display: true, text: 'Mbps' } + } + }, + plugins: { + legend: { + display: true, + position: 'bottom', + labels: { boxWidth: 12, font: { size: 10 } } + } + } + } + }); + }); + }, + openLiveChartPopup(iface) { + const url = `${window.TT_CONFIG.BASE_URL}/DeviceMonitoring/liveGraphPage?rx_id=${iface.rx?.itemid || ''}&tx_id=${iface.tx?.itemid || ''}&name=${encodeURIComponent(iface.name)}`; + window.open(url, `livegraph_${iface.name.replace(/[^a-zA-Z0-9]/g, "_")}`, 'width=800,height=450,resizable=yes,scrollbars=yes'); + }, + destroyCharts() { + Object.values(this.chartInstances).forEach(c => c.destroy()); + this.chartInstances = {}; + }, + }, + watch: { + activeTab: 'fetchTabData', + selectedInterfaces: 'fetchInterfaceHistory', + interfaceTimeRange: 'fetchInterfaceHistory' + } +}); \ No newline at end of file diff --git a/public/js/pages/DeviceGraphing/DeviceGraphing.js b/public/js/pages/DeviceGraphing/DeviceGraphing.js deleted file mode 100644 index 9ce5dc5c5..000000000 --- a/public/js/pages/DeviceGraphing/DeviceGraphing.js +++ /dev/null @@ -1,133 +0,0 @@ -Vue.component('tt-graph', { - template: ` - - - - -
-
-
- `, - props: ['data', 'labels', 'header'], - data() { - return { - chart: null, - width: 400, - height: 220 - } - }, - mounted() { - const ctx = this.$refs.chart.getContext('2d'); - this.chart = new Chart(ctx, { - type: 'line', - data: { - labels: this.labels, - datasets: this.data - }, - options: { - scales: { - y: { - beginAtZero: true - }, - x: { - type: 'time', - time: { - // time is epoch - parser: 'X', - // unit: 'hour', - displayFormats: { - minute: 'DD.M. HH:mm', - } - }, - // min: '00:00:00', - // max: '24:00:00', - ticks: { - autoSkipPadding: 25, - autoSkip: true, - maxRotation: 0 - } - } - }, - responsive: true, - }, - }); - - // set width and height to the canvas element actual width and height - this.width = this.$refs.container.width; - this.height = this.$refs.container.height; - - } -}) - -Vue.component('device-graphing', { - template: ` -
-

{{ hostname }}

- - - - -
- `, - data() { - return { - graphs: [], - hostname: '' - } - }, - async mounted() { - // get hostname from url params - this.hostname = new URLSearchParams(window.location.search).get('hostname'); - console.log(this.hostname); - // get id from url params - const id = new URLSearchParams(window.location.search).get('id'); - const response = await axios.get('/Graphing/data?id=' + id); - const data = response.data; - - const graphs = {}; - - for (const item in data) { - // Create graphs.[item.name.split(':')[0]] if it doesn't exist - if (!graphs[data[item].name.split(':')[0]]) { - graphs[data[item].name.split(':')[0]] = { - name: data[item].name.split(':')[0], - data: [], - labels: [] - } - } - - // Add the data to the graph but check if it's received or sent and already exists - - if (data[item].name.split(':')[1].includes('received') && graphs[data[item].name.split(':')[0]].data.find(data => data.label.includes('received'))) { - continue; - } - - if (data[item].name.split(':')[1].includes('sent') && graphs[data[item].name.split(':')[0]].data.find(data => data.label.includes('sent'))) { - continue; - } - - - graphs[data[item].name.split(':')[0]].data.push({ - label: data[item].name.split(':')[1], - data: data[item].values.map(value => value.value), - fill: false, - borderColor: data[item].name.includes('received') ? 'rgb(75, 192, 192)' : 'rgb(192, 75, 75)', - tension: 0.1 - }); - } - - // Add the labels to the this.graphs - for (const graph in graphs) { - graphs[graph].labels = data[Object.keys(data).find(key => data[key].name.includes(graph))].values.map(value => value.clock); - this.graphs.push(graphs[graph]); - } - - - - - } -}); \ No newline at end of file