下面说明我根据第三次上机作业的要求完成的设计,以及其他对第二次上机程序的改进。
修改电梯运行规则:
E0、E1:可到达每层;
E2、E3:可到达 1、2540 层。
E4、E5:可到达 125 层。
E6、E7:可到达 1、240 层中的偶数层。
E8、E9:可到达 139 层中的奇数层。
按照相同规则运行的两部电梯之间是联动的。
随机产生每位乘客初次所要到达的楼层,选择一部合适的电梯让其等待。
为了限制电梯只能在指定的楼层停留,我在 elevator
类中增加了一个数组,用于存储电梯的可达楼层:
std::vector<class floor *> accessible_floors;
程序读取配置文件 config.json
中填写的电梯可达楼层的信息,并将电梯与楼层关联起来。
config.json:
{
// config.json 中其他内容省略
"building.floors": 40,
"elevator.accessibleFloors": [
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40],
[1, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40],
[1, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25],
[1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40],
[1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40],
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39],
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39]
]
}
在 building
对象生成 elevator
对象时,使用 json 对象 conf
中存储的配置信息将电梯与楼层关联。
building.cpp:
for (int i = 0; i < elevator_num; ++i) {
// 设置电梯初始楼层
int initial_floor = conf["elevator.initialFloor"];
elevators[i]->set_current_floor(floors[initial_floor - 1]);
// 将电梯与楼层关联
auto floor_array = conf["elevator.accessibleFloors"][i].get<std::vector<int>>();
for (auto &floor_id : floor_array) {
auto floor = floors[floor_id - 1];
elevators[i]->add_accessible_floor(floor);
floor->add_accessible_elevator(elevators[i]);
}
}
我为 elevator
类增加了一个函数用于供外界查询该电梯能否到达指定的楼层。
elevator.cpp:
// 查询能否到达指定楼层
bool elevator::is_accessible(int floor_id) {
return std::any_of(accessible_floors.cbegin(), accessible_floors.cend(), [floor_id](auto &floor) {
return floor->get_id() == floor_id;
});
}
我修改了 floor
类的 request_elevator()
函数,在乘客通过楼层请求电梯时,楼层对象会在当前楼层关联的电梯中查询可到达乘客目标楼层的电梯,然后再选出一个距离最近、人数最少的电梯分配给乘客。
floor.cpp:
// 楼层处理乘客的乘梯请求
void floor::request_elevator(passenger *p) {
std::vector<std::pair<elevator *, std::pair<int, int>>> candidates;
int pas_direction = p->get_destination() - id > 0 ? elevator::direction::up : elevator::direction::down;
// 遍历当前楼层关联的电梯
for (auto el: accessible_elevators) {
// 跳过不能到达目标楼层的电梯
if (!el->is_accessible(p->get_destination())) {
continue;
}
// 计算电梯与当前楼层的距离,并选出距离最近且人数最少的电梯。这里省略
}
}
我将大楼中的电梯分为若干电梯组,并为 elevator
类增加了一个 group_id
变量,用于存储电梯对象所属电梯组的 ID,以及增加了一个 group
数组用于存储同组电梯的指针。当乘客请求电梯时,乘客的乘梯请求会被发送到最合适的一部电梯,以及其所属电梯组的所有其他电梯。
config.json:
{
// config.json 中其他内容省略
"elevator.group": [
[1, 2],
[3, 4],
[5, 6],
[7, 8],
[9, 10]
],
"elevator.accessibleFloors": [
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25],
[1, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24],
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25]
]
}
building.cpp:
building::building() {
// 其他内容省略
auto groups = conf["elevator.group"].get<std::vector<std::vector<int>>>();
int initial_floor = conf["elevator.initialFloor"].get<int>() - 1;
for (int g = 0; g < groups.size(); ++g) {
// 创建电梯组
std::vector<elevator *> group_elevators;
for (int el : groups[g]) {
group_elevators.push_back(elevators[el - 1]);
}
// 分配电梯组
for (auto el : groups[g]) {
auto elevator = elevators[el - 1];
elevator->set_group_id(g + 1); // 设置 group id
elevator->set_group(group_elevators);
elevator->set_current_floor(floors[initial_floor]); // 设置电梯初始楼层
// 关联可达楼层
auto floor_array = conf["elevator.accessibleFloors"][g].get<std::vector<int>>();
for (auto floor_id : floor_array) {
auto floor = floors[floor_id - 1];
elevator->add_accessible_floor(floor);
floor->add_accessible_elevator(elevator);
}
}
}
}
floor.cpp:
void floor::request_elevator(passenger *p) {
// 其他内容省略
for (auto e:nearest_elevator->get_group()) {
if (e->reg_pas(p)) { // 对 elevator group 中的所有电梯发出乘梯请求
break;
}
}
}
这次上机我还使用 Qt 实现了程序的图形界面。为此引入了两个新类:ElevatorShaft
和 MainWindow
。其中 ElevatorShaft
类用于绘制单个电梯的界面,MainWindow
类用于绘制主窗口。
说明
每个电梯的旁边都有一个状态条,该状态条以楼层为单位分为若干层。每层有三个数字,分别表示:
- 该层想要乘坐电梯上楼的乘客数
- 该层想要乘坐电梯下楼的乘客数
- 已经乘坐电梯,想要在该层下电梯的乘客数
同时,每个电梯的下面还有一个数字,用于指示当前电梯中的人数。当电梯满员时,该数字的背景会变为红色来指示满员。
界面左下角的消息用于展示乘客进入大楼、乘客离开大楼的信息。
当有乘客请求电梯时,电梯组中的所有电梯都会收到请求。因此联动的电梯之间的上楼乘客数及下楼乘客数是一样的。