设计一个电梯调度系统涉及多个方面,包括用户界面、系统架构、调度算法、安全性等。以下是一个基本的设计框架:
系统需求分析
用户需求
1、支持多层楼的请求
2、提供上行和下行的请求
3、提供紧急停靠的功能
4、显示当前电梯位置和运动方向
技术需求
1、高效的调度算法
2、实时响应用户请求
3、处理并发请求
4、系统的高可用性和可靠性
系统架构设计
前端
电梯内控制面板:按钮、显示屏
各楼层外的请求按钮(上行和下行)
如下图:

后端
电梯控制器:处理电梯的运动,门的开关等
调度系统:接受请求并决定电梯的调度
状态监控:实时监控电梯状态
如下图:

通信模块
实时数据传输
故障报警和紧急处理
调度算法设计
常用的调度算法包括:
先到先服务: 简单但是可能不高效
扫描算法:电梯像磁头一样来回移动,处理上下行请求,减少等待时间
循环扫描算法:类似扫描算法
,但每次到达顶层时直接返回起始点,提高效率
优化sacn算法:电梯只有请求的最远点,再反向移动
调度过程

过程描述:
用户A和用户B在1F【外部控制器】中点击[上行]。
【电梯】监听所有【外部控制器】,根据最先发起状态改动的【外部控制器】做优先处理。
判断[当前楼层]是否大于【监听到的外部控制器】的[楼层]。
- 如果大于,修改【电梯】状态为[下行],并向下移动。
- 否则,修改【电梯】状态为[上行],并向上移动。
当【电梯】的[当前楼层]等于【监听到的外部控制器】的[楼层]时,[状态]修改为[停止],并调用[开门]。
用户A进入【电梯】后,在【内部控制器】点击[目标楼层]按钮,【电梯】记录用户的[目标楼层]。
当用户点击【内部控制器】[关门]后,判断[目标楼层]是否大于[当前楼层]。
- 如果大于,修改【电梯】的[状态]为[上行]。
- 否则,修改【电梯】的[状态]为[下行]。
当【电梯】的[当前楼层]等于用户的[目标楼层]时,【电梯】的[状态]修改为[停止],并调用[开门]等待用户操作。
如果有新的外部请求,在当前请求处理完毕后继续处理。
电梯调度分析及实现
预约式电梯调度系统的基本原理,允许乘客提前预约他们想要乘坐的电梯
并根据乘客的需求和电梯的运行状态来调度电梯的运行
er图

