Files
SenMeshNet/components/monitoring/deviceModels.ts
zhaolei 2f9f8da48e
Some checks failed
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
Initial commit: NetMesh smart ops platform with 3D topology, inspection reports, RCA and AI ops assistant
2026-10-15 13:53:04 +08:00

638 lines
20 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 精细 3D 设备模型工厂
* 参考 ITYUNWEI modelFactory.ts 设计
*/
import * as THREE from 'three';
export type DeviceStatus = 'normal' | 'warning' | 'critical' | 'offline';
export type DeviceType = 'server' | 'switch' | 'router' | 'firewall' | 'storage' | 'workstation';
const statusColors: Record<DeviceStatus, number> = {
normal: 0x86efac, // 浅绿色 tailwind green-300
warning: 0xffe600,
critical: 0xff4d4f,
offline: 0x94a3b8,
};
// 设备类型 accent 色(仅用于装饰性细节,normal 状态下主体发光统一用 statusColors.normal)
const typeColors: Record<DeviceType, number> = {
server: 0x86efac,
switch: 0x86efac,
router: 0x86efac,
firewall: 0x86efac,
storage: 0x86efac,
workstation: 0x86efac,
};
/** 创建设备基础材质:金属外壳 + 状态发光 */
function createBodyMaterial(status: DeviceStatus, type?: DeviceType): THREE.MeshStandardMaterial {
// 根据状态和类型决定发光颜色
let emissiveColor = statusColors[status];
if (status === 'normal' && type) {
emissiveColor = typeColors[type] || emissiveColor;
}
const intensity = status === 'normal' ? 0.9 : status === 'warning' ? 1.2 : status === 'critical' ? 1.6 : 0.3;
return new THREE.MeshStandardMaterial({
color: 0x5B7FA8,
metalness: 0.45,
roughness: 0.3,
emissive: emissiveColor,
emissiveIntensity: intensity,
side: THREE.DoubleSide,
});
}
/** LED 指示灯材质 */
function createLedMaterial(color: number, intensity = 0.8): THREE.MeshStandardMaterial {
return new THREE.MeshStandardMaterial({
color,
emissive: color,
emissiveIntensity: intensity,
metalness: 0.2,
roughness: 0.3,
side: THREE.DoubleSide,
});
}
/** 前面板塑料材质 */
function createPanelMaterial(): THREE.MeshStandardMaterial {
return new THREE.MeshStandardMaterial({
color: 0x334155,
metalness: 0.3,
roughness: 0.6,
side: THREE.DoubleSide,
});
}
/** 端口金属材质 */
function createPortMaterial(): THREE.MeshStandardMaterial {
return new THREE.MeshStandardMaterial({
color: 0x0f172a,
metalness: 0.9,
roughness: 0.2,
side: THREE.DoubleSide,
});
}
// ============================================================
// 1. 服务器 Server - 机架式服务器
// ============================================================
export function createServerModel(status: DeviceStatus, scale = 1.5): THREE.Group {
const group = new THREE.Group();
const bodyMat = createBodyMaterial(status, 'server');
const panelMat = createPanelMaterial();
// 主体机箱(扁平长方体)
const body = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.6, scale * 0.5, scale * 1.0),
bodyMat
);
body.userData.isBody = true;
group.add(body);
// 前面板
const frontPanel = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.62, scale * 0.46, scale * 0.04),
panelMat
);
frontPanel.position.z = scale * 0.52;
group.add(frontPanel);
// 前面板 - 硬盘槽位(4个)
for (let i = 0; i < 4; i++) {
const tray = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.3, scale * 0.35, scale * 0.02),
new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.5, roughness: 0.5 })
);
tray.position.set(-scale * 0.55 + i * scale * 0.37, 0, scale * 0.54);
group.add(tray);
// 硬盘槽位 LED 灯
const ledColor = i === 0 ? statusColors.normal : i === 1 ? statusColors.warning : 0x475569;
const led = new THREE.Mesh(
new THREE.SphereGeometry(0.02, 8, 8),
createLedMaterial(ledColor, i < 2 ? 1.0 : 0.5)
);
led.position.set(-scale * 0.55 + i * scale * 0.37, scale * 0.12, scale * 0.56);
group.add(led);
}
// 服务器状态 LED 灯(右上角)
const powerLed = new THREE.Mesh(
new THREE.SphereGeometry(0.025, 10, 10),
createLedMaterial(statusColors[status], 1.2)
);
powerLed.position.set(scale * 0.68, scale * 0.18, scale * 0.56);
group.add(powerLed);
const statusLed = new THREE.Mesh(
new THREE.SphereGeometry(0.02, 10, 10),
createLedMaterial(0x22d3ee, 1.0)
);
statusLed.position.set(scale * 0.72, scale * 0.18, scale * 0.56);
group.add(statusLed);
// 顶部散热格栅
for (let i = 0; i < 6; i++) {
const fin = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.2, 0.01, 0.03),
new THREE.MeshStandardMaterial({ color: 0x475569, metalness: 0.8, roughness: 0.3 })
);
fin.position.set(0, scale * 0.26, -scale * 0.3 + i * scale * 0.12);
group.add(fin);
}
return group;
}
// ============================================================
// 2. 交换机 Switch - 扁平机架式 + 端口阵列
// ============================================================
export function createSwitchModel(status: DeviceStatus, scale = 1.8): THREE.Group {
const group = new THREE.Group();
const bodyMat = createBodyMaterial(status, 'switch');
const panelMat = createPanelMaterial();
const portMat = createPortMaterial();
const earMat = new THREE.MeshStandardMaterial({ color: 0x475569, metalness: 0.8, roughness: 0.3 });
// 主体机箱(非常扁平)
const body = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.8, scale * 0.28, scale * 0.8),
bodyMat
);
body.userData.isBody = true;
group.add(body);
// 前面板
const frontPanel = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.82, scale * 0.24, scale * 0.03),
panelMat
);
frontPanel.position.z = scale * 0.41;
group.add(frontPanel);
// 机架耳
const leftEar = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.12, scale * 0.22, scale * 0.15),
earMat
);
leftEar.position.set(-scale * 0.97, 0, 0);
group.add(leftEar);
const rightEar = leftEar.clone();
rightEar.position.x = scale * 0.97;
group.add(rightEar);
// 端口阵列(24口,2行 x 12列)
const portSize = scale * 0.06;
const portGap = scale * 0.08;
for (let row = 0; row < 2; row++) {
for (let col = 0; col < 12; col++) {
const port = new THREE.Mesh(
new THREE.BoxGeometry(portSize, portSize, scale * 0.05),
portMat
);
const startX = -scale * 0.72 + col * portGap;
const y = row === 0 ? scale * 0.06 : -scale * 0.06;
port.position.set(startX, y, scale * 0.44);
group.add(port);
// 端口上方的小 LED(亮绿色)
const portLed = new THREE.Mesh(
new THREE.SphereGeometry(0.012, 6, 6),
createLedMaterial(0x22c55e, 0.8)
);
portLed.position.set(startX, y + scale * 0.05, scale * 0.47);
group.add(portLed);
}
}
// 品牌区域
const brandArea = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.3, scale * 0.08, 0.035),
new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.5, roughness: 0.5 })
);
brandArea.position.set(-scale * 0.82, scale * 0.12, scale * 0.44);
group.add(brandArea);
// 状态 LED 灯组
const ledPositions = [
{ color: statusColors[status], x: scale * 0.82, y: scale * 0.08 },
{ color: 0x22d3ee, x: scale * 0.87, y: scale * 0.08 },
{ color: 0x22c55e, x: scale * 0.82, y: -scale * 0.08 },
{ color: 0xf59e0b, x: scale * 0.87, y: -scale * 0.08 },
];
ledPositions.forEach(({ color, x, y }) => {
const led = new THREE.Mesh(
new THREE.SphereGeometry(0.018, 8, 8),
createLedMaterial(color, 1.0)
);
led.position.set(x, y, scale * 0.44);
group.add(led);
});
return group;
}
// ============================================================
// 3. 路由器 Router - 1U 机架式 + 天线 + 端口阵列
// ============================================================
export function createRouterModel(status: DeviceStatus, scale = 1.8): THREE.Group {
const group = new THREE.Group();
const bodyMat = createBodyMaterial(status, 'router');
const panelMat = createPanelMaterial();
const portMat = createPortMaterial();
const earMat = new THREE.MeshStandardMaterial({ color: 0x475569, metalness: 0.8, roughness: 0.3 });
// 1U 主体机箱
const body = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.6, scale * 0.35, scale * 0.9),
bodyMat
);
body.userData.isBody = true;
group.add(body);
// 前面板
const frontPanel = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.62, scale * 0.31, scale * 0.03),
panelMat
);
frontPanel.position.z = scale * 0.47;
group.add(frontPanel);
// 机架耳
const leftEar = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.1, scale * 0.28, scale * 0.12),
earMat
);
leftEar.position.set(-scale * 0.86, 0, 0);
group.add(leftEar);
const rightEar = leftEar.clone();
rightEar.position.x = scale * 0.86;
group.add(rightEar);
// 品牌区域 + 状态 LED
const brandArea = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.22, scale * 0.07, 0.035),
new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.6, roughness: 0.4 })
);
brandArea.position.set(-scale * 0.65, scale * 0.1, scale * 0.49);
group.add(brandArea);
// 状态 LED 组
const routerLeds = [
{ color: statusColors[status], x: -scale * 0.38 },
{ color: 0x22d3ee, x: -scale * 0.33 },
{ color: 0x22c55e, x: -scale * 0.28 },
{ color: 0xf59e0b, x: -scale * 0.23 },
];
routerLeds.forEach(({ color, x }) => {
const led = new THREE.Mesh(
new THREE.SphereGeometry(0.018, 8, 8),
createLedMaterial(color, 1.0)
);
led.position.set(x, scale * 0.1, scale * 0.49);
group.add(led);
});
// WAN 口(光纤口样式)
const sfpMat = new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.7, roughness: 0.3 });
const wanPort = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.12, scale * 0.14, scale * 0.06),
sfpMat
);
wanPort.position.set(-scale * 0.05, -scale * 0.02, scale * 0.5);
group.add(wanPort);
const wanRing = new THREE.Mesh(
new THREE.TorusGeometry(scale * 0.045, 0.008, 6, 16),
new THREE.MeshStandardMaterial({ color: 0xfbbf24, emissive: 0xfbbf24, emissiveIntensity: 0.6, metalness: 0.5, roughness: 0.3 })
);
wanRing.rotation.x = Math.PI / 2;
wanRing.position.set(-scale * 0.05, scale * 0.08, scale * 0.53);
group.add(wanRing);
// LAN 口阵列(4个)
for (let i = 0; i < 4; i++) {
const port = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.1, scale * 0.13, scale * 0.05),
portMat
);
port.position.set(scale * 0.2 + i * scale * 0.13, -scale * 0.02, scale * 0.5);
group.add(port);
const lanLed = new THREE.Mesh(
new THREE.SphereGeometry(0.012, 6, 6),
createLedMaterial(0x22c55e, 0.9)
);
lanLed.position.set(scale * 0.2 + i * scale * 0.13, -scale * 0.11, scale * 0.52);
group.add(lanLed);
}
// 天线
const antennaMat = new THREE.MeshStandardMaterial({ color: 0x64748b, metalness: 0.9, roughness: 0.2 });
// 天线 1 - 左侧直立
const ant1 = new THREE.Mesh(
new THREE.CylinderGeometry(0.012, 0.015, scale * 0.7, 8),
antennaMat
);
ant1.position.set(-scale * 0.55, scale * 0.5, -scale * 0.1);
group.add(ant1);
const ant1Tip = new THREE.Mesh(
new THREE.SphereGeometry(0.022, 10, 10),
createLedMaterial(0x22d3ee, 1.2)
);
ant1Tip.position.set(-scale * 0.55, scale * 0.87, -scale * 0.1);
group.add(ant1Tip);
// 天线 2 - 右侧略向后倾斜
const ant2 = new THREE.Mesh(
new THREE.CylinderGeometry(0.012, 0.015, scale * 0.65, 8),
antennaMat
);
ant2.position.set(scale * 0.55, scale * 0.48, -scale * 0.15);
ant2.rotation.x = 0.15;
ant2.rotation.z = -0.1;
group.add(ant2);
const ant2Tip = new THREE.Mesh(
new THREE.SphereGeometry(0.022, 10, 10),
createLedMaterial(0x22d3ee, 1.2)
);
ant2Tip.position.set(scale * 0.52, scale * 0.83, -scale * 0.2);
group.add(ant2Tip);
return group;
}
// ============================================================
// 4. 防火墙 Firewall - 1U 机架式 + LCD 屏 + 盾形浮雕
// ============================================================
export function createFirewallModel(status: DeviceStatus, scale = 1.8): THREE.Group {
const group = new THREE.Group();
const bodyMat = createBodyMaterial(status, 'firewall');
const panelMat = createPanelMaterial();
const portMat = createPortMaterial();
const earMat = new THREE.MeshStandardMaterial({ color: 0x475569, metalness: 0.8, roughness: 0.3 });
// 1U 主体机箱
const body = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.8, scale * 0.36, scale * 0.9),
bodyMat
);
body.userData.isBody = true;
group.add(body);
// 前面板
const frontPanel = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.82, scale * 0.32, scale * 0.03),
panelMat
);
frontPanel.position.z = scale * 0.48;
group.add(frontPanel);
// 机架耳
const leftEar = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.1, scale * 0.29, scale * 0.12),
earMat
);
leftEar.position.set(-scale * 0.97, 0, 0);
group.add(leftEar);
const rightEar = leftEar.clone();
rightEar.position.x = scale * 0.97;
group.add(rightEar);
// 红色安全装饰条
const redStrip = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.55, 0.018, 0.035),
new THREE.MeshStandardMaterial({
color: 0xef4444,
emissive: 0xef4444,
emissiveIntensity: 0.4,
metalness: 0.3,
roughness: 0.5,
})
);
redStrip.position.set(0, scale * 0.15, scale * 0.5);
group.add(redStrip);
// LCD 状态显示屏
const lcdFrame = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.32, scale * 0.2, scale * 0.04),
new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.6, roughness: 0.4 })
);
lcdFrame.position.set(-scale * 0.55, -scale * 0.02, scale * 0.49);
group.add(lcdFrame);
// LCD 显示面(发光蓝绿色)
const lcdScreen = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.28, scale * 0.16, 0.01),
new THREE.MeshStandardMaterial({
color: 0x0d9488,
emissive: 0x14b8a6,
emissiveIntensity: 0.7,
metalness: 0.2,
roughness: 0.3,
})
);
lcdScreen.position.set(-scale * 0.55, -scale * 0.02, scale * 0.52);
group.add(lcdScreen);
// 盾形浮雕
const shieldEmbossMat = new THREE.MeshStandardMaterial({
color: 0x6366f1,
metalness: 0.7,
roughness: 0.3,
emissive: 0x6366f1,
emissiveIntensity: 0.35,
});
const shieldTop = new THREE.Mesh(
new THREE.ConeGeometry(scale * 0.08, scale * 0.14, 4),
shieldEmbossMat
);
shieldTop.rotation.y = Math.PI / 4;
shieldTop.position.set(-scale * 0.18, -scale * 0.03, scale * 0.5);
group.add(shieldTop);
const shieldBody = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.12, scale * 0.14, 0.02),
shieldEmbossMat
);
shieldBody.position.set(-scale * 0.18, -scale * 0.13, scale * 0.5);
group.add(shieldBody);
// 盾心
const shieldCore = new THREE.Mesh(
new THREE.SphereGeometry(0.012, 8, 8),
createLedMaterial(0x22d3ee, 1.2)
);
shieldCore.position.set(-scale * 0.18, -scale * 0.07, scale * 0.52);
group.add(shieldCore);
// 状态 LED 组
const fwLeds = [
{ color: statusColors[status], x: scale * 0.02 },
{ color: 0xef4444, x: scale * 0.08 },
{ color: 0xf59e0b, x: scale * 0.14 },
{ color: 0x22c55e, x: scale * 0.2 },
];
fwLeds.forEach(({ color, x }) => {
const led = new THREE.Mesh(
new THREE.SphereGeometry(0.018, 8, 8),
createLedMaterial(color, 1.0)
);
led.position.set(x, scale * 0.08, scale * 0.5);
group.add(led);
});
// SFP 口
const sfpMat = new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.7, roughness: 0.3 });
for (let i = 0; i < 2; i++) {
const sfp = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.1, scale * 0.12, scale * 0.06),
sfpMat
);
sfp.position.set(scale * 0.45 + i * scale * 0.15, -scale * 0.02, scale * 0.5);
group.add(sfp);
const sfpLed = new THREE.Mesh(
new THREE.SphereGeometry(0.012, 6, 6),
createLedMaterial(0x22d3ee, 1.0)
);
sfpLed.position.set(scale * 0.45 + i * scale * 0.15, -scale * 0.12, scale * 0.52);
group.add(sfpLed);
}
// 网口
for (let i = 0; i < 2; i++) {
const port = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.08, scale * 0.1, scale * 0.05),
portMat
);
port.position.set(scale * 0.78 + i * scale * 0.11, -scale * 0.03, scale * 0.5);
group.add(port);
const portLed = new THREE.Mesh(
new THREE.SphereGeometry(0.01, 6, 6),
createLedMaterial(0x22c55e, 0.9)
);
portLed.position.set(scale * 0.78 + i * scale * 0.11, -scale * 0.11, scale * 0.52);
group.add(portLed);
}
return group;
}
// ============================================================
// 5. 存储 Storage - 磁盘阵列
// ============================================================
export function createStorageModel(status: DeviceStatus, scale = 1.5): THREE.Group {
const group = new THREE.Group();
const bodyMat = createBodyMaterial(status, 'storage');
const panelMat = createPanelMaterial();
// 主体大型方体
const body = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.4, scale * 0.9, scale * 1.1),
bodyMat
);
body.userData.isBody = true;
group.add(body);
// 前面板
const frontPanel = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.42, scale * 0.82, scale * 0.04),
panelMat
);
frontPanel.position.z = scale * 0.57;
group.add(frontPanel);
// 磁盘槽位(3行 x 4列)
const driveW = scale * 0.28;
const driveH = scale * 0.18;
const startX = -scale * 0.55;
const startY = scale * 0.32;
for (let row = 0; row < 3; row++) {
for (let col = 0; col < 4; col++) {
const drive = new THREE.Mesh(
new THREE.BoxGeometry(driveW, driveH, scale * 0.025),
new THREE.MeshStandardMaterial({
color: row === 2 && col >= 2 ? 0x0f172a : 0x1e293b,
metalness: 0.4,
roughness: 0.5,
})
);
drive.position.set(
startX + col * (driveW + 0.02),
startY - row * (driveH + 0.04),
scale * 0.6
);
group.add(drive);
// 磁盘 LED
const isActive = !(row === 2 && col >= 2);
const ledColor = isActive
? row === 0 && col === 0
? statusColors[status]
: 0x22c55e
: 0x475569;
const led = new THREE.Mesh(
new THREE.SphereGeometry(0.015, 8, 8),
createLedMaterial(ledColor, isActive ? 0.8 : 0.3)
);
led.position.set(
startX + col * (driveW + 0.02) - driveW * 0.35,
startY - row * (driveH + 0.04),
scale * 0.62
);
group.add(led);
}
}
// 状态区域
const statusArea = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.3, scale * 0.1, scale * 0.045),
new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.6, roughness: 0.4 })
);
statusArea.position.set(-scale * 0.5, -scale * 0.32, scale * 0.59);
group.add(statusArea);
const stLeds = [
{ color: statusColors[status], x: -scale * 0.58 },
{ color: 0x22d3ee, x: -scale * 0.53 },
{ color: 0x22c55e, x: -scale * 0.48 },
];
stLeds.forEach(({ color, x }) => {
const led = new THREE.Mesh(
new THREE.SphereGeometry(0.02, 10, 10),
createLedMaterial(color, 1.0)
);
led.position.set(x, -scale * 0.32, scale * 0.61);
group.add(led);
});
return group;
}
// ============================================================
// 统一的设备模型创建入口
// ============================================================
export function createDeviceModel(type: DeviceType, status: DeviceStatus, scale?: number): THREE.Group {
switch (type) {
case 'server':
return createServerModel(status, scale ?? 2.2);
case 'switch':
return createSwitchModel(status, scale ?? 2.6);
case 'router':
return createRouterModel(status, scale ?? 2.8);
case 'firewall':
return createFirewallModel(status, scale ?? 2.8);
case 'storage':
return createStorageModel(status, scale ?? 2.4);
case 'workstation':
// 工作站用服务器模型代替(简化版)
return createServerModel(status, scale ?? 2.0);
default:
return createServerModel(status, scale ?? 2.2);
}
}