Files
SenMeshNet/components/monitoring/deviceModels.ts

638 lines
20 KiB
TypeScript
Raw Normal View History

/**
* 精细 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);
}
}