@@ -245,6 +245,106 @@ foreach ($rackLinksByKey as $entry) {
|
|||||||
'sample_connection_id' => (int)($entry['sample_connection_id'] ?? 0),
|
'sample_connection_id' => (int)($entry['sample_connection_id'] ?? 0),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$deviceIdByDevicePort = [];
|
||||||
|
foreach ($sql->get(
|
||||||
|
"SELECT id, device_id
|
||||||
|
FROM device_ports",
|
||||||
|
"",
|
||||||
|
[]
|
||||||
|
) as $row) {
|
||||||
|
$portId = (int)($row['id'] ?? 0);
|
||||||
|
$deviceId = (int)($row['device_id'] ?? 0);
|
||||||
|
if ($portId <= 0 || $deviceId <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$deviceIdByDevicePort[$portId] = $deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
$deviceIdByModulePort = [];
|
||||||
|
foreach ($sql->get(
|
||||||
|
"SELECT mp.id AS port_id, MIN(dp.device_id) AS device_id
|
||||||
|
FROM module_ports mp
|
||||||
|
JOIN modules m ON m.id = mp.module_id
|
||||||
|
JOIN device_port_modules dpm ON dpm.module_id = m.id
|
||||||
|
JOIN device_ports dp ON dp.id = dpm.device_port_id
|
||||||
|
GROUP BY mp.id",
|
||||||
|
"",
|
||||||
|
[]
|
||||||
|
) as $row) {
|
||||||
|
$portId = (int)($row['port_id'] ?? 0);
|
||||||
|
$deviceId = (int)($row['device_id'] ?? 0);
|
||||||
|
if ($portId <= 0 || $deviceId <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$deviceIdByModulePort[$portId] = $deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolveDeviceId = static function (string $endpointType, int $endpointId) use ($deviceIdByDevicePort, $deviceIdByModulePort): int {
|
||||||
|
if ($endpointId <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = strtolower(trim($endpointType));
|
||||||
|
if ($type === 'device' || $type === 'device_ports') {
|
||||||
|
return (int)($deviceIdByDevicePort[$endpointId] ?? 0);
|
||||||
|
}
|
||||||
|
if ($type === 'module' || $type === 'module_ports') {
|
||||||
|
return (int)($deviceIdByModulePort[$endpointId] ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
$deviceLinksByKey = [];
|
||||||
|
foreach ($sql->get(
|
||||||
|
"SELECT id, port_a_type, port_a_id, port_b_type, port_b_id
|
||||||
|
FROM connections",
|
||||||
|
"",
|
||||||
|
[]
|
||||||
|
) as $row) {
|
||||||
|
$deviceA = $resolveDeviceId((string)($row['port_a_type'] ?? ''), (int)($row['port_a_id'] ?? 0));
|
||||||
|
$deviceB = $resolveDeviceId((string)($row['port_b_type'] ?? ''), (int)($row['port_b_id'] ?? 0));
|
||||||
|
if ($deviceA <= 0 || $deviceB <= 0 || $deviceA === $deviceB) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$from = min($deviceA, $deviceB);
|
||||||
|
$to = max($deviceA, $deviceB);
|
||||||
|
$key = $from . ':' . $to;
|
||||||
|
if (!isset($deviceLinksByKey[$key])) {
|
||||||
|
$deviceLinksByKey[$key] = [
|
||||||
|
'from_device_id' => $from,
|
||||||
|
'to_device_id' => $to,
|
||||||
|
'count' => 0,
|
||||||
|
'sample_connection_id' => (int)($row['id'] ?? 0)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$deviceLinksByKey[$key]['count']++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$deviceNameById = [];
|
||||||
|
foreach ($topologyPayload as $entry) {
|
||||||
|
$deviceId = (int)($entry['device_id'] ?? 0);
|
||||||
|
if ($deviceId <= 0 || isset($deviceNameById[$deviceId])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$deviceNameById[$deviceId] = (string)($entry['device_name'] ?? ('Gerät #' . $deviceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
$deviceLinkPayload = [];
|
||||||
|
foreach ($deviceLinksByKey as $entry) {
|
||||||
|
$fromId = (int)($entry['from_device_id'] ?? 0);
|
||||||
|
$toId = (int)($entry['to_device_id'] ?? 0);
|
||||||
|
$deviceLinkPayload[] = [
|
||||||
|
'from_device_id' => $fromId,
|
||||||
|
'to_device_id' => $toId,
|
||||||
|
'count' => (int)($entry['count'] ?? 0),
|
||||||
|
'from_device_name' => (string)($deviceNameById[$fromId] ?? ('Gerät #' . $fromId)),
|
||||||
|
'to_device_name' => (string)($deviceNameById[$toId] ?? ('Gerät #' . $toId)),
|
||||||
|
'sample_connection_id' => (int)($entry['sample_connection_id'] ?? 0),
|
||||||
|
];
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="dashboard">
|
<div class="dashboard">
|
||||||
@@ -343,6 +443,7 @@ foreach ($rackLinksByKey as $entry) {
|
|||||||
|
|
||||||
<script id="dashboard-topology-data" type="application/json"><?php echo json_encode($topologyPayload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?></script>
|
<script id="dashboard-topology-data" type="application/json"><?php echo json_encode($topologyPayload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?></script>
|
||||||
<script id="dashboard-topology-links" type="application/json"><?php echo json_encode($rackLinkPayload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?></script>
|
<script id="dashboard-topology-links" type="application/json"><?php echo json_encode($rackLinkPayload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?></script>
|
||||||
|
<script id="dashboard-topology-device-links" type="application/json"><?php echo json_encode($deviceLinkPayload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?></script>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
const root = document.getElementById('dashboard-topology-wall');
|
const root = document.getElementById('dashboard-topology-wall');
|
||||||
@@ -366,8 +467,10 @@ foreach ($rackLinksByKey as $entry) {
|
|||||||
|
|
||||||
const dataTag = document.getElementById('dashboard-topology-data');
|
const dataTag = document.getElementById('dashboard-topology-data');
|
||||||
const linkTag = document.getElementById('dashboard-topology-links');
|
const linkTag = document.getElementById('dashboard-topology-links');
|
||||||
|
const deviceLinkTag = document.getElementById('dashboard-topology-device-links');
|
||||||
let nodes = [];
|
let nodes = [];
|
||||||
let rackLinks = [];
|
let rackLinks = [];
|
||||||
|
let deviceLinks = [];
|
||||||
try {
|
try {
|
||||||
nodes = JSON.parse((dataTag && dataTag.textContent) || '[]');
|
nodes = JSON.parse((dataTag && dataTag.textContent) || '[]');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -378,6 +481,11 @@ foreach ($rackLinksByKey as $entry) {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
rackLinks = [];
|
rackLinks = [];
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
deviceLinks = JSON.parse((deviceLinkTag && deviceLinkTag.textContent) || '[]');
|
||||||
|
} catch (error) {
|
||||||
|
deviceLinks = [];
|
||||||
|
}
|
||||||
|
|
||||||
const scene = { width: 2400, height: 1400 };
|
const scene = { width: 2400, height: 1400 };
|
||||||
let view = { x: 0, y: 0, width: scene.width, height: scene.height };
|
let view = { x: 0, y: 0, width: scene.width, height: scene.height };
|
||||||
@@ -513,6 +621,7 @@ foreach ($rackLinksByKey as $entry) {
|
|||||||
|
|
||||||
const positioned = [];
|
const positioned = [];
|
||||||
const rackCenters = new Map();
|
const rackCenters = new Map();
|
||||||
|
const deviceCenters = new Map();
|
||||||
let maxY = 1400;
|
let maxY = 1400;
|
||||||
let locationIndex = 0;
|
let locationIndex = 0;
|
||||||
|
|
||||||
@@ -607,6 +716,9 @@ foreach ($rackLinksByKey as $entry) {
|
|||||||
port_count: Number(device.port_count || 0),
|
port_count: Number(device.port_count || 0),
|
||||||
port_preview: Array.isArray(device.port_preview) ? device.port_preview : []
|
port_preview: Array.isArray(device.port_preview) ? device.port_preview : []
|
||||||
});
|
});
|
||||||
|
if (Number(device.device_id || 0) > 0) {
|
||||||
|
deviceCenters.set(Number(device.device_id || 0), { x, y });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
rackCursorY += rackHeight + 16;
|
rackCursorY += rackHeight + 16;
|
||||||
@@ -651,7 +763,7 @@ foreach ($rackLinksByKey as $entry) {
|
|||||||
|
|
||||||
const width = Math.max(2400, 220 + locationIndex * 760);
|
const width = Math.max(2400, 220 + locationIndex * 760);
|
||||||
const height = Math.max(1400, Math.ceil(maxY));
|
const height = Math.max(1400, Math.ceil(maxY));
|
||||||
return { entries: positioned, rackCenters, width, height };
|
return { entries: positioned, rackCenters, deviceCenters, width, height };
|
||||||
}
|
}
|
||||||
|
|
||||||
function showOverlay(item) {
|
function showOverlay(item) {
|
||||||
@@ -898,6 +1010,30 @@ foreach ($rackLinksByKey as $entry) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
deviceLinks.forEach((link) => {
|
||||||
|
const fromDeviceId = Number(link.from_device_id || 0);
|
||||||
|
const toDeviceId = Number(link.to_device_id || 0);
|
||||||
|
const count = Math.max(1, Number(link.count || 1));
|
||||||
|
const fromPoint = layout.deviceCenters.get(fromDeviceId);
|
||||||
|
const toPoint = layout.deviceCenters.get(toDeviceId);
|
||||||
|
if (!fromPoint || !toPoint) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const line = svgElement('line');
|
||||||
|
line.setAttribute('x1', String(fromPoint.x));
|
||||||
|
line.setAttribute('y1', String(fromPoint.y));
|
||||||
|
line.setAttribute('x2', String(toPoint.x));
|
||||||
|
line.setAttribute('y2', String(toPoint.y));
|
||||||
|
line.setAttribute('class', 'topology-device-link-line');
|
||||||
|
line.setAttribute('stroke-width', String(Math.min(5, 1 + (count * 0.4))));
|
||||||
|
|
||||||
|
const title = svgElement('title');
|
||||||
|
title.textContent = `${link.from_device_name || `Gerät ${fromDeviceId}`} <-> ${link.to_device_name || `Gerät ${toDeviceId}`}: ${count} Verbindungen`;
|
||||||
|
line.appendChild(title);
|
||||||
|
connectionLayer.appendChild(line);
|
||||||
|
});
|
||||||
|
|
||||||
rackLinks.forEach((link) => {
|
rackLinks.forEach((link) => {
|
||||||
const fromRackId = Number(link.from_rack_id || 0);
|
const fromRackId = Number(link.from_rack_id || 0);
|
||||||
const toRackId = Number(link.to_rack_id || 0);
|
const toRackId = Number(link.to_rack_id || 0);
|
||||||
@@ -1262,6 +1398,13 @@ foreach ($rackLinksByKey as $entry) {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topology-device-link-line {
|
||||||
|
stroke: #56a17f;
|
||||||
|
stroke-opacity: 0.5;
|
||||||
|
stroke-linecap: round;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.topology-connection-line:hover,
|
.topology-connection-line:hover,
|
||||||
.topology-connection-line:focus,
|
.topology-connection-line:focus,
|
||||||
.topology-connection-line.active {
|
.topology-connection-line.active {
|
||||||
|
|||||||
Reference in New Issue
Block a user