• 基于Python实现的电梯进程及调度管理


    进程管理 - 电梯调度_设计方案报告

    操作系统第一次课程作业 - 电梯调度

    项目需求

    某一层楼20层,有五部互联的电梯。基于线程思想,编写一个电梯调度程序。

    功能描述

    1. 每个电梯里面设置必要功能键:如数字键关门键开门键上行键下行键报警键、当前电梯的楼层数上升及下降状态等。
    2. 每层楼的每部电梯门口,应该有上行和下行按钮和当前电梯状态的数码显示器
    3. 五部电梯门口的按钮是互联结的,即当一个电梯按钮按下去时,其他电梯的相应按钮也就同时点亮,表示也按下去了。
    4. 所有电梯初始状态都在第一层。每个电梯如果在它的上层或者下层没有相应请求情况下,则应该在原地保持不动

    开发环境

    • 开发环境: Windows 10
    • 开发软件:
      1. PyCharm 2019.1.1.PC-191.6605.12
      2. Qt Designer v5.11.2.0
    • 开发语言: python3
    • 主要引用块内容:
      1. PyQt5 (QTimer, QtCore, QtGui, QtWidgets)
      2. pyqt5-tools
      3. threading

    项目结构

    │  myElevator.exe   
    │  README.md   
    │  电梯调度_设计方案报告.md   
    │  
    ├─Resources   
    │  ├─Button   
    │  │      doordown.png   
    │  │      doordown_hover.png   
    │  │      doordown_pressed.png   
    │  │      doorup.png   
    │  │      doorup_hover.png   
    │  │      doorup_pressed.png   
    │  │      down.png   
    │  │      down_hover.png   
    │  │      down_pressed.png   
    │  │      state.png   
    │  │      state_down.png   
    │  │      state_up.png   
    │  │      up.png   
    │  │      up_hover.png   
    │  │      up_pressed.png   
    │  │      
    │  ├─Figure   
    │  │      people.png   
    │  │      
    │  └─Icon   
    │          elevator.ico   
    │          icon.png   
    │          
    └─Src   
            dispatch.py   
            myElevator.py   
            myElevatorInterface.py      
    
    • 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

    操作说明

    • 双击运行myElevator.exe, 进入电梯模拟系统如下图

    在这里插入图片描述

    • 点击每部电梯的功能键(开/关键, 报警器, 楼层按钮), 进行单部电梯内命令处理模拟

    在这里插入图片描述

    • 在上方下拉框中选择所在楼层, 并点击上/下按钮, 进行多部电梯外命令处理模拟

    在这里插入图片描述

    • 下拉框中选择所在楼层, 并点击未被禁用电梯的上/下行开关****(互联结)*, 进行多部电梯外命令处理**模拟

    在这里插入图片描述

    系统分析

    • 单部电梯内命令处理

      • 内部事件:
        1. 用户点击楼层按钮
        2. 用户点击开/关门按钮
        3. 用户点击报警器
      • 预期响应:
        1. 若按键楼层与当前楼层相同 => 该电梯开门(并等待用户自行关闭)
          • 若是在电梯运行过程瞬间点击该楼层, 则忽略该命令
        2. 若按键楼层与当前楼层不同:
          1. 若电梯为静止状态 => 将该楼层信息加入消息队列中
          2. 若电梯忙碌:
            1. 若电梯正在上行且按键楼层大于电梯此时楼层 => 将该楼层信息加入消息队列中
            2. 若电梯正在下行且按键楼层小于电梯此时楼层 => 将该楼层信息加入不顺路消息队列中
            3. 若电梯正在下行且按键楼层小于电梯此时楼层 => 将该楼层信息加入消息队列中
            4. 若电梯正在下行且按键楼层大于电梯此时楼层 => 将该楼层信息加入不顺路消息队列中

    • 多部电梯外命令处理

      • 外部事件:

        1. 用户选择所在楼层
        2. 用户选择上/下行方式
        3. 用户可以点击任意没禁用电梯的上/下行按钮 (每部电梯的按钮是相互关联的)
      • 预期响应:

        1. 筛选可以响应的电梯 (没禁用的电梯)

        2. 可响应电梯中:

          1. 向上顺路:

            若某电梯正在向上运动并且用户选择楼层大于该电梯当前楼层 => "可调度性"定义为(用户楼层 - 该电梯当前楼层)

          2. 向下顺路:

            若某电梯正在向下运动并且用户选择楼层小于该电梯当前楼层 => "可调度性"定义为(该电梯当前楼层 - 用户楼层)

          3. 某电梯静止:

            若某电梯当前处于静止状态 => "可调度性"定义为(|该电梯当前楼层 - 用户楼层|)

        3. 选择可调度性最好的作为最佳电梯

          1. 若最佳电梯当前就在用户选择的楼层 => 该电梯开门(并等待用户自行关闭)
          2. 否则 => 将用户楼层信息加入该最佳电梯的消息队列中

    系统设计

    界面设计

    1. 整体设计

    在这里插入图片描述
    在这里插入图片描述

    1. 墙模型: QtWidgets.QGraphicsView

    2. 电梯模型(包括电梯文字模型):

      1. 电梯背景 QtWidgets.QGraphicsView
      2. 前方两扇门 QtWidgets.QGraphicsView
      3. 将两扇门绑定动画 QPropertyAnimation
      4. 电梯文字 QtWidgets.QLabel
    3. 电梯楼层数码管模型: QtWidgets.QLCDNumber

    4. 电梯上下行标志 & 门口按钮: QtWidgets.QGraphicsView | QtWidgets.QPushButton

    5. 报警器模型: QtWidgets.QPushButton

    6. 楼层按键模型: QtWidgets.QGridLayout | QtWidgets.QWidget | QtWidgets.QPushButton

    7. 开关键模型: QtWidgets.QPushButton

    8. 下拉框模型: QtWidgets.QComboBox | QtWidgets.QLabel | QtWidgets.QPushButton

    9. 小人模型: QtWidgets.QGraphicsView | QPropertyAnimation

    状态设计

    1. 门状态
      • OPEN = 0 # 开门状态
      • CLOSED = 1 # 关门状态
    2. 电梯状态
      • STANDSTILL = 0 # 静止状态
      • RUNNING_UP = 1 # 电梯上行状态
      • RUNNING_DOWN = 2 # 电梯下行状态
    3. 动画状态
      • NOPE = 0 # 空动画
      • READYSTART = 1 # 电梯即将运动
      • READYSTOP` = 2 # 电梯即将停止
    4. 用户选择状态
      • GOUP = 1 # 用户要上行
      • GODOWN = 2 # 用户要下行

    类设计

    1. mywindow类: 界面的显示

      class mywindow(QtWidgets.QMainWindow, Ui_MainWindow):
          def __init__(self):
              super(mywindow, self).__init__()
              self.setupUi(self)
              self.setWindowTitle('myElevator')
              self.setWindowIcon(QIcon('Resources/Icon/icon.png'))
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    2. Ui_MainWindow类: 界面控件设置及其逻辑

      class Ui_MainWindow(object):
              def __init__(self):
              self.Ctrl = Controler(self)  # 与调度文件建立连接
      
              self.elevEnabled = [True] * 5  # 电梯状态(可使用/禁用)标志位
              self.doorState = [CLOSED] * 5  # 电梯门状态(开门/关门)标志位
              self.elevState = [STANDSTILL] * 5  # 电梯状态(运行向上/运行向下/静止)标志位
              self.animState = [NOPE] * 5  # 动画播放状态(空/即将运动/即将停止)标志位
              self.elevNow = [1] * 5  # 电梯楼层
      
              self.wall = []  # 墙模型
              self.elevator_back = []  # 电梯模型
              self.elevator_front = []
              self.elevator_Anim = []
              self.label = []
              self.lcdNumber = []  # 数码管模型
              self.stateshow = []  # 上下行标志模型
              self.updoorbtn = []  # 门口上下行按钮模型
              self.downdoorbtn = []
              self.warnbtn = []  # 报警器模型
              self.gridLayoutWidget = []  # 楼层按键模型
              self.gridLayout = []
              self.openbtn = []  # 开关键模型
              self.closebtn = []
              self.figure = []  # 小人模型
              self.figure_Anim = []
              
          def setupUi(self, MainWindow):
          def retranslateUi(self, MainWindow):
              
          # 报警器槽函数
          def warningClick(self):
              
          # 楼层按键槽函数
          def btnClick(self):
              
          # 外命令选择槽函数
          def chooseClick(self):
              
          # 开关门槽函数
          def doorClick(self):
      
      • 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
    3. Controler类: 用于电梯的控制及调度

      class Controler(object):
          def __init__(self, Elev):
              # 与界面文件建立连接
              self.elev = Elev
      
              # 创建定时器, 1s中更新一次电梯状态
              self.timer = QTimer()
              self.timer.timeout.connect(self.updateElevState)
              self.timer.start(1000)
      
              # 5个电梯内部消息列表(用列表代替队列)
              self.messQueue = []
              for i in range(0, 5):
                  self.messQueue.append([])
              # 5个电梯内部不顺路消息列表
              self.messQueue_reverse = []
              for i in range(0, 5):
                  self.messQueue_reverse.append([])
                  
          # 警报器槽函数
          def warnCtrl(self, whichelev):
          
          # 开关门槽函数
          def doorCtrl(self, whichelev, whichcommand):
              
          # 开门动画
          def openDoor_Anim(self, whichelev):
          # 关门动画
          def closeDoor_Anim(self, whichelev):
          # 小人进电梯动画
          def figureIn_Anim(self, whichelev):
          # 小人出电梯动画
          def figureOut_Anim(self, whichelev):
          # 将门至于顶层
          def setDoorTop(self, whichelev):
          # 将小人至于顶层
          def setFigureTop(self, whichelev):
              
          # 内命令电梯运动
          def elevMove(self, whichelev, dest):
              
          # 外命令电梯调度
          def chooseCtrl(self, whichfloor, choice):
              
          # 更新电梯状态
          def updateElevState(self):
      
      • 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

    系统实现

    内命令处理

    1. 报警器

      • 用户点击某报警器 => 更改其颜色(响应用户) => 弹出警告窗口"第i号电梯已损坏" => 调用控制器进行warnCtrl处理

      • 该电梯状态设置为禁用 => 设置该电梯各部件禁用并停止所有动画

      • 如果所有5部电梯都禁用 => 设置下拉框等外命令空间禁用 => 弹出窗口"所有电梯以损坏"

        # 报警器槽函数
        def warningClick(self):
            which_warnbtn = int(self.sender().objectName()[-1])
            print("点击了{0}号报警器".format(which_warnbtn))
            self.warnbtn[which_warnbtn].setStyleSheet("background-color: rgb(255, 255, 255);")
            self.MessBox = QtWidgets.QMessageBox.information(self.warnbtn[int(which_warnbtn)], "警告",  # 弹出警告框
                                                             "第" + str(which_warnbtn) + "号电梯已损坏, 不能继续使用")
            self.warnbtn[which_warnbtn].setStyleSheet("background-color: rgb(180, 0, 0);")
        
            self.Ctrl.warnCtrl(which_warnbtn)  # 调用控制器进行warnCtrl处理
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        # 警报器槽函数
        def warnCtrl(self, whichelev):
            self.elev.elevEnabled[whichelev] = False  # 该电梯禁用
        
            # 省略电梯各部件禁用(详见Src中dispatch.py)
        
            # 五部电梯全部禁用
            arr = np.array(self.elev.elevEnabled)
            if ((arr == False).all()):
                self.elev.comboBox.setEnabled(False)  # 下拉框禁用
                self.elev.chooselabel.setEnabled(False)  # 文字禁用
                self.elev.upbtn.setEnabled(False)  # 上行按钮禁用
                self.elev.downbtn.setEnabled(False)  # 下行按钮禁用
        
                time.sleep(0.5)
                self.MessBox = QtWidgets.QMessageBox.information(self.elev, "警告", "所有电梯已损坏!")
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16

    2. 楼层按键

      • 用户点击某个楼层按键 => 获取电梯编号 => 获取楼层信息 => 改变按钮背景颜色(模拟点击状态) => 将该按钮设置为不可点击状态 => 调用控制器进行elevMove处理

      • 如果按键楼层大于当前楼层:

        • 电梯处于STANDSTILL状态 => 将目标楼层加入 消息队列
        • 电梯正在RUNNING_UP状态 => 将目标楼层加入 消息队列并排序
        • 电梯正在RUNNING_DOWN状态 => 将目标楼层加入 不顺路消息队列并排序
      • 如果按键楼层小于当前楼层:

        • 电梯处于STANDSTILL状态 => 将目标楼层加入 消息队列
        • 电梯正在RUNNING_DOWN状态 => 将目标楼层加入 消息队列并反向排序
        • 电梯正在RUNNING_UP状态 => 将目标楼层加入 不顺路消息队列并反向排序
      • 如果按键就为当前楼层:

        • 电梯处于STANDSTILL状态 => 打开门(并等待用户自行关闭)
        • 恢复按键背景并重新允许点击
        # 楼层按键槽函数
        def btnClick(self):
            whichbtn = self.sender()
        
            btn_name = whichbtn.objectName()
            buf = [int(s) for s in btn_name.split() if s.isdigit()]  # 提取字符串中的数字
            whichelev = buf[0]
            whichfloor = buf[1]
            print("{0}号电梯, {1}按键被按".format(whichelev, whichfloor))
        
            whichbtn.setStyleSheet("background-color: rgb(255, 150, 3);")  # 改变按钮背景颜色(模拟点击状态)
            whichbtn.setEnabled(False)  # 将该按钮设置为不可点击状态
            self.Ctrl.elevMove(whichelev, whichfloor)  # 调用控制器进行elevMove处理
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        # 内命令电梯运动
        def elevMove(self, whichelev, dest):
        
            nowFloor = self.elev.elevNow[whichelev]  # 获取当前电梯位置
        
            if nowFloor < dest:  # 如果按键大于当前楼层
                if self.elev.elevState[whichelev] == STANDSTILL:  # 电梯处于静止状态
                    self.messQueue[whichelev].append(dest)  # 将目标楼层加入 消息队列
        
                else:
                    if self.elev.elevState[whichelev] == RUNNING_UP:  # 电梯正在向上运行
                        self.messQueue[whichelev].append(dest)  # 将目标楼层加入 消息队列并排序
                        self.messQueue[whichelev].sort()
                    elif self.elev.elevState[whichelev] == RUNNING_DOWN:  # 电梯正在向下运行
                        self.messQueue_reverse[whichelev].append(dest)  # 将目标楼层加入 不顺路消息队列并排序
                        self.messQueue_reverse[whichelev].sort()
        
            elif nowFloor > dest:
                if self.elev.elevState[whichelev] == STANDSTILL:
                    self.messQueue[whichelev].append(dest)  # 将目标楼层加入 消息队列
        
                else:
                    if self.elev.elevState[whichelev] == RUNNING_DOWN:
                        self.messQueue[whichelev].append(dest)  # 将目标楼层加入 消息队列并反向排序
                        self.messQueue[whichelev].sort()
                        self.messQueue[whichelev].reverse()
                    elif self.elev.elevState[whichelev] == RUNNING_UP:
                        self.messQueue_reverse[whichelev].append(dest)  # 将目标楼层加入 不顺路消息队列并反向排序
                        self.messQueue_reverse[whichelev].sort()
                        self.messQueue_reverse[whichelev].reverse()
        
            else:  # 如果按键就为当前楼层
                if self.elev.elevState[whichelev] == STANDSTILL:  # 电梯静止 => 打开门(并等待用户自行关闭)
                    self.elev.doorState[whichelev] = OPEN
                    self.openDoor_Anim(whichelev)
        
                button = self.elev.findChild(QtWidgets.QPushButton,
                                             "button {0} {1}".format(whichelev, nowFloor))  # 恢复按键背景并重新允许点击
                button.setStyleSheet("")
                button.setEnabled(True)
        
        • 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

    3. 开/关门

      • 用户点击某个开/关门按钮 => 获取电梯编号 => 获取按键信息 => 调用控制器进行doorCtrl处理

      • 如果用户要开门:

        • 如果当前门是CLOSED状态并且电梯处于STANDSTILL状态 => 门的状态改为OPEN => 电梯状态更新为禁用 => 播放开门动画
      • 如果用户要关门:

        • 如果当前门是OPEN状态并且电梯处于STANDSTILL状态 => 门的状态改为CLOSED => 电梯状态更新为允许使用 => 将电梯门前的上下行开关恢复 => 播放关门动画
        # 开关门槽函数
            def doorCtrl(self, whichelev, whichcommand):
                if whichcommand == 0:  # 如果用户要开门
                    if self.elev.doorState[whichelev] == CLOSED and self.elev.elevState[
                        whichelev] == STANDSTILL:  # 如果当前门是关闭状态并且电梯是静止的
                        self.elev.doorState[whichelev] = OPEN  # 先将门状态更新为打开
                        self.elev.elevEnabled[whichelev] = False
        
                        self.openDoor_Anim(whichelev)
        
                else:  # 如果用户要关门
                    if self.elev.doorState[whichelev] == OPEN and self.elev.elevState[
                        whichelev] == STANDSTILL:  # 如果当前门是打开状态并且电梯是静止的
                        self.elev.doorState[whichelev] = CLOSED  # 先将门状态更新为关闭
                        self.elev.elevEnabled[whichelev] = True
        
                        #将电梯门前的上下行按键熄灭
                        for i in range(0, 5):
                            if self.elev.elevEnabled[i]:
                                self.elev.updoorbtn[i].setStyleSheet("QPushButton{border-image: url(Resources/Button/doorup.png)}"
                                                                "QPushButton:hover{border-image: url(Resources/Button/doorup_hover.png)}"
                                                                "QPushButton:pressed{border-image: url(Resources/Button/doorup_pressed.png)}")
                                self.elev.downdoorbtn[i].setStyleSheet("QPushButton{border-image: url(Resources/Button/doordown.png)}"
                                                                  "QPushButton:hover{border-image: url(Resources/Button/doordown_hover.png)}"
                                                                  "QPushButton:pressed{border-image: url(Resources/Button/doordown_pressed.png)}")
                                self.elev.updoorbtn[i].setEnabled(True)
                                self.elev.downdoorbtn[i].setEnabled(True)
                        self.closeDoor_Anim(whichelev)
        
        • 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

    外命令处理

    1. 选择楼层及上下行

      • 用户选择下拉框数字并点击上/下行开关 => 获取用户选择的楼层 => 获取上/下行并转换为用户选择状态 => 调用控制器进行chooseCtrl处理

        # 外命令选择槽函数
        def chooseClick(self):
            whichfloor = int(self.comboBox.currentText())
            whichbtn = self.sender().objectName()
        
            if whichbtn[0] == 'd':
                if whichbtn != "downbtn":  # 如果是电梯门前的按钮
                    for i in range(0, len(self.downdoorbtn)):
                        if self.elevEnabled[i]:
                            self.downdoorbtn[i].setStyleSheet(
                                "QPushButton{border-image: url(Resources/Button/doordown_pressed.png)}")
                            self.downdoorbtn[i].setEnabled(False)
                choice = GODOWN
            else:
                if whichbtn != "upbtn":  # 如果是电梯门前的按钮
                    for i in range(0, len(self.downdoorbtn)):
                        if self.elevEnabled[i]:
                            self.updoorbtn[i].setStyleSheet(
                                "QPushButton{border-image: url(Resources/Button/doorup_pressed.png)}")
                            self.updoorbtn[i].setEnabled(False)
        
                choice = GOUP
        
            print("用户选择了 {0} {1}".format(whichfloor, choice))
        
            self.Ctrl.chooseCtrl(whichfloor, choice)  # 调用控制器进行chooseCtrl处理
        
        • 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

    2. 点击电梯上下行按钮

      • 五部电梯门口的按钮是互联结的 => 用户点击了某一个按钮 => 将所有没被禁用的按钮都设置为点击状态 => 设置为失效状态 => 调用控制器进行chooseCtrl处理

    3. 电梯调度

      • 初步筛选没损坏的电梯 => 计算每部可用电梯的"可调度性" => 选择可调度性最好的电梯作为最佳电梯

      • 如果最佳电梯就在用户选择的楼层 => 打开门并等待用户自行关闭 => 播放开门动画 => 该电梯设置为禁用

      • 否则 => 加入该最佳电梯的消息队列 => 将用户的目标楼层设定为特殊颜色

      • 可调度性:

        • 向上顺路: 若某电梯正在RUNNING_UP状态并且用户选择楼层大于该电梯当前楼层 => "可调度性"定义为(用户楼层 - 该电梯当前楼层)
        • 向下顺路: 若某电梯正在RUNNING_DOWN状态并且用户选择楼层小于该电梯当前楼层 => "可调度性"定义为(该电梯当前楼层 - 用户楼层)
        • 某电梯静止:若某电梯当前处于STANDSTILL状态 => "可调度性"定义为(|该电梯当前楼层 - 用户楼层|)
        # 外命令电梯调度
            def chooseCtrl(self, whichfloor, choice):
        
                # region 初步筛选没损坏的电梯
                EnabledList = []
                for i in range(0, 5):
                    if self.elev.elevEnabled[i]:
                        EnabledList.append(i)
                print(EnabledList)
                # endregion
        
                # region 计算每部可用电梯的"可调度性"
                dist = [INFINITE] * 5  # 可使用电梯距离用户的距离
                for EnabledElev in EnabledList:
                    if self.elev.elevState[EnabledElev] == RUNNING_UP and choice == GOUP and whichfloor > self.elev.elevNow[
                        EnabledElev]:  # 向上顺路
                        dist[EnabledElev] = whichfloor - self.elev.elevNow[EnabledElev]
        
                    elif self.elev.elevState[EnabledElev] == RUNNING_DOWN and choice == GODOWN and whichfloor < \
                            self.elev.elevNow[EnabledElev]:  # 向下顺路
                        dist[EnabledElev] = self.elev.elevNow[EnabledElev] - whichfloor
        
                    elif self.elev.elevState[EnabledElev] == STANDSTILL:  # 该电梯此时静止
                        dist[EnabledElev] = abs(self.elev.elevNow[EnabledElev] - whichfloor)
                # endregion
        
                BestElev = dist.index(min(dist))  # 选择可调度性最好的电梯作为最佳电梯
                if dist[BestElev] == 0:  # 如果最佳电梯就在用户选择的楼层
                    self.elev.doorState[BestElev] = OPEN  # 打开门并等待用户自行关闭
                    self.openDoor_Anim(BestElev)
        
                else:
                    self.messQueue[BestElev].append(whichfloor)  # 加入该最佳电梯的消息队列
                    button = self.elev.findChild(QtWidgets.QPushButton,
                                                 "button {0} {1}".format(BestElev, whichfloor))  # 将用户的目标楼层设定为特殊颜色
                    button.setStyleSheet("background-color: rgb(11, 15, 255);")
                    button.setEnabled(False)
        
        • 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

    更新电梯状态

    1. 定时器

      • 采用定时器(1s调用一次方法更新电梯状态)

        # 创建定时器, 1s中更新一次电梯状态
        self.timer = QTimer()
        self.timer.timeout.connect(self.updateElevState)
        self.timer.start(1000)
        
        • 1
        • 2
        • 3
        • 4

    2. 更新电梯状态

      • 遍历五部电梯

      • 某个电梯的消息队列不为空

        • 如果电梯门是打开的 => 等待电梯关门
        • 电梯处于STANDSTILL状态 => 播放开门动画 => 播放小人进门动画 => 根据即将运行的方向更新电梯状态 => 动画变为READYSTART状态
        • 动画处于READYSTART状态 => 播放关门动画 => 动画变为NOPE状态
        • 动画处于就绪READYSTOP状态 => 结束该命令的处理 => 动画变为NOPE状态 => 电梯变为STANDSTILL状态
        • 除此之外 => 获取第一个目标楼层
          • 向上运动: 当前楼层小于目标楼层 => 电梯状态变为RUNNING_UP => 显示向上运行图标 => 将当前楼层加一并设置数码管显示
          • 向下运动: 当前楼层大于目标楼层 => 电梯状态变为RUNNING_DOWN => 显示向下运行图标 => 将当前楼层减一并设置数码管显示
          • 电梯到达目的地: 当前楼层等于目标楼层 => 播放开门动画 => 播放小人出电梯动画 => 动画变为READYSTOP状态 => 将该楼层按钮恢复到原始状态
      • 某个电梯的消息队列为空 & 不顺路消息队列不为空 => 交换两个队列

      • 遍历五部电梯, 在运行过程中禁止点击报警键:

        • 如果这个电梯没被禁用
          • 如果电梯处于STANDSTILL状态 => 允许使用报警键
          • 否则 => 报警键禁用
        # 更新电梯状态
        def updateElevState(self):
            # print('timer clock......')
        
            for i in range(0, len(self.messQueue)):  # 遍历五部电梯
                if len(self.messQueue[i]):  # 某个电梯的消息队列不为空
        
                    if self.elev.doorState[i] == OPEN:  # 如果电梯门是打开的 => 等待电梯关门
                        continue
        
                    elif self.elev.elevState[i] == STANDSTILL:  # 电梯处于静止状态
                        self.openDoor_Anim(i)
                        self.figureIn_Anim(i)
        
                        if self.elev.elevNow[i] < self.messQueue[i][0]:  # 根据即将运行的方向更新电梯状态
                            self.elev.elevState[i] = RUNNING_UP
                        elif self.elev.elevNow[i] > self.messQueue[i][0]:
                            self.elev.elevState[i] = RUNNING_DOWN
        
                        self.elev.animState[i] = READYSTART  # 动画变为就绪运行状态
        
                    elif self.elev.animState[i] == READYSTART:  # 动画处于就绪运行状态
                        self.closeDoor_Anim(i)
                        self.elev.animState[i] = NOPE  # 动画变为空状态
        
                    elif self.elev.animState[i] == READYSTOP:  # 动画处于就绪停止状态
                        self.messQueue[i].pop(0)  # 结束该命令的处理
                        self.closeDoor_Anim(i)
                        self.elev.animState[i] = NOPE       # 动画变为空状态
                        self.elev.elevState[i] = STANDSTILL  # 电梯变为静止状态
                        self.elev.stateshow[i].setStyleSheet("QGraphicsView{border-image: url(Resources/Button/state.png)}")
        
                    else:
                        destFloor = self.messQueue[i][0]  # 获取第一个目标楼层
        
                        if self.elev.elevNow[i] < destFloor:  # 向上运动
                            self.elev.elevState[i] = RUNNING_UP
                            self.elev.stateshow[i].setStyleSheet(
                                "QGraphicsView{border-image: url(Resources/Button/state_up.png)}")
                            self.elev.elevNow[i] = self.elev.elevNow[i] + 1  # 将当前楼层加一并设置数码管显示
                            self.elev.lcdNumber[i].setProperty("value", self.elev.elevNow[i])
        
                        elif self.elev.elevNow[i] > destFloor:  # 向下运动
                            self.elev.elevState[i] = RUNNING_DOWN
                            self.elev.stateshow[i].setStyleSheet(
                                "QGraphicsView{border-image: url(Resources/Button/state_down.png)}")
                            self.elev.elevNow[i] = self.elev.elevNow[i] - 1  # 将当前楼层减一并设置数码管显示
                            self.elev.lcdNumber[i].setProperty("value", self.elev.elevNow[i])
        
                        else:  # 电梯到达目的地
                            self.openDoor_Anim(i)
                            self.figureOut_Anim(i)
                            self.elev.animState[i] = READYSTOP  # 到达目的地 => 动画变为就绪停止状态
        
                            button = self.elev.findChild(QtWidgets.QPushButton,
                                                         "button {0} {1}".format(i, self.elev.elevNow[i]))  # 恢复该按钮的状态
                            button.setStyleSheet("")
                            button.setEnabled(True)
        
                elif len(self.messQueue_reverse[i]):  # 如果消息队列为空 & 不顺路消息队列不为空
                    self.messQueue[i] = self.messQueue_reverse[i].copy()  # 交替两个队列
                    self.messQueue_reverse[i].clear()
        
            # 电梯在运行过程中禁止点击报警键
            for i in range(0, 5):
                if self.elev.gridLayoutWidget[i].isEnabled():  # 如果这个电梯没被禁用
                    if self.elev.elevState[i] == STANDSTILL:  # 如果电梯是静止的
                        self.elev.warnbtn[i].setEnabled(True)
                    else:
                        self.elev.warnbtn[i].setEnabled(False)
        
        • 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

    动画实现

    1. 开门动画

      # 开门动画
      def openDoor_Anim(self, whichelev):
          self.elev.elevator_Anim[2 * whichelev].setDirection(QAbstractAnimation.Forward)  # 正向设定动画
          self.elev.elevator_Anim[2 * whichelev + 1].setDirection(QAbstractAnimation.Forward)
          self.elev.elevator_Anim[2 * whichelev].start()  # 开始播放
          self.elev.elevator_Anim[2 * whichelev + 1].start()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    2. 关门动画

      # 关门动画
      def closeDoor_Anim(self, whichelev):
          self.elev.elevator_Anim[2 * whichelev].setDirection(QAbstractAnimation.Backward)  # 反向设定动画
          self.elev.elevator_Anim[2 * whichelev + 1].setDirection(QAbstractAnimation.Backward)
          self.elev.elevator_Anim[2 * whichelev].start()  # 开始播放
          self.elev.elevator_Anim[2 * whichelev + 1].start()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    3. 小人进电梯动画

      # 小人进电梯动画
      def figureIn_Anim(self, whichelev):
          self.elev.figure[whichelev].setVisible(True)
          self.elev.figure_Anim[whichelev].setDirection(QAbstractAnimation.Forward)
          self.elev.figure_Anim[whichelev].start()
      
          s = threading.Timer(1.5, self.setDoorTop, (whichelev,))  # 1.5秒之后把门至于顶层
          s.start()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    4. 小人出电梯动画

      # 小人出电梯动画
      def figureOut_Anim(self, whichelev):
          self.elev.figure[whichelev].setVisible(True)
          self.elev.figure_Anim[whichelev].setDirection(QAbstractAnimation.Backward)
          self.elev.figure_Anim[whichelev].start()
      
          s = threading.Timer(1, self.setFigureTop, (whichelev,))  # 1s之后将人至于顶层
          s.start()
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    5. 将门置于顶层

      # 将门至于顶层
      def setDoorTop(self, whichelev):
          self.elev.elevator_front[2 * whichelev].raise_()
          self.elev.elevator_front[2 * whichelev + 1].raise_()
      
      • 1
      • 2
      • 3
      • 4
    6. 将小人置于顶层

      # 将小人至于顶层
      def setFigureTop(self, whichelev):
          self.elev.figure[whichelev].raise_()
          self.elev.figure[whichelev].setVisible(False)
      
      • 1
      • 2
      • 3
      • 4

    功能实现截屏展示

    1. 报警器功能展示

    在这里插入图片描述
    在这里插入图片描述

    1. 开/关门功能展示

    在这里插入图片描述

    1. 内部指令处理与多线程

    在这里插入图片描述

    1. 外部指令处理与电梯调度

    在这里插入图片描述

    1. 上下行按钮联结

    在这里插入图片描述

  • 相关阅读:
    Redis支撑秒杀场景的关键技术和实践都有哪些?
    链接&装载(一)虚拟地址与物理地址
    北理工嵩天Python语言程序设计笔记(2 Python基本语法元素)
    macOS 13 Ventura后,打开软件显示“XXapp已损坏,无法打开”如何解决?
    使用 Java 解析 Apache 日志文件
    php使用阿里云文本内容检测openapi-sdk-php
    ABB UF C911B108 3BHE037864R010控制主板模块
    逆波兰表达式求值(leetcode 150)
    hydra(九头蛇)--参数及常见命令
    服务器数据恢复-阵列崩溃导致LVM结构破坏的数据恢复案例
  • 原文地址:https://blog.csdn.net/sheziqiong/article/details/125974949