现代软件工程-对编程项目总结作业2

现代软件工程 - 电梯调度系统项目总结

作业类型: 对编程项目总结作业2

项目仓库:

• Gitee: https://z.gitee.cn/zgca/repos/zgca/elevator_hw

• GitHub: https://github.com/Shenzhaolong1330/elevator_hw


📋 摘要

本项目实现了一个智能电梯调度系统,采用客户端-服务器架构,将"算法控制"和"GUI显示"完全解耦。系统支持作为独立客户端对接电梯模拟服务器,既可以运行本组算法配合本组界面,也可以跨组互相对接。

核心特性:

  • 基于分区驻守与 SCAN 顺扫策略的调度算法
  • 使用评分派梯实现高效响应
  • 完整的启动脚本与依赖说明
  • 支持 Windows/Linux 跨平台运行

📁 目录结构

复制代码
/repo_root
├── gui.py                  # 原始 GUI 组件库(监控系统主窗口、信号桥、事件回调)
├── gui_only.py             # 纯 GUI 模式(仅显示,不控制;观察者模式)
├── algorithm_only.py       # 纯算法模式(只控制,不显示;调度控制器)
├── start.bat               # Windows 启动脚本(按序启动 Server→GUI→Algorithm)
├── start.sh                # Linux 启动脚本(GUI模式)
├── start_no_gui.bat        # Windows 无头模式(仅算法)
├── start_no_gui.sh         # Linux 无头模式(仅算法)
├── requirements.txt        # 依赖包清单
├── README.md               # 项目使用与设计说明
└── result.json             # 状态快照(tick/elevators/events/passengers 等)

1️⃣ PSP 计划表

说明: PSP(Personal Software Process)用于在编码前做时间预算与过程管理。以下是我们在开始实现程序前的时间估算,单位为小时。

阶段 子项 说明 估算耗时 实际耗时 差异原因
规划 计划 明确目标、范围、角色分工 2 2 -
需求分析 场景与约束梳理 电梯参数、事件模型、服务能力、接口边界 2 3 模拟器事件模型理解花费更多时间
高层设计 架构设计 信息隐藏、接口设计、松耦合方案、模块边界 2 3 接口边界拉齐与跨组对接讨论
详细设计 算法与数据结构 调度策略(SCAN + 分区 + 评分)、状态管理 6 7 评分函数维度调整与权重试验
编码 模块实现 algorithm_only / gui_only / gui 8 8 -
单元测试 测试用例 事件回放、边界情况、能耗统计 4 3 测试没有预想中困难
集成测试 GUI对接算法 接口互通、状态同步 4 3 就绪标记与启动顺序等问题解决比较顺利
重构 需求变更适配 评分函数调整、状态机补强、异常处理 4 4 -
文档 README/博客 使用说明、设计说明、对接说明 1 1 -
代码评审 Pair Review 双人走查、规范统一 2 2 -
发布 打包与脚本 start.bat / start_no_gui.bat / 环境变量方案 3 3 -

2️⃣ 信息隐藏、接口设计、松耦合

🔒 信息隐藏 (Information Hiding)

算法控制器内部状态全部封装在算法侧,不暴露给 GUI 或其他模块:

  • unit_stateunit_headingservice_queue
  • pending_callszone_assignment
  • move_counttotal_energy

外部仅通过以下接口交互:

  • 电梯代理 API: ProxyElevator.go_to_floor
  • 事件回调: on_initon_elevator_stopped

GUI 只负责可视化,通过 SignalBridge 转换事件为界面信号,不依赖任何算法的内部实现细节。

🔌 接口设计 (Interface Design)

基于 elevator_saga 的统一事件回调接口:

python 复制代码
on_init(elevators, floors)
on_passenger_call(passenger, floor, direction)
on_elevator_move(elevator, from_pos, to_pos, direction, status)
on_elevator_stopped(elevator, floor)
on_elevator_idle(elevator)
on_elevator_passing_floor(elevator, floor)
on_elevator_approaching(elevator, floor)
on_event_execute_start(event_type)
on_event_execute_end(event_type)

通过环境变量 ELEVATOR_CLIENT_TYPE 控制运行模式:

  • gui - 仅显示
  • algorithm - 仅控制
  • server - 启动模拟器

🔗 松耦合 (Loose Coupling)

