mirror of
https://github.com/jcreek/OpenNetworkDiagram.git
synced 2026-07-14 03:23:44 +00:00
feat(*): Track cable type, colour and length on port connections
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
deletePort,
|
deletePort,
|
||||||
renameOwner,
|
renameOwner,
|
||||||
renamePort,
|
renamePort,
|
||||||
|
setPortCable,
|
||||||
setPortConnection,
|
setPortConnection,
|
||||||
type OwnerKind,
|
type OwnerKind,
|
||||||
withReconciledConnections
|
withReconciledConnections
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
import { validateNetworkData, type ValidationIssue } from '$lib/data/networkSchema';
|
import { validateNetworkData, type ValidationIssue } from '$lib/data/networkSchema';
|
||||||
import transformNetworkDataToGraph from '$lib/graph/transformNetworkData';
|
import transformNetworkDataToGraph from '$lib/graph/transformNetworkData';
|
||||||
import { computeSearchMatches } from '$lib/graph/searchHighlight';
|
import { computeSearchMatches } from '$lib/graph/searchHighlight';
|
||||||
import type { GraphNodeData, GraphTransformResult } from '$lib/graph/types';
|
import type { GraphEdgeData, GraphNodeData, GraphTransformResult } from '$lib/graph/types';
|
||||||
import { themeMode, type ThemeMode } from '$lib/stores/theme';
|
import { themeMode, type ThemeMode } from '$lib/stores/theme';
|
||||||
import type { NetworkData, Port } from '$lib/types';
|
import type { NetworkData, Port } from '$lib/types';
|
||||||
import Modal from './Modal.svelte';
|
import Modal from './Modal.svelte';
|
||||||
@@ -571,6 +572,12 @@
|
|||||||
color: theme === 'dark' ? '#94a3b8' : '#64748b'
|
color: theme === 'dark' ? '#94a3b8' : '#64748b'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
selector: 'edge[cableColor]',
|
||||||
|
style: {
|
||||||
|
'line-color': 'data(cableColor)'
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
selector: 'node[vlanColor]',
|
selector: 'node[vlanColor]',
|
||||||
style: {
|
style: {
|
||||||
@@ -1417,6 +1424,31 @@
|
|||||||
updateDevicePortConnection(deviceIndex, portIndex, next);
|
updateDevicePortConnection(deviceIndex, portIndex, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updatePortCableField(
|
||||||
|
kind: OwnerKind,
|
||||||
|
ownerName: string,
|
||||||
|
port: Port,
|
||||||
|
field: 'type' | 'color' | 'lengthM',
|
||||||
|
value: string
|
||||||
|
) {
|
||||||
|
const current: NonNullable<NonNullable<Port['connectedTo']>['cable']> = {
|
||||||
|
...(port.connectedTo?.cable ?? {})
|
||||||
|
};
|
||||||
|
if (field === 'lengthM') {
|
||||||
|
const parsed = Number(value);
|
||||||
|
if (value.trim() && Number.isFinite(parsed) && parsed >= 0) {
|
||||||
|
current.lengthM = parsed;
|
||||||
|
} else {
|
||||||
|
delete current.lengthM;
|
||||||
|
}
|
||||||
|
} else if (value.trim()) {
|
||||||
|
current[field] = value.trim();
|
||||||
|
} else {
|
||||||
|
delete current[field];
|
||||||
|
}
|
||||||
|
applyDraft(setPortCable(networkData, kind, ownerName, port.portName, current));
|
||||||
|
}
|
||||||
|
|
||||||
function deleteMachine(machineIndex: number) {
|
function deleteMachine(machineIndex: number) {
|
||||||
const machine = networkData.machines[machineIndex];
|
const machine = networkData.machines[machineIndex];
|
||||||
if (!machine) {
|
if (!machine) {
|
||||||
@@ -1734,6 +1766,33 @@
|
|||||||
tooltip.visible = false;
|
tooltip.visible = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cy.on('mouseover', 'edge[kind = "physical"]', (event) => {
|
||||||
|
const edge = event.target;
|
||||||
|
const data = edge.data() as GraphEdgeData;
|
||||||
|
const parts = [
|
||||||
|
`${data.sourcePort ?? '?'} ↔ ${data.targetPort ?? '?'}`,
|
||||||
|
...(data.speedGbps ? [`${data.speedGbps}GbE`] : []),
|
||||||
|
...(data.cableType ? [data.cableType] : []),
|
||||||
|
...(data.cableColorName ? [data.cableColorName] : []),
|
||||||
|
...(typeof data.cableLengthM === 'number' ? [`${data.cableLengthM}m`] : [])
|
||||||
|
];
|
||||||
|
const midpoint = edge.renderedMidpoint();
|
||||||
|
const clampedPosition = clampTooltipPosition(
|
||||||
|
midpoint.x + tooltipOffset.x,
|
||||||
|
midpoint.y + tooltipOffset.y
|
||||||
|
);
|
||||||
|
tooltip = {
|
||||||
|
visible: true,
|
||||||
|
text: parts.join(' · '),
|
||||||
|
x: clampedPosition.x,
|
||||||
|
y: clampedPosition.y
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.on('mouseout', 'edge', () => {
|
||||||
|
tooltip.visible = false;
|
||||||
|
});
|
||||||
|
|
||||||
cy.on('pan zoom', () => {
|
cy.on('pan zoom', () => {
|
||||||
tooltip.visible = false;
|
tooltip.visible = false;
|
||||||
});
|
});
|
||||||
@@ -2150,6 +2209,27 @@
|
|||||||
<p class="icon-results-empty">No icons match your search.</p>
|
<p class="icon-results-empty">No icons match your search.</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
<datalist id="cable-type-options">
|
||||||
|
<option value="Cat5e"></option>
|
||||||
|
<option value="Cat6"></option>
|
||||||
|
<option value="Cat6a"></option>
|
||||||
|
<option value="Cat7"></option>
|
||||||
|
<option value="Cat8"></option>
|
||||||
|
<option value="DAC"></option>
|
||||||
|
<option value="Fibre"></option>
|
||||||
|
</datalist>
|
||||||
|
<datalist id="cable-color-options">
|
||||||
|
<option value="blue"></option>
|
||||||
|
<option value="red"></option>
|
||||||
|
<option value="green"></option>
|
||||||
|
<option value="yellow"></option>
|
||||||
|
<option value="orange"></option>
|
||||||
|
<option value="purple"></option>
|
||||||
|
<option value="pink"></option>
|
||||||
|
<option value="white"></option>
|
||||||
|
<option value="grey"></option>
|
||||||
|
<option value="black"></option>
|
||||||
|
</datalist>
|
||||||
{#if selectedTarget?.type === 'machine' && selectedMachine}
|
{#if selectedTarget?.type === 'machine' && selectedMachine}
|
||||||
<div class="inline-actions">
|
<div class="inline-actions">
|
||||||
{#if selectedMachineHostId && selectedMachineVmCount > 0}
|
{#if selectedMachineHostId && selectedMachineVmCount > 0}
|
||||||
@@ -2452,6 +2532,57 @@
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
{#if port.connectedTo}
|
||||||
|
<label>
|
||||||
|
Cable Type
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
list="cable-type-options"
|
||||||
|
value={port.connectedTo.cable?.type ?? ''}
|
||||||
|
on:change={(event) =>
|
||||||
|
updatePortCableField(
|
||||||
|
'machine',
|
||||||
|
selectedMachine.machineName,
|
||||||
|
port,
|
||||||
|
'type',
|
||||||
|
(event.currentTarget as HTMLInputElement).value
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Cable Colour
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
list="cable-color-options"
|
||||||
|
value={port.connectedTo.cable?.color ?? ''}
|
||||||
|
on:change={(event) =>
|
||||||
|
updatePortCableField(
|
||||||
|
'machine',
|
||||||
|
selectedMachine.machineName,
|
||||||
|
port,
|
||||||
|
'color',
|
||||||
|
(event.currentTarget as HTMLInputElement).value
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Cable Length (m)
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.5"
|
||||||
|
value={port.connectedTo.cable?.lengthM ?? ''}
|
||||||
|
on:change={(event) =>
|
||||||
|
updatePortCableField(
|
||||||
|
'machine',
|
||||||
|
selectedMachine.machineName,
|
||||||
|
port,
|
||||||
|
'lengthM',
|
||||||
|
(event.currentTarget as HTMLInputElement).value
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
{/if}
|
||||||
<div class="item-actions">
|
<div class="item-actions">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -2628,6 +2759,57 @@
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
{#if port.connectedTo}
|
||||||
|
<label>
|
||||||
|
Cable Type
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
list="cable-type-options"
|
||||||
|
value={port.connectedTo.cable?.type ?? ''}
|
||||||
|
on:change={(event) =>
|
||||||
|
updatePortCableField(
|
||||||
|
'device',
|
||||||
|
selectedDevice.name,
|
||||||
|
port,
|
||||||
|
'type',
|
||||||
|
(event.currentTarget as HTMLInputElement).value
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Cable Colour
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
list="cable-color-options"
|
||||||
|
value={port.connectedTo.cable?.color ?? ''}
|
||||||
|
on:change={(event) =>
|
||||||
|
updatePortCableField(
|
||||||
|
'device',
|
||||||
|
selectedDevice.name,
|
||||||
|
port,
|
||||||
|
'color',
|
||||||
|
(event.currentTarget as HTMLInputElement).value
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Cable Length (m)
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.5"
|
||||||
|
value={port.connectedTo.cable?.lengthM ?? ''}
|
||||||
|
on:change={(event) =>
|
||||||
|
updatePortCableField(
|
||||||
|
'device',
|
||||||
|
selectedDevice.name,
|
||||||
|
port,
|
||||||
|
'lengthM',
|
||||||
|
(event.currentTarget as HTMLInputElement).value
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
{/if}
|
||||||
<div class="item-actions">
|
<div class="item-actions">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Machine, NetworkData, NetworkDevice, Port, VM } from '../types';
|
import type { CableInfo, Machine, NetworkData, NetworkDevice, Port, VM } from '../types';
|
||||||
|
|
||||||
export type OwnerKind = 'machine' | 'device';
|
export type OwnerKind = 'machine' | 'device';
|
||||||
|
|
||||||
@@ -119,9 +119,10 @@ function ensureReciprocalConnections(data: NetworkData) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const expectedReciprocal = {
|
const expectedReciprocal: Port['connectedTo'] = {
|
||||||
device: owner.name,
|
device: owner.name,
|
||||||
port: port.portName
|
port: port.portName,
|
||||||
|
...(port.connectedTo.cable ? { cable: deepClone(port.connectedTo.cable) } : {})
|
||||||
};
|
};
|
||||||
if (!targetPort.connectedTo) {
|
if (!targetPort.connectedTo) {
|
||||||
targetPort.connectedTo = expectedReciprocal;
|
targetPort.connectedTo = expectedReciprocal;
|
||||||
@@ -132,6 +133,19 @@ function ensureReciprocalConnections(data: NetworkData) {
|
|||||||
equalsIgnoreCase(targetPort.connectedTo.device, owner.name) &&
|
equalsIgnoreCase(targetPort.connectedTo.device, owner.name) &&
|
||||||
equalsIgnoreCase(targetPort.connectedTo.port, port.portName);
|
equalsIgnoreCase(targetPort.connectedTo.port, port.portName);
|
||||||
if (hasMatchingReciprocal) {
|
if (hasMatchingReciprocal) {
|
||||||
|
// keep cable metadata identical on both ends; the lexicographically
|
||||||
|
// smaller owner:port key is the canonical copy so the sync is deterministic
|
||||||
|
const ownKey = `${owner.name}:${port.portName}`.toLowerCase();
|
||||||
|
const targetKey = `${targetOwner.name}:${targetPort.portName}`.toLowerCase();
|
||||||
|
const [from, to] =
|
||||||
|
ownKey <= targetKey
|
||||||
|
? [port.connectedTo, targetPort.connectedTo]
|
||||||
|
: [targetPort.connectedTo, port.connectedTo];
|
||||||
|
if (from.cable) {
|
||||||
|
to.cable = deepClone(from.cable);
|
||||||
|
} else {
|
||||||
|
delete to.cable;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -330,6 +344,47 @@ export function deletePort(
|
|||||||
return cloned;
|
return cloned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setPortCable(
|
||||||
|
data: NetworkData,
|
||||||
|
kind: OwnerKind,
|
||||||
|
ownerName: string,
|
||||||
|
portName: string,
|
||||||
|
cable: CableInfo | undefined
|
||||||
|
): NetworkData {
|
||||||
|
const cloned = cloneNetworkData(data);
|
||||||
|
const owner = findOwnerByKindAndName(cloned, kind, ownerName);
|
||||||
|
const port = owner ? findPort(owner, portName) : undefined;
|
||||||
|
if (!owner || !port?.connectedTo) {
|
||||||
|
return cloned;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entries = Object.entries(cable ?? {}).filter(([, value]) =>
|
||||||
|
typeof value === 'number' ? Number.isFinite(value) : Boolean(value)
|
||||||
|
);
|
||||||
|
const normalized = entries.length > 0 ? (Object.fromEntries(entries) as CableInfo) : undefined;
|
||||||
|
|
||||||
|
if (normalized) {
|
||||||
|
port.connectedTo.cable = deepClone(normalized);
|
||||||
|
} else {
|
||||||
|
delete port.connectedTo.cable;
|
||||||
|
}
|
||||||
|
|
||||||
|
const peerOwner = findOwnerByName(cloned, port.connectedTo.device);
|
||||||
|
const peerPort = peerOwner ? findPort(peerOwner, port.connectedTo.port) : undefined;
|
||||||
|
if (
|
||||||
|
peerPort?.connectedTo &&
|
||||||
|
equalsIgnoreCase(peerPort.connectedTo.device, owner.name) &&
|
||||||
|
equalsIgnoreCase(peerPort.connectedTo.port, port.portName)
|
||||||
|
) {
|
||||||
|
if (normalized) {
|
||||||
|
peerPort.connectedTo.cable = deepClone(normalized);
|
||||||
|
} else {
|
||||||
|
delete peerPort.connectedTo.cable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cloned;
|
||||||
|
}
|
||||||
|
|
||||||
export function setPortConnection(
|
export function setPortConnection(
|
||||||
data: NetworkData,
|
data: NetworkData,
|
||||||
kind: OwnerKind,
|
kind: OwnerKind,
|
||||||
|
|||||||
@@ -57,6 +57,28 @@ function connectionKey(a: string, b: string): string {
|
|||||||
return a < b ? `${a}|${b}` : `${b}|${a}`;
|
return a < b ? `${a}|${b}` : `${b}|${a}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cableColorHex: Record<string, string> = {
|
||||||
|
blue: '#3b82f6',
|
||||||
|
red: '#ef4444',
|
||||||
|
green: '#22c55e',
|
||||||
|
yellow: '#eab308',
|
||||||
|
orange: '#f97316',
|
||||||
|
purple: '#a855f7',
|
||||||
|
pink: '#ec4899',
|
||||||
|
white: '#e2e8f0',
|
||||||
|
grey: '#94a3b8',
|
||||||
|
gray: '#94a3b8',
|
||||||
|
black: '#334155'
|
||||||
|
};
|
||||||
|
|
||||||
|
function resolveCableColor(name: string | undefined): string | undefined {
|
||||||
|
if (!name) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const trimmed = name.trim();
|
||||||
|
return cableColorHex[trimmed.toLowerCase()] ?? (/^#[0-9a-fA-F]{3,8}$/.test(trimmed) ? trimmed : undefined);
|
||||||
|
}
|
||||||
|
|
||||||
function buildVlanResolver(subnets: Subnet[] | undefined) {
|
function buildVlanResolver(subnets: Subnet[] | undefined) {
|
||||||
const vlanSubnets = (subnets ?? [])
|
const vlanSubnets = (subnets ?? [])
|
||||||
.filter((subnet) => typeof subnet.vlanId === 'number')
|
.filter((subnet) => typeof subnet.vlanId === 'number')
|
||||||
@@ -315,6 +337,7 @@ export default function transformNetworkDataToGraph(data: NetworkData): GraphTra
|
|||||||
|
|
||||||
physicalEdgeCounter += 1;
|
physicalEdgeCounter += 1;
|
||||||
const speedGbps = edgeSpeed(port.speedGbps, targetPort.speedGbps);
|
const speedGbps = edgeSpeed(port.speedGbps, targetPort.speedGbps);
|
||||||
|
const cable = port.connectedTo?.cable ?? targetPort.connectedTo?.cable;
|
||||||
|
|
||||||
edges.push({
|
edges.push({
|
||||||
data: {
|
data: {
|
||||||
@@ -326,7 +349,11 @@ export default function transformNetworkDataToGraph(data: NetworkData): GraphTra
|
|||||||
sourcePort: port.portName,
|
sourcePort: port.portName,
|
||||||
targetPort: connectedTo.port,
|
targetPort: connectedTo.port,
|
||||||
speedGbps,
|
speedGbps,
|
||||||
reciprocal
|
reciprocal,
|
||||||
|
...(cable?.type ? { cableType: cable.type } : {}),
|
||||||
|
...(cable?.color ? { cableColorName: cable.color } : {}),
|
||||||
|
...(resolveCableColor(cable?.color) ? { cableColor: resolveCableColor(cable?.color) } : {}),
|
||||||
|
...(typeof cable?.lengthM === 'number' ? { cableLengthM: cable.lengthM } : {})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ export interface PortDetails {
|
|||||||
connectedTo?: {
|
connectedTo?: {
|
||||||
device: string;
|
device: string;
|
||||||
port: string;
|
port: string;
|
||||||
|
cable?: {
|
||||||
|
type?: string;
|
||||||
|
color?: string;
|
||||||
|
lengthM?: number;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +84,10 @@ export interface GraphEdgeData {
|
|||||||
targetPort?: string;
|
targetPort?: string;
|
||||||
speedGbps?: number;
|
speedGbps?: number;
|
||||||
reciprocal?: boolean;
|
reciprocal?: boolean;
|
||||||
|
cableType?: string;
|
||||||
|
cableColorName?: string;
|
||||||
|
cableColor?: string;
|
||||||
|
cableLengthM?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GraphNodeElement {
|
export interface GraphNodeElement {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef {{ path: string; message: string }} ValidationIssue
|
* @typedef {{ path: string; message: string }} ValidationIssue
|
||||||
* @typedef {{ device: string; port: string }} ConnectedTo
|
* @typedef {{ type?: string; color?: string; lengthM?: number }} CableInfo
|
||||||
|
* @typedef {{ device: string; port: string; cable?: CableInfo }} ConnectedTo
|
||||||
* @typedef {{ portName: string; speedGbps?: number; macAddress?: string; connectedTo?: ConnectedTo }} Port
|
* @typedef {{ portName: string; speedGbps?: number; macAddress?: string; connectedTo?: ConnectedTo }} Port
|
||||||
* @typedef {{ name: string; role: string; ipAddress: string; iconKey?: string; macAddress?: string }} VM
|
* @typedef {{ name: string; role: string; ipAddress: string; iconKey?: string; macAddress?: string }} VM
|
||||||
* @typedef {{ cpu: string; ram: string; networkPorts: number; networkPortSpeedGbps?: number; gpu?: string }} Hardware
|
* @typedef {{ cpu: string; ram: string; networkPorts: number; networkPortSpeedGbps?: number; gpu?: string }} Hardware
|
||||||
@@ -118,8 +119,36 @@ export function normalizePort(issues, path, value, ownerLabel) {
|
|||||||
} else {
|
} else {
|
||||||
const device = readString(issues, `${path}.connectedTo.device`, value.connectedTo.device);
|
const device = readString(issues, `${path}.connectedTo.device`, value.connectedTo.device);
|
||||||
const port = readString(issues, `${path}.connectedTo.port`, value.connectedTo.port);
|
const port = readString(issues, `${path}.connectedTo.port`, value.connectedTo.port);
|
||||||
|
let cable;
|
||||||
|
const cableRaw = value.connectedTo.cable;
|
||||||
|
if (cableRaw !== undefined) {
|
||||||
|
if (!isRecord(cableRaw)) {
|
||||||
|
issues.push({ path: `${path}.connectedTo.cable`, message: 'must be an object' });
|
||||||
|
} else {
|
||||||
|
const cableType = readString(issues, `${path}.connectedTo.cable.type`, cableRaw.type, {
|
||||||
|
optional: true,
|
||||||
|
allowEmpty: true
|
||||||
|
});
|
||||||
|
const cableColor = readString(issues, `${path}.connectedTo.cable.color`, cableRaw.color, {
|
||||||
|
optional: true,
|
||||||
|
allowEmpty: true
|
||||||
|
});
|
||||||
|
const lengthM = readNumber(issues, `${path}.connectedTo.cable.lengthM`, cableRaw.lengthM, {
|
||||||
|
optional: true,
|
||||||
|
min: 0
|
||||||
|
});
|
||||||
|
const candidate = {
|
||||||
|
...(cableType ? { type: cableType } : {}),
|
||||||
|
...(cableColor ? { color: cableColor } : {}),
|
||||||
|
...(typeof lengthM === 'number' ? { lengthM } : {})
|
||||||
|
};
|
||||||
|
if (Object.keys(candidate).length > 0) {
|
||||||
|
cable = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (device && port) {
|
if (device && port) {
|
||||||
connectedTo = { device, port };
|
connectedTo = { device, port, ...(cable ? { cable } : {}) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
|
export interface CableInfo {
|
||||||
|
type?: string;
|
||||||
|
color?: string;
|
||||||
|
lengthM?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ConnectedPortRef {
|
export interface ConnectedPortRef {
|
||||||
device: string;
|
device: string;
|
||||||
port: string;
|
port: string;
|
||||||
|
cable?: CableInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Port {
|
export interface Port {
|
||||||
|
|||||||
@@ -55,7 +55,12 @@
|
|||||||
"macAddress": "84:47:09:1c:aa:10",
|
"macAddress": "84:47:09:1c:aa:10",
|
||||||
"connectedTo": {
|
"connectedTo": {
|
||||||
"device": "Gigabit Switch",
|
"device": "Gigabit Switch",
|
||||||
"port": "1"
|
"port": "1",
|
||||||
|
"cable": {
|
||||||
|
"type": "Cat6",
|
||||||
|
"color": "blue",
|
||||||
|
"lengthM": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -67,7 +72,12 @@
|
|||||||
"speedGbps": 1,
|
"speedGbps": 1,
|
||||||
"connectedTo": {
|
"connectedTo": {
|
||||||
"device": "Gigabit Switch",
|
"device": "Gigabit Switch",
|
||||||
"port": "2"
|
"port": "2",
|
||||||
|
"cable": {
|
||||||
|
"type": "Cat6",
|
||||||
|
"color": "blue",
|
||||||
|
"lengthM": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -101,7 +111,12 @@
|
|||||||
"speedGbps": 1,
|
"speedGbps": 1,
|
||||||
"connectedTo": {
|
"connectedTo": {
|
||||||
"device": "Gigabit Switch",
|
"device": "Gigabit Switch",
|
||||||
"port": "3"
|
"port": "3",
|
||||||
|
"cable": {
|
||||||
|
"type": "Cat5e",
|
||||||
|
"color": "red",
|
||||||
|
"lengthM": 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -337,7 +352,12 @@
|
|||||||
"speedGbps": 1,
|
"speedGbps": 1,
|
||||||
"connectedTo": {
|
"connectedTo": {
|
||||||
"device": "ProxRouter",
|
"device": "ProxRouter",
|
||||||
"port": "eth0"
|
"port": "eth0",
|
||||||
|
"cable": {
|
||||||
|
"type": "Cat6",
|
||||||
|
"color": "blue",
|
||||||
|
"lengthM": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -345,7 +365,12 @@
|
|||||||
"speedGbps": 1,
|
"speedGbps": 1,
|
||||||
"connectedTo": {
|
"connectedTo": {
|
||||||
"device": "ProxRouter",
|
"device": "ProxRouter",
|
||||||
"port": "eth2"
|
"port": "eth2",
|
||||||
|
"cable": {
|
||||||
|
"type": "Cat6",
|
||||||
|
"color": "blue",
|
||||||
|
"lengthM": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -353,7 +378,12 @@
|
|||||||
"speedGbps": 1,
|
"speedGbps": 1,
|
||||||
"connectedTo": {
|
"connectedTo": {
|
||||||
"device": "Asustor NAS",
|
"device": "Asustor NAS",
|
||||||
"port": "eth0"
|
"port": "eth0",
|
||||||
|
"cable": {
|
||||||
|
"type": "Cat5e",
|
||||||
|
"color": "red",
|
||||||
|
"lengthM": 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user