Python實現狀態機

對於一個狀態機,最基本的要素就是狀態和事件,所以根據這個思路,我們可以設計一個具備基本功能的狀態機。

以看碟片爲例,DVD的狀態包含:已開機,正在播放,正在暫停,已關機。而觸發這些狀態的事件有:遙控開機,遙控播放,遙控暫停,遙控關機。所以畫一個狀態轉換表如下:
在這裏插入圖片描述
首先,設計狀態基類。

class FsmState:
    def enter(self, event, fsm):  ## 參數event爲觸發狀態轉換的事件, fsm則爲狀態所在狀態機
        pass
    
    def exit(self, fsm):
        pass

FsmState將是我們這個狀態機所有狀態的基類。enter函數用於執行進入該狀態的操作,exit函數用於執行離開該狀態的操作。

然後我們將例子中的四個狀態轉換爲代碼:

class DvdPowerOn(FsmState):
    def enter(self, event, fsm):
        print("dvd is power on")

class DvdPlaying(FsmState):
    def enter(self, event, fsm):
        print("dvd is going to play")

    def exit(self, fsm):
        print("dvd stoped play")

class DvdPausing(FsmState):
    def enter(self, event, fsm):
        print("dvd is going to pause")

    def exit(self, fsm):
        print("dvd stopped pause")

class DvdPowerOff(FsmState):
    def enter(self, event, fsm):
        print("dvd is power off")

接下來,設計事件基類。

class FsmEvent:
    pass

FsmEvent將是我們這個狀態機中所有事件的基類。這個事件中的所有成員,均由子類自由添加。
子事件如下:

class PowerOnEvent(FsmEvent):
    pass

class PowerOffEvent(FsmEvent):
    pass

class PlayEvent(FsmEvent):
    pass

class PauseEvent(FsmEvent):
    pass

然後是我們的狀態機基類,用於將我們的狀態和事件聯繫起來。

from collections import namedtuple

Transaction = namedtuple("Transaction", ["prev_state", "event", "next_state"])    ## 一次狀態轉換的所有要素:上一個狀態--事件-->下一個狀態

class FSM:
    def __init__(self, context):                                                  ## context:狀態機上下文
        self.context = context
        self.state_transaction_table = []                                         ## 常規狀態轉換表
        self.global_transaction_table = []                                        ## 全局狀態轉換表
        self.current_state = None
        self.working_state = FsmState

    def add_global_transaction(self, event, end_state):     # 全局轉換,直接進入到結束狀態
        if not issubclass(end_state, FsmFinalState):
            raise FsmException("The state should be FsmFinalState")
        self.global_transaction_table.append(Transaction(self.working_state, event, end_state))

    def add_transaction(self, prev_state, event, next_state):
        if issubclass(prev_state, FsmFinalState):
            raise FsmException("It's not allowed to add transaction after Final State Node")
        self.state_transaction_table.append(Transaction(prev_state, event, next_state))

    def process_event(self, event):
        for transaction in self.global_transaction_table:
            if isinstance(event, transaction.event):
                self.current_state = transaction.next_state()
                self.current_state.enter(event, self)
                self.clear_transaction_table()
                return
        for transaction in self.state_transaction_table:
            if isinstance(self.current_state, transaction.prev_state) and isinstance(event, transaction.event):
                self.current_state.exit(self.context)
                self.current_state = transaction.next_state()
                self.current_state.enter(event, self)
                if isinstance(self.current_state, FsmFinalState):
                    self.clear_transaction_table()
                return
        raise FsmException("Transaction not found")
    
    def clear_transaction_table(self):
        self.global_transaction_table = []
        self.state_transaction_table = []
        self.current_state = None

    def run(self):
        if len(self.state_transaction_table) == 0: return
        self.current_state = self.state_transaction_table[0].prev_state()
        self.current_state.enter(None, self)

    def isRunning(self):
        return self.current_state is not None

    def next_state(self, event):
        for transaction in self.global_transaction_table:
            if isinstance(event, transaction.event):
                return transaction.next_state
        for transaction in self.state_transaction_table:
            if isinstance(self.current_state, transaction.prev_state) and isinstance(event, transaction.event):
                return transaction.next_state
        return None

在上述代碼中看到的FsmException定義如下:

class FsmException(Exception):
    def __init__(self, description):
        super().__init__(description)

所以最後,設計我們的DVD類。

class DVD(FSM):
    def __init__(self):
        super().__init__(None)

讓DVD類運行起來:

dvd = DVD();
dvd.add_transaction(DvdPowerOff, PowerOnEvent, DvdPowerOn)
dvd.add_transaction(DvdPowerOn, PowerOffEvent, DvdPowerOff)
dvd.add_transaction(DvdPowerOn, PlayEvent, DvdPlaying)
dvd.add_transaction(DvdPlaying, PowerOffEvent, DvdPowerOff)
dvd.add_transaction(DvdPlaying, PauseEvent, DvdPausing)
dvd.add_transaction(DvdPausing, PowerOffEvent, DvdPowerOff)
dvd.add_transaction(DvdPausing, PlayEvent, DvdPlaying)
dvd.run()

dvd.process_event(PowerOnEvent())
dvd.process_event(PlayEvent())
dvd.process_event(PauseEvent())
dvd.process_event(PlayEvent())
dvd.process_event(PowerOffEvent())

運行結果如下:

dvd is power off
dvd is power on
dvd is going to play
dvd stoped play
dvd is going to pause
dvd stopped pause
dvd is going to play
dvd stoped play
dvd is power off
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章