架构优势:

  • GUI 与算法分别是独立客户端,连接 Elevator Server
  • GUI 可以对接任意算法,算法也可对接任意 GUI
  • 启动脚本采用顺序启动与"就绪标记"机制,避免耦合与竞争

启动流程:

Server → GUI (等待就绪) → Algorithm


3️⃣ 重要模块接口设计与实现

🧠 算法控制器: IntelligentDispatchController

文件: algorithm_only.py

核心方法

main() - 入口函数

  • 设置环境变量 ELEVATOR_CLIENT_TYPE=algorithm
  • 实例化控制器并启动

on_init(elevators, floors) - 初始化

  • 初始化每台电梯状态 (idle/none)
  • 计算驻守楼层 (home) 与服务分区 (zone)
  • 电梯移动至驻守楼层 (immediate=True)

on_passenger_call(passenger, floor, direction) - 接收呼叫

  • 将呼叫加入 pending_calls (up/down)
  • 触发智能派梯 _intelligent_dispatch(target_floor, direction)

_intelligent_dispatch(target_floor, direction) - 智能派梯

  • 对所有电梯进行评分:
    • 距离代价
    • 方向一致性
    • 空闲优先
    • 分区匹配
    • 负载约束
  • 选择评分最高者执行 _assign_task(best_unit, target_floor)

_assign_task(unit, target_floor) - 分配任务

  • 将目标楼层加入该电梯 service_queue
  • 若电梯空闲 (idle),立即执行 _execute_next

_execute_next(unit) - SCAN 调度

  • 使用 SCAN 顺扫策略:
    • 沿当前方向优先处理最近目标
    • 若该方向无目标,则反向或回归队列首
  • 最终调用 unit.go_to_floor(next_floor)

on_elevator_move(elevator, from_pos, to_pos, direction, status) - 移动统计

  • 统计移动次数与能耗 (例如第 4 台电梯能耗权重更高)

on_elevator_stopped(elevator, floor) - 停靠处理

  • 移除停靠楼层、清理 pending_calls
  • 若有队列或载客,继续执行 _execute_next
  • 否则回 idle 状态

on_elevator_idle(elevator) - 空闲处理

  • 若仍有队列,继续执行
  • 否则从 pending_calls 中"抢最近呼叫"
  • 完全空闲则返回驻守楼层 (immediate=True)

🖥️ GUI 监控系统

文件: gui_only.py / gui.py

GUIOnlyController

观察者模式:

  • 接收事件,不主动控制
  • 避免触发模拟器重置
  • 通过 SignalBridge 发出日志与状态更新到界面
ElevatorMonitorSystem (QMainWindow)

主窗口组件:

  • 电梯卡片栅格
  • 日志面板
  • 控制栏
  • 持有 SignalBridge

start_simulation() - 启动监控

  • 启动观察线程 run_simulation_observable

run_simulation_observable - 状态监听

  • 持续监听模拟器状态
  • 用 tick (模拟器时钟) 与 last_elevator_states 检测刷新
  • 安全处理算法重启导致的 tick 回退,避免界面崩溃

4️⃣ 算法关键与独到之处

🎯 核心策略

1. 分区与驻守

  • 每台电梯初始化时分配服务区与驻守楼层
  • 缩短响应距离
  • 减少"全楼巡航"的无效移动

2. SCAN 顺扫策略

  • 沿既定方向优先处理离当前最近的目标
  • 减少方向反复与不必要停靠
  • 降低乘客等待时间

3. 智能评分派梯

评分维度 说明
距离代价 越近越好
方向一致性 不反向优先
空闲优先 idle 电梯优先响应
分区匹配 目标楼层在本电梯负责区更优
负载约束 接近满员/接近重量上限时拒绝外部新呼叫

4. 能耗统计

  • 记录每台电梯移动次数与总能耗
  • 为后续调度优化提供依据

💡 与"公交车(bus)算法"的对比

算法类型 特点 性能
Bus 算法 层层停靠,上下循环 正确但慢,产生大量无效停靠
本项目算法 按需停靠,评分派梯+SCAN+分区 总旅行时间更短,停靠次数更少

适用场景: 在呼叫稀疏或分布较均匀的场景下优势明显


5️⃣ 需求变化的重构与回归测试

📝 需求变化

初版问题:

  • 派梯仅考虑"最短距离"
  • 忽略方向与负载

新需求:

  • 方向一致性
  • 分区优先级
  • 能耗统计

