现代软件工程-对编程项目总结作业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)
- 算法控制器内部状态(unit_state、unit_heading、service_queue、pending_calls、zone_assignment、move_count、total_energy)全部封装在算法侧,不暴露给 GUI 或其他模块;外部仅通过电梯代理 API(ProxyElevator.go_to_floor)与事件回调(on_init/on_elevator_stopped 等)交互。
- GUI 只负责可视化:SignalBridge 把控制器事件转换为界面信号;界面不依赖任何算法的内部实现细节。
-
接口设计(Interface Design)
- 基于 elevator_saga 的统一事件回调接口:on_init、on_passenger_call、on_elevator_move、on_elevator_stopped、on_elevator_idle、on_elevator_passing_floor、on_elevator_approaching、on_event_execute_start、on_event_execute_end。算法控制器 IntelligentDispatchController 只需实现这些接口即可对接模拟器。
- 通过环境变量 ELEVATOR_CLIENT_TYPE 控制运行模式(gui / algorithm / server),实现“显示”与“控制”的解耦。
-
松耦合(Loose Coupling)
- GUI 与算法分别是独立客户端,连接 Elevator Server(elevator_saga 模拟器)。GUI 可以对接任意算法,算法也可对接任意 GUI。
- 启动脚本采用顺序启动与“就绪标记”机制,避免耦合与竞争(先 Server,后 GUI,待 GUI 就绪再启动 Algorithm)。
3. 重要模块接口的设计与实现过程
算法控制器:IntelligentDispatchController(algorithm_only.py)
- 入口 main()
- 设置环境变量 ELEVATOR_CLIENT_TYPE=algorithm
- 实例化控制器并启动(controller.start())
- 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 顺扫策略:沿当前方向优先处理最近目标;若该方向无目标,则反向或回归队列首;最终调用 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. 算法关键与独到之处
- 分区与驻守:每台电梯初始化时分配服务区与驻守楼层,缩短响应距离,减少“全楼巡航”的无效移动。
- SCAN 顺扫策略:沿既定方向优先处理离当前最近的目标,减少方向反复与不必要停靠,降低乘客等待时间。
- 智能评分派梯:
- 距离代价:越近越好
- 方向一致性:不反向优先
- 空闲优先:idle 电梯优先响应
- 分区匹配:目标楼层在本电梯负责区更优
- 负载约束:接近满员/接近重量上限时拒绝外部新呼叫
- 能耗统计:记录每台电梯移动次数与总能耗,为后续调度优化提供依据
- 与“公交车(bus)算法”的对比
- bus:层层停靠,上下循环;正确但慢,产生大量无效停靠
- 我们的方案:“按需停靠”,评分派梯+SCAN+分区;理论上总旅行时间、总停靠次数更低,在呼叫稀疏或分布较均匀的场景下优势明显
5. 当需求发生变化时的重构与回归测试
- 需求变化
- 初版派梯仅考虑“最短距离”,忽略方向与负载
- 新需求加入“方向一致性”“分区优先级”“能耗统计”
- 重构策略
- 拆分评分函数各维度并权重化
- 补充 pending_calls 与 idle 回流策略
- 统一在停靠事件中清理队列与 pending_calls,收敛状态管理
- GUI 观察模式修复:避免触发 reset,采用 run_simulation_observable 持续监听
- 回归测试
- 固定 rush hour 场景进行事件回放,比对各版本总旅行时间与停靠次数
- 使用 result.json 记录关键 tick 的状态快照(队列、方向、载客数)
- 纯算法模式(start_no_gui.bat)进行速度回归
- PR/MR 流程(Gitee/GitHub)
- 采用 feature 分支 → PR → Review → squash merge
- 冲突与解决:由于其中之一并不熟悉 git 的使用流程,在结对编程的前期一直是一位在 commit,后期两个人在 commit 的时候会先同步 repo 再进行 commit ,所以没有冲突与合并
6. 程序的代码规范与设计规范、异常处理、质量保障
- 代码规范
- PEP 8 命名、模块拆分清晰;函数关注单一职责
- 日志统一通过 _emit_log 与 SignalBridge 输出,避免散乱打印
- 脚本中使用 UTF-8(chcp 65001)避免中文乱码;路径使用带引号的变量,规避 Windows start 命令的坑
- 异常处理
- GUI 观察模式对状态获取做 try/except 与“强制刷新间隔”保护;算法重启导致的 tick 回退不会让 UI 崩溃
- 启动脚本对 Python/conda 环境做显式检查与失败提示
- 质量保障工具
- 建议集成 flake8 + black + isort;CI 上跑单元测试与场景回归
- 依赖(requirements.txt)
注:GUI 使用 PyQt6,请在环境中安装;Server/Client 通过 elevator_saga 连接电梯模拟服务器flask>=2.0.0 flask-cors>=3.0.0 requests>=2.0.0 elevator_saga>=0.1.0 PyQt6>=6.5
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”与“Ping-Pong Pairing”
- Driver 专注编码实现;Navigator 监督方向与风险,查阅资料
- Ping-Pong:一人写测试/用例,另一人完善实现,随后角色交换
-
结对编程优缺点
- 优点:实时评审减少缺陷;知识流动更快;复杂设计更易落地
- 缺点:时间协调成本高;节奏不合容易疲劳;水平差异导致沟通成本上升
-
伙伴优缺点
- 优点:1. 相应迅速;2. 擅长vibe coding;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 做简短交接
- 结构化反馈促进团队成长
- 对基础差异较大的伙伴,预设“任务包粒度”:强项者搭建骨架,弱项者补齐单元测试与边界情况;定期三明治反馈,记录改进点并度量(commit 数、测试覆盖、线上缺陷数)
10. 运行与对接说明
- 纯 GUI 模式(观察者)
- Windows:双击 start.bat(按序启动 Server→GUI→Algorithm)
- 跨组对接:也可先启动其他组的算法,再运行本组 gui_only.py 并设 ELEVATOR_CLIENT_TYPE=gui
- 纯算法模式(无头)
- Windows:运行 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

被折叠的 条评论
为什么被折叠?