具体类
电梯类(Elevator)
字段 |
类型 |
备注 |
id |
int |
主键 |
no |
varchar |
电梯编号 |
current_floor |
int |
电梯当前楼层 |
status |
enum(up-上,down-下,stop-停止) |
电梯当前状态(如“上行”、“下行”、“停止”) |
target_floors |
varchar |
用户目的楼层列表 |
|
|
|
方法 |
返回值 |
备注 |
addRequest(Request request) |
void |
添加到请求队列 |
processNextRequest() |
void |
下一个请求 |
moveToFloor |
void |
移动到指定楼层 |
openDoors |
void |
开门 |
closeDoors() |
void |
关门 |
addTargetFloor |
void |
添加到目标楼层队列 |
|
|
|
外部调度器类(ExternalController)
字段 |
类型 |
备注 |
id |
int |
主键 |
floor |
int |
该控制器所在的楼层 |
up_button |
boolean |
上行请求按钮状态(按下/未按下) |
down_button |
boolean |
下行请求按钮状态(按下/未按下) |
elevator_id |
int |
关联的电梯ID |
|
|
|
方法 |
返回值 |
备注 |
requestElevator(direction) |
void |
请求电梯(上行或下行) |
cancelRequest(direction) |
void |
取消电梯请求 |
|
|
|
电梯类(Elevator)
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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| import java.util.*; import lombok.Data;
@Data class Elevator { private int id; private int currentFloor; private String status; private String number; private Queue<Request> requestQueue; private List<Integer> targetFloorsList;
public Elevator(int id, int currentFloor, String number) { this.id = id; this.currentFloor = currentFloor; this.status = "stop"; this.number = number; this.requestQueue = new LinkedList<>(); this.targetFloorsList = new ArrayList<>(); }
public void addRequest(Request request) { requestQueue.add(request); processNextRequest(); }
private void processNextRequest() { if (status.equals("stop") && !requestQueue.isEmpty()) { Request request = requestQueue.poll(); if (currentFloor > request.getFloor()) { status = "down"; } else if (currentFloor < request.getFloor()) { status = "up"; } moveToFloor(request.getFloor()); } }
private void moveToFloor(int floor) { while (currentFloor != floor) { if (status.equals("up")) { currentFloor++; } else if (status.equals("down")) { currentFloor--; } } status = "stop"; openDoors(); }
public void openDoors() { System.out.println("Doors opening at floor " + currentFloor); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } }
public void closeDoors() { System.out.println("Doors closing at floor " + currentFloor); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } processNextRequest(); }
public void addTargetFloor(int floor) { if (!targetFloorsList.contains(floor)) { targetFloorsList.add(floor); Collections.sort(targetFloorsList); } } }
|
请求类
1 2 3 4 5 6 7 8 9 10
| @Data class Request { private int floor; private String direction;
public Request(int floor, String direction) { this.floor = floor; this.direction = direction; } }
|
外部调度器类(ExternalController)
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 41 42 43 44 45 46
| public class ExternalController { private int floor; private boolean upButton; private boolean downButton; private int elevatorId;
public ExternalController(int floor, int elevatorId) { this.floor = floor; this.upButton = false; this.downButton = false; this.elevatorId = elevatorId; }
public void requestElevator(String direction, Elevator elevator) { if ("up".equals(direction)) { this.upButton = true; } else if ("down".equals(direction)) { this.downButton = true; } Request request = new Request(floor, direction); elevator.addRequest(request); }
public void cancelRequest(String direction) { if ("up".equals(direction)) { this.upButton = false; } else if ("down".equals(direction)) { this.downButton = false; } }
public boolean isUpButton() { return upButton; }
public boolean isDownButton() { return downButton; } }
|
测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class Main { public static void main(String[] args) { Elevator elevator = new Elevator(1, 0, "Elevator 1"); ExternalController controller1 = new ExternalController(1, elevator.getId()); ExternalController controller2 = new ExternalController(2, elevator.getId());
controller1.requestElevator("up", elevator); controller2.requestElevator("up", elevator);
elevator.addTargetFloor(5); elevator.closeDoors();
elevator.moveToFloor(5); elevator.openDoors(); } }
|
改进
上面的代码存在问题,当电梯一开始为 55层 ,用户1
在第一层楼的外部控制器点击上去 ,用户2 随后在
第二层楼的外部控制器点击上去,
电梯在下到第2层时,并不会停下,而是直接下到第一层
电梯类
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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| import java.util.*; import lombok.Data;
@Data class Elevator { private int id; private int currentFloor; private String status; private String number; private TreeSet<Integer> upRequests; private TreeSet<Integer> downRequests;
public Elevator(int id, int currentFloor, String number) { this.id = id; this.currentFloor = currentFloor; this.status = "stop"; this.number = number; this.upRequests = new TreeSet<>(); this.downRequests = new TreeSet<>(Collections.reverseOrder()); }
public void addRequest(Request request) { if ("up".equals(request.getDirection())) { upRequests.add(request.getFloor()); } else if ("down".equals(request.getDirection())) { downRequests.add(request.getFloor()); } processRequests(); }
private void processRequests() { if (status.equals("stop")) { if (!upRequests.isEmpty()) { if (currentFloor < upRequests.first()) { status = "up"; moveToFloor(upRequests.first()); } else if (currentFloor > upRequests.last()) { status = "down"; moveToFloor(downRequests.first()); } } else if (!downRequests.isEmpty()) { status = "down"; moveToFloor(downRequests.first()); } } }
private void moveToFloor(int targetFloor) { while (currentFloor != targetFloor) { if (status.equals("up")) { currentFloor++; } else if (status.equals("down")) { currentFloor--; } checkAndHandleCurrentFloor(); } status = "stop"; openDoors(); processRequests(); }
private void checkAndHandleCurrentFloor() { if (status.equals("up") && upRequests.contains(currentFloor)) { openDoors(); upRequests.remove(currentFloor); } else if (status.equals("down") && downRequests.contains(currentFloor)) { openDoors(); downRequests.remove(currentFloor); } }
public void openDoors() { System.out.println("Doors opening at floor " + currentFloor); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } closeDoors(); }
public void closeDoors() { System.out.println("Doors closing at floor " + currentFloor); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } }
public void addTargetFloor(int floor) { if (floor > currentFloor) { upRequests.add(floor); } else if (floor < currentFloor) { downRequests.add(floor); } processRequests(); } }
|