🔧 重构策略

  1. 拆分评分函数各维度并权重化
  2. 补充 pending_calls 与 idle 回流策略
  3. 统一在停靠事件中清理队列与 pending_calls,收敛状态管理
  4. GUI 观察模式修复:避免触发 reset,采用 run_simulation_observable 持续监听

✅ 回归测试

测试方法:

  • 固定 rush hour 场景进行事件回放
  • 比对各版本总旅行时间与停靠次数
  • 使用 result.json 记录关键 tick 的状态快照
  • 纯算法模式 (start_no_gui.bat) 进行速度回归

🔀 PR/MR 流程

工作流程:

  • Feature 分支 → PR → Review → Squash Merge

冲突处理:

由于其中之一并不熟悉 git 的使用流程,在结对编程的前期一直是一位在 commit,后期两个人在 commit 的时候会先同步 repo 再进行 commit,所以没有冲突与合并。


6️⃣ 代码质量保障

📏 代码规范

遵循 PEP 8:

  • 命名规范
  • 模块拆分清晰
  • 函数关注单一职责

日志管理:

  • 统一通过 _emit_logSignalBridge 输出
  • 避免散乱打印

脚本规范:

  • 使用 UTF-8 (chcp 65001) 避免中文乱码
  • 路径使用带引号的变量
  • 规避 Windows start 命令的坑

⚠️ 异常处理

GUI 保护:

  • 观察模式对状态获取做 try/except
  • "强制刷新间隔"保护
  • 算法重启导致的 tick 回退不会让 UI 崩溃

脚本检查:

  • 对 Python/conda 环境做显式检查
  • 失败时提供明确提示

🛠️ 质量保障工具

建议集成:

  • flake8 - 代码检查
  • black - 代码格式化
  • isort - 导入排序
  • CI 上跑单元测试与场景回归

📦 依赖管理

requirements.txt:

txt 复制代码
flask>=2.0.0
flask-cors>=3.0.0
requests>=2.0.0
elevator_saga>=0.1.0
PyQt6>=6.5

注意: GUI 使用 PyQt6,Server/Client 通过 elevator_saga 连接电梯模拟服务器


7️⃣ 界面模块详细设计 (MVC)

🎨 设计目标

清晰展示多台电梯的运行状态,与算法分离,支持跨组对接:

  • 楼层/方向/载客/能耗
  • 呼叫信号与统计数据

🏗️ 主要类与职责

ElevatorMonitorSystem (QMainWindow)

  • 主窗口管理
  • 电梯卡片栅格
  • 日志面板
  • 控制栏
  • 持有 SignalBridge

GUIOnlyController

  • 连接模拟器
  • 作为观察者接收事件
  • 通过 signals (log_message 等) 传递到 UI

SignalBridge

  • 抽象信号通道
  • 降低 UI 与 Controller 的耦合

🔄 对接关系

环境变量控制:

  • ELEVATOR_CLIENT_TYPE=gui - 仅显示
  • ELEVATOR_CLIENT_TYPE=algorithm - 仅控制
  • ELEVATOR_CLIENT_TYPE=server - 启动模拟器

启动协调:

GUI 启动后写入就绪标记,脚本等待就绪再启动算法,避免"边启动边重置"的竞争。

🎯 MVC 实践

层次 组件 职责
Model Elevator Server 状态 电梯/楼层/乘客数据
View PyQt6 UI 卡片/日志/统计界面
Controller GUIOnlyController Algorithm Controller 事件处理与调度逻辑

📸 界面截图


8️⃣ 结对编程实践

📷 结对照片

🤝 采用的合作方式

混合模式:

  • Driver-Navigator: Driver 专注编码实现,Navigator 监督方向与风险,查阅资料
  • Ping-Pong Pairing: 一人写测试/用例,另一人完善实现,随后角色交换

⚖️ 结对编程优缺点

优点:

  • ✅ 实时评审减少缺陷
  • ✅ 知识流动更快
  • ✅ 复杂设计更易落地

缺点:

  • ⚠️ 时间协调成本高
  • ⚠️ 节奏不合容易疲劳
  • ⚠️ 水平差异导致沟通成本上升

👥 伙伴评价

优点:

  1. 响应迅速
  2. 擅长 vibe coding
  3. 沟通顺畅

缺点:

  • 空闲时间较少,影响速度

🥪 三明治方法的结构化反馈

反馈结构:

  1. 肯定贡献
  2. 指出具体问题与影响
  3. 提供可执行的改进建议

9️⃣ 其它收获与讨论

