# LGX_project **Repository Path**: lethe-c/lgx_project ## Basic Information - **Project Name**: LGX_project - **Description**: 数据结构课设代做 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-12-31 - **Last Updated**: 2026-01-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 无人机编队表演控制系统 ## 一、项目目录结构 本项目采用标准的Java项目目录结构,基于MVC架构并按功能模块进行划分,便于代码的组织和维护。项目使用`com.lgx`作为根包,具体目录结构如下: ``` LGX_project/ ├── .idea/ # IDE配置目录 ├── docs/ # 项目文档目录 │ ├── UML用例图.puml # UML用例图 │ ├── UML类图.puml # UML类图 │ ├── 用户使用说明.md # 用户使用说明文档 │ └── 系统模块结构图.vsdx # 系统模块结构图 ├── lib/ # 依赖库目录 │ └── gson-2.10.1.jar # Gson库,用于JSON数据处理 ├── src/ # 源代码目录 │ └── main/ # 主代码目录 │ ├── data/ # 数据目录 │ │ ├── drone_data_2.json # 2架无人机模拟数据,3种配置(直线、曲线、环形),1种配置对应30个时间点 │ │ ├── drone_data_3.json # 3架无人机模拟数据,3种配置(直线、曲线、环形),1种配置对应30个时间点 │ │ ├── drone_data_4.json # 4架无人机模拟数据,3种配置(直线、曲线、环形),1种配置对应30个时间点 │ │ ├── drone_data_5.json # 5架无人机模拟数据,3种配置(直线、曲线、环形),1种配置对应30个时间点 │ │ ├── drone_data_6.json # 6架无人机模拟数据,3种配置(直线、曲线、环形),1种配置对应30个时间点 │ │ ├── drone_data_7.json # 7架无人机模拟数据,3种配置(直线、曲线、环形),1种配置对应30个时间点 │ │ └── drone_data_8.json # 8架无人机模拟数据,3种配置(直线、曲线、环形),1种配置对应30个时间点 │ └── java/ # Java源代码 │ └── com/ # 主包目录 │ └── lgx/ # 项目根包 │ ├── app/ # 应用入口层 │ │ └── DroneFormationControlSystem.java # 系统主程序文件(系统入口) │ ├── dataStructure/ # 自定义数据结构层 │ │ ├── array/ # 数组相关 │ │ │ └── FlightPath.java # 飞行轨迹实现(并列数组) │ │ ├── graph/ # 图结构相关 │ │ │ ├── AdjacencyMatrix.java # 邻接矩阵实现 │ │ │ └── UndirectedGraph.java # 无向图实现 │ │ ├── hash/ # 哈希表相关 │ │ │ └── CustomHashMap.java # 自定义哈希表实现 │ │ └── queue/ # 队列相关 │ │ └── PriorityQueue.java # 自定义优先队列实现 │ ├── model/ # 模型层 │ │ ├── AvoidanceCommand.java # 避障指令类,实现Comparable接口 │ │ └── Drone.java # 无人机模型 │ ├── service/ # 服务层,提供业务逻辑服务 │ │ ├── DroneStatusMonitor.java # 无人机状态监控 │ │ ├── JSONDataGenerator.java # JSON数据生成 │ │ └── SimulationDataLoader.java # 模拟数据加载 │ └── view/ # 视图层,负责界面显示 │ ├── AvoidanceQueuePanel.java # 避障指令队列面板 │ ├── FlightTrajectoryPanel.java # 飞行轨迹面板 │ └── GraphDisplayPanel.java # 图形显示面板 ├── target/ # 编译输出目录 ├── LGX_project.iml # IDE项目配置文件 ├── README.md # 项目说明文档 └── pom.xml # Maven项目配置文件 ``` ## 二、类与接口设计 **主要类设计**: | 类名 | 所属包 | 功能描述 | |-----|-------|---------| | DroneFormationControlSystem | controller | 系统主控制器,协调各模块功能 | | Drone | model | 无人机模型,存储位置和状态信息 | | AdjacencyMatrix | model | 邻接矩阵,存储无人机之间的距离 | | UndirectedGraph | model | 无向图,可视化无人机连接关系 | | PriorityQueue | service | 优先队列,管理避障指令 | | CustomHashMap | model | 自定义哈希表,存储无人机状态 | | FlightPath | model | 飞行轨迹,记录无人机位置历史 | | AvoidanceCommand | model | 避障指令,包含优先级和相关信息 | | DroneStatusMonitor | service | 状态监控,管理无人机状态 | | GraphDisplayPanel | view | 图形显示面板,显示邻接矩阵和无向图 | | FlightTrajectoryPanel | view | 飞行轨迹面板,显示无人机轨迹 | | AvoidanceQueuePanel | view | 避障队列面板,显示避障指令 | ## 三、类的详细定义 ### 1. Drone类 | 属性 | 类型 | 描述 | |-----|-----|-----| | id | int | 无人机ID | | x, y, z | double | 三维坐标 | | status | DroneStatus | 状态(正常/异常) | | 方法 | 返回类型 | 参数 | 描述 | |-----|---------|-----|-----| | getPosition() | double[] | 无 | 获取三维坐标 | | updatePosition() | void | double x, double y, double z | 更新位置 | | getStatus() | DroneStatus | 无 | 获取状态 | | setStatus() | void | DroneStatus status | 设置状态 | ### 2. AdjacencyMatrix类 | 属性 | 类型 | 描述 | |-----|-----|-----| | matrix | double[][] | 邻接矩阵 | | size | int | 矩阵大小 | | 方法 | 返回类型 | 参数 | 描述 | |-----|---------|-----|-----| | setDistance() | void | int i, int j, double distance | 设置无人机i和j之间的距离 | | getDistance() | double | int i, int j | 获取无人机i和j之间的距离 | | getFormattedMatrix() | String | 无 | 获取格式化的矩阵字符串 | ### 3. PriorityQueue类 | 属性 | 类型 | 描述 | |-----|-----|-----| | heap | AvoidanceCommand[] | 二叉堆数组 | | size | int | 队列大小 | | 方法 | 返回类型 | 参数 | 描述 | |-----|---------|-----|-----| | insert() | void | AvoidanceCommand command | 插入避障指令 | | remove() | AvoidanceCommand | 无 | 移除最高优先级指令 | | isEmpty() | boolean | 无 | 检查队列是否为空 | ### 4. CustomHashMap类 | 属性 | 类型 | 描述 | |-----|-----|-----| | table | LinkedList>[] | 哈希表数组 | | size | int | 当前大小 | | capacity | int | 容量 | | 方法 | 返回类型 | 参数 | 描述 | |-----|---------|-----|-----| | put() | void | K key, V value | 插入键值对 | | get() | V | K key | 获取值 | | remove() | V | K key | 移除键值对 | | containsKey() | boolean | K key | 检查键是否存在 | ### 5. FlightPath类 | 属性 | 类型 | 描述 | |-----|-----|-----| | droneCount | int | 无人机数量 | | xCoordinates | List> | x坐标序列 | | yCoordinates | List> | y坐标序列 | | zCoordinates | List> | z坐标序列 | | timePoints | List> | 时间点序列 | | 方法 | 返回类型 | 参数 | 描述 | |-----|---------|-----|-----| | recordPosition() | void | int droneId, double x, double y, double z, int time | 记录位置 | | getPositionAtTime() | int[] | int droneId, int time | 获取指定时间的位置 | | getTrajectory() | int[][] | int droneId | 获取完整轨迹 | ## 四、模块划分和调用关系 ### 1. 模块划分 1. **配置模块**:处理无人机数量和轨迹选择 2. **模拟模块**:控制飞行模拟流程,更新时间步 3. **图形模块**:绘制邻接矩阵和无向图 4. **轨迹模块**:记录和显示飞行轨迹 5. **避障模块**:生成和处理避障指令 6. **状态模块**:监控和处理无人机状态 ### 2. 调用关系 ``` DroneFormationControlSystem ├── 配置模块 │ └── 初始化无人机 ├── 模拟模块 │ └── 模拟飞行 ├── 图形模块 │ ├── 邻接矩阵更新 │ └── 无向图绘制 ├── 轨迹模块 │ └── 记录飞行轨迹 ├── 避障模块 │ ├── 生成避障指令 │ └── 处理避障指令 └── 状态模块 └── 监控无人机状态 ``` ### 3. 主流程调用关系 1. `main()` → `createAndShowGUI()`:初始化界面 2. `配置无人机` → `initializeDrones()`:初始化无人机和数据结构 3. `下一个时间点` → `simulateFlight()`:模拟飞行 - `updateAdjacencyMatrix()`:更新邻接矩阵 - `generateAvoidanceCommands()`:生成避障指令 - 更新各个面板显示 4. `处理最高优先级指令` → `avoidanceQueue.remove()`:移除并处理避障指令 5. `处理异常无人机` → `processDrone()`:将无人机状态从异常变为正常 ## 五、四个自定义数据结构的详细说明 ### 1. 邻接矩阵 **实现方式**:使用二维数组存储无人机之间的距离,矩阵是对称的,对角线元素为0。 **完整实现代码**: ```java package com.lgx.dataStructure.graph; /** * 邻接矩阵类 * 用于表示无人机之间的连接关系,存储无人机两两之间的距离 * 实现了无向图的邻接矩阵表示,矩阵是对称的 */ public class AdjacencyMatrix { // 邻接矩阵,存储无人机之间的距离 private double[][] matrix; // 矩阵大小,即无人机数量 private int size; /** * 构造函数,初始化邻接矩阵 * @param size 无人机数量,决定矩阵的维度 */ public AdjacencyMatrix(int size) { this.size = size; this.matrix = new double[size][size]; // 初始化矩阵:对角线为0(自身到自身的距离),其他为无穷大(表示初始无连接) for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { if (i == j) { matrix[i][j] = 0.0; } else { matrix[i][j] = Double.POSITIVE_INFINITY; } } } } /** * 设置两个无人机之间的距离 * @param i 第一个无人机的索引 * @param j 第二个无人机的索引 * @param distance 两个无人机之间的距离 */ public void setDistance(int i, int j, double distance) { // 检查索引是否合法 if (i >= 0 && i < size && j >= 0 && j < size) { matrix[i][j] = distance; // 无向图的邻接矩阵是对称的,所以对称位置也要设置相同的距离 matrix[j][i] = distance; } } /** * 获取两个无人机之间的距离 * @param i 第一个无人机的索引 * @param j 第二个无人机的索引 * @return 两个无人机之间的距离,若索引不合法则返回无穷大 */ public double getDistance(int i, int j) { // 检查索引是否合法 if (i >= 0 && i < size && j >= 0 && j < size) { return matrix[i][j]; } return Double.POSITIVE_INFINITY; } /** * 获取格式化的矩阵字符串,用于GUI显示 * @return 格式化后的矩阵字符串 */ public String getFormattedMatrix() { StringBuilder sb = new StringBuilder(); // 构建列标题 sb.append(" "); for (int i = 0; i < size; i++) { sb.append(String.format("D%-2d ", i)); } sb.append("\n"); // 构建每行数据 for (int i = 0; i < size; i++) { // 行标题 sb.append(String.format("D%-2d ", i)); for (int j = 0; j < size; j++) { // 无穷大值显示为"∞",否则显示实际距离值 if (matrix[i][j] == Double.POSITIVE_INFINITY) { sb.append(" ∞ "); } else { sb.append(String.format("%6.2f ", matrix[i][j])); } } sb.append("\n"); } return sb.toString(); } } ``` **特点**: - 高效的距离访问,时间复杂度O(1) - 适用于密集图 - 空间复杂度O(n²) - 支持格式化输出,便于可视化展示 ### 2. 优先队列 **实现方式**:使用二叉堆实现,堆顶元素为最高优先级(数值最小)。 **完整实现代码**: ```java package com.lgx.dataStructure.queue; import com.lgx.model.AvoidanceCommand; /** * 优先队列类 * 基于二叉堆实现,用于管理无人机避障指令 * 支持插入和移除最高优先级元素的操作 * 优先级数值越小,优先级越高 */ public class PriorityQueue { // 二叉堆数组,存储避障指令 private AvoidanceCommand[] heap; // 当前队列大小 private int size; // 默认初始容量 private static final int DEFAULT_CAPACITY = 10; /** * 构造函数,初始化空优先队列 */ public PriorityQueue() { // 堆从索引1开始存储,索引0不使用 heap = new AvoidanceCommand[DEFAULT_CAPACITY]; size = 0; } /** * 插入新的避障指令到优先队列 * @param command 避障指令对象 */ public void insert(AvoidanceCommand command) { // 检查是否需要扩容 if (size >= heap.length - 1) { resize(); } // 将新元素添加到堆的末尾 heap[++size] = command; // 上浮操作,维护堆的性质 swim(size); } /** * 移除并返回最高优先级的避障指令 * @return 最高优先级的避障指令,队列为空则返回null */ public AvoidanceCommand remove() { // 检查队列是否为空 if (size == 0) { return null; } // 获取最高优先级元素(堆顶) AvoidanceCommand max = heap[1]; // 将堆顶元素与最后一个元素交换 swap(1, size--); // 下沉操作,维护堆的性质 sink(1); // 清空最后一个元素的引用,便于垃圾回收 heap[size + 1] = null; return max; } /** * 上浮操作,用于维护堆的性质 * 当插入新元素时,将其上浮到合适的位置 * @param k 当前元素的索引 */ private void swim(int k) { // 当k>1且父节点优先级低于当前节点时,交换它们的位置 while (k > 1 && heap[k / 2].compareTo(heap[k]) > 0) { swap(k, k / 2); // 移动到父节点位置,继续比较 k = k / 2; } } /** * 下沉操作,用于维护堆的性质 * 当移除堆顶元素后,将最后一个元素下沉到合适的位置 * @param k 当前元素的索引 */ private void sink(int k) { // 当左子节点存在时,继续比较 while (2 * k <= size) { // 左子节点索引 int j = 2 * k; // 如果右子节点存在且优先级高于左子节点,选择右子节点 if (j < size && heap[j].compareTo(heap[j + 1]) > 0) { j++; } // 如果当前节点优先级低于子节点,交换它们的位置 if (heap[k].compareTo(heap[j]) <= 0) { break; } swap(k, j); // 移动到子节点位置,继续比较 k = j; } } /** * 交换堆中两个元素的位置 * @param i 第一个元素的索引 * @param j 第二个元素的索引 */ private void swap(int i, int j) { AvoidanceCommand temp = heap[i]; heap[i] = heap[j]; heap[j] = temp; } /** * 扩容操作,当堆满时将容量翻倍 */ private void resize() { // 创建新的更大容量的堆数组 AvoidanceCommand[] newHeap = new AvoidanceCommand[heap.length * 2]; // 复制原堆中的元素到新堆 System.arraycopy(heap, 0, newHeap, 0, heap.length); // 更新堆引用 heap = newHeap; } /** * 检查优先队列是否为空 * @return 如果队列为空返回true,否则返回false */ public boolean isEmpty() { return size == 0; } /** * 获取队列中的所有元素 * @return 避障指令数组 */ public AvoidanceCommand[] getElements() { AvoidanceCommand[] elements = new AvoidanceCommand[size]; // 复制堆中的元素到数组,忽略索引0 System.arraycopy(heap, 1, elements, 0, size); return elements; } } ``` **特点**: - 高效的插入和删除操作,时间复杂度均为O(log n) - 支持动态扩容,自动调整大小 - 支持优先级排序,确保紧急指令优先处理 - 用于管理避障指令队列 ### 3. 哈希表 **实现方式**:使用数组+链表的链地址法解决哈希冲突,支持动态扩容。 **完整实现代码**: ```java package com.lgx.dataStructure.hash; import java.util.LinkedList; import java.util.ArrayList; import java.util.Collection; /** * 自定义哈希表类 * 基于数组+链表实现,支持泛型键值对存储 * 采用链地址法解决哈希冲突 */ public class CustomHashMap { // 默认初始容量 private static final int DEFAULT_CAPACITY = 16; // 负载因子,用于触发扩容 private static final float LOAD_FACTOR = 0.75f; // 哈希表的数组,每个元素是一个链表,用于存储冲突的键值对 private LinkedList>[] table; // 当前哈希表中键值对的数量 private int size; // 当前哈希表的容量 private int capacity; /** * 无参构造函数,使用默认初始容量 */ public CustomHashMap() { this(DEFAULT_CAPACITY); } /** * 有参构造函数,指定初始容量 * @param capacity 初始容量 */ public CustomHashMap(int capacity) { this.capacity = capacity; // 初始化哈希表数组 this.table = new LinkedList[capacity]; this.size = 0; // 为每个数组元素初始化一个空链表 for (int i = 0; i < capacity; i++) { table[i] = new LinkedList<>(); } } /** * 内部静态类,表示哈希表中的键值对条目 */ private static class Entry { // 键 K key; // 值 V value; /** * 构造函数,创建键值对条目 * @param key 键 * @param value 值 */ Entry(K key, V value) { this.key = key; this.value = value; } /** * 比较当前条目与给定键是否相等 * @param otherKey 要比较的键 * @return 如果键相等返回true,否则返回false */ boolean equalsKey(K otherKey) { // 处理null键的情况 return key == null ? otherKey == null : key.equals(otherKey); } } /** * 哈希函数,计算键的哈希值并转换为数组索引 * @param key 键 * @return 数组索引 */ private int hash(K key) { if (key == null) { return 0; // null键的哈希值为0 } int hashCode = key.hashCode(); // 使用取模运算获取数组索引,并确保结果为正数 return Math.abs(hashCode % capacity); } /** * 向哈希表中插入或更新键值对 * @param key 键 * @param value 值 */ public void put(K key, V value) { // 检查是否需要扩容 if (size >= capacity * LOAD_FACTOR) { resize(); } // 计算键的哈希值,得到数组索引 int index = hash(key); // 获取对应索引的链表(桶) LinkedList> bucket = table[index]; // 遍历链表,查找是否已存在相同的键 for (Entry entry : bucket) { if (entry.equalsKey(key)) { // 如果存在,更新值并返回 entry.value = value; return; } } // 如果不存在,添加新的键值对条目到链表末尾 bucket.add(new Entry<>(key, value)); // 更新哈希表大小 size++; } /** * 根据键获取对应的值 * @param key 键 * @return 键对应的值,如果键不存在则返回null */ public V get(K key) { // 计算键的哈希值,得到数组索引 int index = hash(key); // 获取对应索引的链表(桶) LinkedList> bucket = table[index]; // 遍历链表,查找匹配的键 for (Entry entry : bucket) { if (entry.equalsKey(key)) { return entry.value; // 找到,返回对应的值 } } return null; // 未找到,返回null } /** * 根据键移除对应的键值对 * @param key 键 * @return 被移除的值,如果键不存在则返回null */ public V remove(K key) { // 计算键的哈希值,得到数组索引 int index = hash(key); // 获取对应索引的链表(桶) LinkedList> bucket = table[index]; // 遍历链表,查找匹配的键 for (Entry entry : bucket) { if (entry.equalsKey(key)) { // 找到,移除该条目 bucket.remove(entry); // 更新哈希表大小 size--; return entry.value; // 返回被移除的值 } } return null; // 未找到,返回null } /** * 扩容操作,将哈希表容量翻倍 */ private void resize() { // 计算新容量,翻倍原容量 int newCapacity = capacity * 2; // 创建新的哈希表数组 LinkedList>[] newTable = new LinkedList[newCapacity]; // 初始化新数组的每个元素为一个空链表 for (int i = 0; i < newCapacity; i++) { newTable[i] = new LinkedList<>(); } // 重新哈希所有键值对到新数组 for (LinkedList> bucket : table) { for (Entry entry : bucket) { // 重新计算键在新数组中的索引 int newIndex = Math.abs(entry.key.hashCode() % newCapacity); // 将条目添加到新数组对应的链表中 newTable[newIndex].add(entry); } } // 更新哈希表引用和容量 table = newTable; capacity = newCapacity; } /** * 获取哈希表中所有值的集合 * @return 值的集合 */ public Collection values() { // 创建一个ArrayList用于存储所有值 ArrayList valuesList = new ArrayList<>(); // 遍历所有桶 for (LinkedList> bucket : table) { // 遍历每个桶中的所有条目 for (Entry entry : bucket) { valuesList.add(entry.value); // 添加值到集合 } } return valuesList; } } ``` **特点**: - 平均查找、插入、删除时间复杂度为O(1) - 支持动态扩容,自动调整大小 - 采用链地址法解决哈希冲突 - 支持泛型,可存储任意类型的键值对 - 用于高效存储和查询无人机状态 ### 4. 并列数组 **实现方式**:使用多个ArrayList分别存储x、y、z坐标和时间点,为每架无人机创建独立的列表。 **完整实现代码**: ```java package com.lgx.dataStructure.array; import java.util.ArrayList; import java.util.List; /** * 飞行轨迹类 * 用于记录和管理无人机的飞行轨迹数据 * 采用并行数组的数据结构,为每架无人机存储独立的坐标和时间序列 */ public class FlightPath { // 无人机数量 private int droneCount; // 并行数组实现:为每架无人机存储x坐标序列 // xCoordinates.get(droneId) 表示第droneId架无人机的x坐标序列 private List> xCoordinates; // 并行数组实现:为每架无人机存储y坐标序列 private List> yCoordinates; // 并行数组实现:为每架无人机存储z坐标序列 private List> zCoordinates; // 并行数组实现:为每架无人机存储时间点序列 private List> timePoints; /** * 构造函数,初始化飞行轨迹对象 * @param droneCount 无人机数量 */ public FlightPath(int droneCount) { this.droneCount = droneCount; // 初始化并行数组,为每架无人机创建对应的坐标和时间列表 xCoordinates = new ArrayList<>(droneCount); yCoordinates = new ArrayList<>(droneCount); zCoordinates = new ArrayList<>(droneCount); timePoints = new ArrayList<>(droneCount); // 为每架无人机初始化空列表 for (int i = 0; i < droneCount; i++) { xCoordinates.add(new ArrayList<>()); yCoordinates.add(new ArrayList<>()); zCoordinates.add(new ArrayList<>()); timePoints.add(new ArrayList<>()); } } /** * 记录无人机在某个时间点的位置 * @param droneId 无人机ID * @param x x坐标 * @param y y坐标 * @param z z坐标 * @param time 时间点 */ public void recordPosition(int droneId, double x, double y, double z, int time) { // 检查无人机ID是否合法 if (droneId >= 0 && droneId < droneCount) { // 将坐标转换为整数后存储,减少内存占用并提高访问效率 xCoordinates.get(droneId).add((int) Math.round(x)); yCoordinates.get(droneId).add((int) Math.round(y)); zCoordinates.get(droneId).add((int) Math.round(z)); timePoints.get(droneId).add(time); } } /** * 获取无人机在指定时间点的位置 * @param droneId 无人机ID * @param time 时间点 * @return 包含x、y、z坐标的数组,未找到则返回null */ public int[] getPositionAtTime(int droneId, int time) { // 检查无人机ID是否合法 if (droneId >= 0 && droneId < droneCount) { List times = timePoints.get(droneId); // 遍历时间点列表,查找匹配的时间点 for (int i = 0; i < times.size(); i++) { if (times.get(i) == time) { // 找到匹配的时间点,返回对应的坐标 return new int[]{ xCoordinates.get(droneId).get(i), yCoordinates.get(droneId).get(i), zCoordinates.get(droneId).get(i) }; } } } return null; // 未找到该时间点的位置 } /** * 获取无人机的完整飞行轨迹 * @param droneId 无人机ID * @return 包含所有轨迹点的二维数组,格式为[time, x, y, z],ID无效则返回null */ public int[][] getTrajectory(int droneId) { // 检查无人机ID是否合法 if (droneId >= 0 && droneId < droneCount) { // 获取轨迹长度 int length = timePoints.get(droneId).size(); // 创建二维数组存储轨迹数据 int[][] trajectory = new int[length][4]; // [time, x, y, z] // 遍历所有轨迹点,填充轨迹数据 for (int i = 0; i < length; i++) { trajectory[i][0] = timePoints.get(droneId).get(i); trajectory[i][1] = xCoordinates.get(droneId).get(i); trajectory[i][2] = yCoordinates.get(droneId).get(i); trajectory[i][3] = zCoordinates.get(droneId).get(i); } return trajectory; } return null; } /** * 获取格式化的轨迹字符串,用于GUI显示 * @param droneId 无人机ID * @return 格式化的轨迹字符串,ID无效则返回错误信息 */ public String getFormattedTrajectory(int droneId) { // 检查无人机ID是否合法 if (droneId >= 0 && droneId < droneCount) { StringBuilder sb = new StringBuilder(); // 构建标题行 sb.append("无人机 " + droneId + " 的飞行轨迹:\n"); sb.append("时间\tX\tY\tZ\n"); sb.append("--------------------\n"); // 获取该无人机的所有轨迹数据 List times = timePoints.get(droneId); List xs = xCoordinates.get(droneId); List ys = yCoordinates.get(droneId); List zs = zCoordinates.get(droneId); // 只显示最近的10个轨迹点,避免界面过于拥挤 int startIndex = Math.max(0, times.size() - 10); for (int i = startIndex; i < times.size(); i++) { sb.append(String.format("%d\t%d\t%d\t%d\n", times.get(i), xs.get(i), ys.get(i), zs.get(i))); } return sb.toString(); } return "无效的无人机ID"; } } ``` **特点**: - 高效记录和查询无人机飞行轨迹 - 便于按时间顺序分析位置数据 - 适合轨迹可视化和分析 - 支持格式化输出,便于在GUI中展示 - 支持多种轨迹查询方式(按时间点、完整轨迹、最新位置) ## 六、模块说明 | 模块 | 主要职责 | 核心类 | |-----|---------|-------| | app | 应用入口,处理用户输入,协调模型和视图 | DroneFormationControlSystem | | model | 定义业务实体 | Drone, AvoidanceCommand | | dataStructure | 自定义数据结构实现 | AdjacencyMatrix, UndirectedGraph, CustomHashMap, FlightPath, PriorityQueue | | service | 提供业务逻辑服务 | DroneStatusMonitor, JSONDataGenerator, SimulationDataLoader | | view | 负责界面显示 | GraphDisplayPanel, FlightTrajectoryPanel, AvoidanceQueuePanel | ## 七、核心类关系说明 - **PriorityQueue** 是自定义优先队列的实现,用于存储和管理避障指令 - **AvoidanceCommand** 是避障指令类,实现了 `Comparable` 接口,用于在优先队列中按照优先级排序 - 两者配合使用:`PriorityQueue` 存储 `AvoidanceCommand` 对象,利用 `AvoidanceCommand` 的 `compareTo` 方法实现优先级排序 - 这种设计符合单一职责原则:`AvoidanceCommand` 负责表示避障指令,`PriorityQueue` 负责队列管理 ## 八、数据流程 1. **配置流程**:用户在界面配置无人机数量和轨迹类型 → app层初始化无人机和数据结构 → dataStructure层创建邻接矩阵、无向图等数据结构 → 视图层更新显示 2. **模拟流程**:用户点击"下一个时间点" → app层调用模拟飞行方法 → service层加载模拟数据 → 更新无人机位置和邻接矩阵 → 生成避障指令 → 视图层更新图形和轨迹显示 3. **避障流程**:service层检测到无人机间距过小 → 生成避障指令并插入优先队列 → 视图层显示避障指令 → 用户处理指令 → app层从队列中移除指令 → 视图层更新显示 4. **状态监控流程**:service层监控无人机状态 → 检测到异常 → 视图层显示异常状态 → 用户处理异常 → service层更新状态 → 视图层更新显示