13 KiB
Raw Blame History

Design Document: AMT Network Scanner

Overview

本设计文档描述了机房远程控制系统中 AMT 网络扫描功能的技术实现方案。该系统采用前后端分离架构,使用 Spring Boot 构建后端服务Vue 3 构建前端界面。核心功能是通过 RMCP (Remote Management Control Protocol) Ping 协议扫描局域网中支持 Intel AMT 的设备,并使用异步多线程技术提高扫描效率。

系统将 Intel AMT SDK 中的 RMCPPing 模块移植到 Java 环境,实现跨平台的设备发现能力。扫描结果通过 WebSocket 实时推送到前端,提供流畅的用户体验。

Architecture

系统架构图

┌─────────────────────────────────────────────────────────────┐
│                        Frontend (Vue 3)                      │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │ Scan Config  │  │ Progress Bar │  │ Device List  │      │
│  │   Component  │  │  Component   │  │  Component   │      │
│  └──────────────┘  └──────────────┘  └──────────────┘      │
│         │                  │                  │              │
│         └──────────────────┴──────────────────┘              │
│                            │                                 │
│                    ┌───────▼────────┐                        │
│                    │  WebSocket     │                        │
│                    │  Connection    │                        │
│                    └───────┬────────┘                        │
└────────────────────────────┼──────────────────────────────────┘
                             │ HTTP/WebSocket
┌────────────────────────────┼──────────────────────────────────┐
│                    ┌───────▼────────┐                         │
│                    │  API Gateway   │                         │
│                    │  (Controller)  │                         │
│                    └───────┬────────┘                         │
│                            │                                  │
│              ┌─────────────┴─────────────┐                   │
│              │                           │                   │
│      ┌───────▼────────┐         ┌───────▼────────┐          │
│      │ Scan Service   │         │ Device Service │          │
│      └───────┬────────┘         └───────┬────────┘          │
│              │                           │                   │
│      ┌───────▼────────┐         ┌───────▼────────┐          │
│      │ RMCP Scanner   │         │ Device Repo    │          │
│      │ (Thread Pool)  │         │ (JPA)          │          │
│      └───────┬────────┘         └───────┬────────┘          │
│              │                           │                   │
│      ┌───────▼────────┐         ┌───────▼────────┐          │
│      │ AMT Detector   │         │   Database     │          │
│      │ (RMCP Ping)    │         │   (MySQL)      │          │
│      └────────────────┘         └────────────────┘          │
│                                                               │
│                Backend (Spring Boot)                         │
└───────────────────────────────────────────────────────────────┘

技术栈

后端:

  • Spring Boot 3.x
  • Spring Web (RESTful API)
  • Spring WebSocket (实时通信)
  • Spring Data JPA (数据持久化)
  • MySQL 8.x (数据库)
  • Java 17+
  • Lombok (简化代码)

前端:

  • Vue 3
  • Vite (构建工具)
  • Element Plus (UI 组件库)
  • Axios (HTTP 客户端)
  • Pinia (状态管理)

Components and Interfaces

Backend Components

1. ScanController

负责处理扫描相关的 HTTP 请求和 WebSocket 连接。

接口:

@RestController
@RequestMapping("/api/scan")
public class ScanController {
    
    // 启动扫描任务
    @PostMapping("/start")
    ResponseEntity<ScanTaskResponse> startScan(@RequestBody ScanRequest request);
    
    // 获取扫描任务状态
    @GetMapping("/status/{taskId}")
    ResponseEntity<ScanStatus> getScanStatus(@PathVariable String taskId);
    
    // 取消扫描任务
    @PostMapping("/cancel/{taskId}")
    ResponseEntity<Void> cancelScan(@PathVariable String taskId);
}

@Controller
public class ScanWebSocketController {
    
    // WebSocket 端点,用于推送扫描进度
    @MessageMapping("/scan/progress")
    @SendTo("/topic/scan/{taskId}")
    void sendProgress(ScanProgress progress);
}

2. ScanService

核心业务逻辑层,协调扫描任务的执行。

接口:

public interface ScanService {
    
    // 创建并启动扫描任务
    String startScan(String networkSegment, String subnetMask);
    
    // 获取扫描任务状态
    ScanStatus getStatus(String taskId);
    
    // 取消扫描任务
    void cancelScan(String taskId);
    
    // 处理发现的设备
    void handleDiscoveredDevice(AmtDevice device);
}

3. RmcpScanner

多线程扫描引擎,负责并发执行 IP 地址检测。

接口:

public class RmcpScanner {
    
    // 扫描指定网段
    CompletableFuture<ScanResult> scanNetwork(
        String networkSegment, 
        String subnetMask,
        ScanProgressCallback callback
    );
    
    // 检测单个 IP 是否支持 AMT
    CompletableFuture<Optional<AmtDevice>> detectDevice(String ipAddress);
    
    // 取消扫描
    void cancelScan(String taskId);
}

4. AmtDetector

AMT 设备检测器,实现 RMCP Ping 协议。

接口:

public class AmtDetector {
    
    // 发送 RMCP Ping 并解析响应
    Optional<PlatformData> ping(String ipAddress, int timeout);
    