💪 攻克技术难点

事件驱动与状态机复杂度:

  • 将"评分派梯"与"队列执行"分离
  • 采用 SCAN 控制方向与停靠
  • pending_calls 统一收口外部呼叫
  • 避免在事件回调中直接"拍脑袋"改队列

跨组对接与启动顺序:

  • 用就绪标记等待 GUI 再启动算法
  • 避免模拟器 reset 导致 tick 回退

🤖 与 AI 工具配合

AI 的角色定位:

  • 第三方参与者
  • 资料检索
  • API 差异比对
  • 测试数据生成

使用原则:

Navigator 对 AI 建议二次筛选,防止"合理但错误"的结论进入关键路径。

📚 阅读与探索

参考资料:

  • elevator_saga 与 elevator-py 的接口文档
  • MVC 在事件驱动 GUI 场景的应用

🏆 KPI 比赛展望

若参与总旅行时间等指标比赛:

优势:

  • ✅ 分区 + SCAN + 评分派梯可在热点楼层加速响应
  • ✅ 能耗统计可用于调度细化 (高能耗梯少跑长距离)

劣势:

  • ⚠️ 呼叫极度集中且频繁反向时,SCAN 需要更精细的队列管理
  • ⚠️ 需要并列目标的批处理与合并停靠优化

🔄 角色切换频率的平衡

建议策略:

  • ⏱️ 25--40 分钟或"完成一个可度量子任务"为一轮
  • 📝 避免上下文丢失
  • 🤝 轮换时由 Navigator 做简短交接

📈 结构化反馈促进团队成长

对基础差异较大的伙伴:

  1. 预设"任务包粒度":

    • 强项者搭建骨架
    • 弱项者补齐单元测试与边界情况
  2. 定期三明治反馈:

    • 记录改进点并度量
    • commit 数
    • 测试覆盖
    • 线上缺陷数

🚀 运行与对接说明

🖥️ 纯 GUI 模式 (观察者)

Windows:

bash 复制代码
双击 start.bat
# 按序启动 Server → GUI → Algorithm

跨组对接:

  1. 先启动其他组的算法
  2. 运行本组 gui_only.py
  3. 设置 ELEVATOR_CLIENT_TYPE=gui

⚙️ 纯算法模式 (无头)

Windows:

bash 复制代码
运行 start_no_gui.bat
# 内部设置 ELEVATOR_CLIENT_TYPE=algorithm 并启动 algorithm_only.py

🔧 环境变量与依赖

系统要求:

  • Python ≥ 3.8
  • pip 安装 requirements.txt

环境变量 ELEVATOR_CLIENT_TYPE 控制运行模式:

  • server - 启动模拟器
  • gui - 仅显示界面
  • algorithm - 仅运行算法

⚠️ 兼容与注意事项

Windows 特殊处理:

  • CMD 中文需 chcp 65001
  • start 命令路径必须加引号
  • 脚本使用 ^& 链接命令避免路径解析错误

Conda 环境:

  • 脚本自动尝试激活指定环境
  • 否则使用系统 Python
相关推荐
慧都小项1 小时前
汽车工具链重复测试太耗人?用 TestComplete 自动跑
功能测试·回归·汽车·嵌入式·软件工程·测试覆盖率
不懂的浪漫6 小时前
企业 Agent Runtime | 项目知识需要生命周期
人工智能·软件工程·coding agent
陈彬深大8 小时前
《AI 渐进编程》之二十九:从面向对象到 Harness——封装 AI 的不确定性
人工智能·软件工程
夜听莺儿鸣1 天前
901-005_系统分析师基础知识-计算机网络与分布式系统
计算机网络·软件工程·软考·系统分析师
红薯大哥1 天前
软件工程团队如何构建可持续的学习文化
软件工程
梁辰兴1 天前
软件工程:Halstead 软件科学法
软件工程·应用领域·梁辰兴·halstead 软件科学法·基本元素·基本度量·派生度量
智造ERP规划3 天前
装备制造 ERP↔CRM 集成深度拆解:从询价到项目立项的 6 个独特挑战与解决方案
系统架构·软件工程·制造
Life lies in diligence.3 天前
resdownloader开发日志-1
软件工程·开源软件·yt-dlp·resdownloader
梁辰兴3 天前
软件工程:结构化程序设计
软件工程·设计原则·设计工具·设计方法·梁辰兴·结构化程序设计·三种基本结构