    // 构建 RMCP Ping 数据包
    byte[] buildRmcpPingPacket();
    
    // 解析 RMCP Pong 响应
    PlatformData parsePongResponse(byte[] response);
}

5. DeviceService

设备管理服务,负责设备的 CRUD 操作。

接口:

public interface DeviceService {
    
    // 保存设备
    AmtDevice saveDevice(AmtDevice device);
    
    // 查询所有设备
    List<AmtDevice> getAllDevices();
    
    // 根据 IP 查询设备
    Optional<AmtDevice> getDeviceByIp(String ipAddress);
    
    // 删除设备
    void deleteDevice(Long deviceId);
    
    // 搜索设备
    List<AmtDevice> searchDevices(String keyword);
}

6. DeviceRepository

数据访问层,使用 Spring Data JPA。

接口:

public interface DeviceRepository extends JpaRepository<AmtDevice, Long> {
    
    Optional<AmtDevice> findByIpAddress(String ipAddress);
    
    List<AmtDevice> findByIpAddressContaining(String keyword);
    
    List<AmtDevice> findByProvisioningState(ProvisioningState state);
}

Frontend Components

1. ScanConfigComponent

扫描配置组件,用户输入网段和子网掩码。

Props & Events:

interface ScanConfigProps {
  // 无 props
}

interface ScanConfigEmits {
  (e: 'start-scan', config: ScanConfig): void;
}

interface ScanConfig {
  networkSegment: string;  // 例如: "192.168.1.0"
  subnetMask: string;      // 例如: "255.255.255.0" 或 "/24"
}

2. ProgressBarComponent

进度条组件,显示扫描进度。

Props:

interface ProgressBarProps {
  taskId: string;
  progress: number;        // 0-100
  scannedCount: number;
  totalCount: number;
  foundDevices: number;
  status: 'running' | 'completed' | 'cancelled' | 'error';
}

3. DeviceListComponent

设备列表组件,展示已发现的设备。

Props & Events:

interface DeviceListProps {
  devices: AmtDevice[];
  loading: boolean;
}

interface DeviceListEmits {
  (e: 'delete-device', deviceId: number): void;
  (e: 'refresh'): void;
}

Data Models

Backend Data Models

AmtDevice Entity

@Entity
@Table(name = "amt_devices")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AmtDevice {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, unique = true, length = 15)
    private String ipAddress;
    
    @Column(nullable = false)
    private Integer majorVersion;
    
    @Column(nullable = false)
    private Integer minorVersion;
    
    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 20)
    private ProvisioningState provisioningState;
    
    @Column(nullable = false)
    private LocalDateTime discoveredAt;
    
    @Column(nullable = false)
    private LocalDateTime lastSeenAt;
    
    @Column(nullable = false)
    private Boolean online;
    
    @Column(length = 100)
    private String hostname;
    
    @Column(length = 500)
    private String description;
}

ProvisioningState Enum

public enum ProvisioningState {
    PRE("Pre-provisioning"),
    IN("In-provisioning"),
    POST("Post-provisioning"),
    UNKNOWN("Unknown");
    
    private final String displayName;
    
    ProvisioningState(String displayName) {
        this.displayName = displayName;
    }
    
    public String getDisplayName() {
        return displayName;
    }
}

PlatformData DTO

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PlatformData {
    private ProvisioningState provisioningState;
    private Integer majorVersion;
    private Integer minorVersion;
}

ScanRequest DTO

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ScanRequest {
    
    @NotBlank(message = "Network segment is required")
    @Pattern(regexp = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
             message = "Invalid IP address format")
    private String networkSegment;
    
    @NotBlank(message = "Subnet mask is required")
    private String subnetMask;  // 支持 "255.255.255.0" 或 "/24" 格式
}

ScanTaskResponse DTO

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ScanTaskResponse {
    private String taskId;
    private String status;
    private Integer totalIpCount;
    private LocalDateTime startTime;
}

ScanProgress DTO

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ScanProgress {
    private String taskId;
    private Integer scannedCount;
    private Integer totalCount;
    private Integer foundDevices;
    private Double progressPercentage;
    private String currentIp;
    private AmtDevice latestDevice;  // 最新发现的设备
}

ScanStatus DTO

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ScanStatus {
    private String taskId;
    private String status;  // RUNNING, COMPLETED, CANCELLED, ERROR
    private Integer scannedCount;
    private Integer totalCount;
    private Integer foundDevices;
    private LocalDateTime startTime;
    private LocalDateTime endTime;
    private String errorMessage;
}

Frontend Data Models

AmtDevice Interface

export interface AmtDevice {
  id: number;
  ipAddress: string;
  majorVersion: number;
  minorVersion: number;
  provisioningState: 'PRE' | 'IN' | 'POST' | 'UNKNOWN';
  discoveredAt: string;
  lastSeenAt: string;
  online: boolean;
  hostname?: string;
  description?: string;
}

ScanConfig Interface

export interface ScanConfig {
  networkSegment: string;
  subnetMask: string;
}

ScanProgress Interface

export interface ScanProgress {
  taskId: string;
  scannedCount: number;
  totalCount: number;
  foundDevices: number;
  progressPercentage: number;
  currentIp: string;
  latestDevice?: AmtDevice;
}