Python 状态机和在循环中切换状态

Python 状态机和在循环中切换状态,python,transitions,state-machine,Python,Transitions,State Machine,我有一个应用程序在BeagleBone Black上运行,控制一些硬件。用户界面由一个LCD显示屏和一个带有集成按钮的旋转编码器开关组成 就状态转换而言,应用程序所做的只是对按钮事件做出反应,在它们之间进行切换: 初始状态:“手动” 推送事件:将状态切换到下一个(“自动”),执行自动操作 推送事件:将状态切换到下一个(“手动”),执行手动操作 总之,有两种状态,触发器是按钮按下,它实际上在这两种状态之间切换 当前代码是一个带有if。。。else条件,根据状态执行“手动”模式或“自动”模式操作

我有一个应用程序在BeagleBone Black上运行,控制一些硬件。用户界面由一个LCD显示屏和一个带有集成按钮的旋转编码器开关组成

就状态转换而言,应用程序所做的只是对按钮事件做出反应,在它们之间进行切换:

  • 初始状态:“手动”
  • 推送事件:将状态切换到下一个(“自动”),执行自动操作
  • 推送事件:将状态切换到下一个(“手动”),执行手动操作
总之,有两种状态,触发器是按钮按下,它实际上在这两种状态之间切换

当前代码是一个带有if。。。else条件,根据状态执行“手动”模式或“自动”模式操作。在循环的末尾添加一个延迟。每当检测到按钮事件时,通过中断更新(切换)手动<代码>变量

while True:
    # Toggle mode when the push button event is detected
    # It can also be detected asynchronously outside the loop,
    # but we'll keep it on the loop for a smaller example
    if gpio.event_detected(push_button):
        manual_mode = not manual_mode

    if manual_mode:
        # MANUAL MODE:
        do_user_input_actions()
    else:
        # AUTO mode
        do_automatic_actions()

   time.sleep(0.5)
这个构造类似于我以前使用过的。但我想知道在Python中是否有一种更面向对象的方法可以实现同样的效果

从本质上讲,逻辑似乎适合于使用,特别是在将来需要添加更多状态时,因此我正在考虑将代码移植到它

我可以描绘这两种状态和转换:

states = ['manual', 'sweep']
transitions = [['handle_manual_input', 'auto', 'manual'],
               ['run_auto_test', 'manual', 'auto']]
transitions = [{'trigger': 'loop', 'source': ['manual', 'auto'],
                'dest': 'manual', 'conditions': gpio.event_detected,
                'after': do_user_input_actions},
               {'trigger': 'loop', 'source': ['manual', 'auto'],
                'dest': 'auto', 'after': do_automatic_actions}]

machine = Machine(states=states, transitions=transitions, initial='manual')
然而,我不能完全想象在pytranslations模型中实现与当前无限循环等价的状态检查的正确方法是什么

实际上,在每次循环迭代中都应该处理手动输入,而不仅仅是在从自动转换时,反之亦然,以便运行自动模式

我理解应该让状态机单独处理状态和转换,以便更好地分离代码

这是一个我无法完全描述的模型实现:我欢迎任何关于如何以及在何处运行
do\u user\u input\u actions()
do\u automatic\u actions()

的指导,如果您愿意(重新)输入每个周期的状态,这应该可以做到:

from transitions import Machine
from random import choice


class Model(object):

    def on_enter_manual(self):
        # do_user_input_actions()
        print('manual')

    def on_enter_auto(self):
        # do_automatic_actions()
        print('auto')

    def event_detected(self):
        # return gpio.event_detected()
        # For simulation purposes randomise the return value
        return choice([True, False])


states = ['manual', 'auto']
transitions = [{'trigger': 'loop', 'source': ['manual', 'auto'],
                'dest': 'manual', 'conditions': 'event_detected'},
               {'trigger': 'loop', 'source': ['manual', 'auto'],
                'dest': 'auto'}]


model = Model()
machine = Machine(model=model, states=states,
                  transitions=transitions, initial='manual')

for i in range(10):
    model.loop()
转换
按添加顺序处理可能的转换。这意味着在检测到
事件时执行第一次转换
返回
True
。如果不是这种情况,将选择第二个过渡,因为它没有条件

对于此解决方案,可以进行一些调整:

a) 您可以将
source:[..]
替换为
source:'*'
,以允许从所有状态进行循环转换。如果您想在将来添加状态,这可能很有用,但如果您计划使用多个触发器,也可能适得其反

b) 如果
do\u user\u input\u actions
gpio.event\u detected
do\u automatic\u actions
是静态方法,您可以通过使用以下转换或多或少地忽略模型:

states = ['manual', 'sweep']
transitions = [['handle_manual_input', 'auto', 'manual'],
               ['run_auto_test', 'manual', 'auto']]
transitions = [{'trigger': 'loop', 'source': ['manual', 'auto'],
                'dest': 'manual', 'conditions': gpio.event_detected,
                'after': do_user_input_actions},
               {'trigger': 'loop', 'source': ['manual', 'auto'],
                'dest': 'auto', 'after': do_automatic_actions}]

machine = Machine(states=states, transitions=transitions, initial='manual')
请注意,我传递的是函数引用,而不是字符串。字符串被认为是模型函数,而函数引用可以来自任何地方。因为我们的模型或多或少是空的,所以我们可以使用
机器
实例作为模型。仅当行为非常简单时才建议这样做。专用模型使处理复杂配置更易于维护。我在之后将函数回调传递给了
,但在这种情况下,它是在状态转换之前、期间还是之后执行并不重要

出于完整性的考虑,并且由于您明确提到您不希望在转换期间处理用户输入,因此我建议使用一种方法,在这种方法中,我们可以创建可以以相同方式处理但执行不同任务的策略对象

每当状态发生变化时,策略就会被替换。这一次,我们需要在第二次转换中使用
除非
,以便在未检测到用户输入时仅进入“自动”模式(
除非
只是
条件
的方便对应项)。我们还利用了
机器
的关键字
finalize\u事件
,无论先前尝试的转换是否成功,它都将始终执行函数。我们也可以调用
模型。自己执行

from transitions import Machine
from random import choice


class AutoStrategy(object):

    def execute(self):
        # do_automatic_actions()
        print('auto')


class UserInputStrategy(object):

    def execute(self):
        # do_user_input_actions()
        print('manual')


class Model(object):

    def __init__(self):
        self.strategy = UserInputStrategy()

    def execute(self):
        self.strategy.execute()

    def on_enter_manual(self):
        # We could use a singleton here if *Strategy is stateless
        self.strategy = UserInputStrategy()

    def on_enter_strategy(self):
        self.strategy = AutoStrategy()

    def event_detected(self):
        # return gpio.event_detected()
        # For simulation purposes, randomise the return value
        return choice([True, False])


states = ['manual', 'auto']
transitions = [{'trigger': 'loop', 'source': 'auto', 'dest': 'manual',
                'conditions': 'event_detected'},
               {'trigger': 'loop', 'source': 'manual', 'dest': 'auto',
                'unless': 'event_detected'}]


model = Model()
machine = Machine(model=model, states=states, transitions=transitions,
                  initial='manual', finalize_event='execute')

for i in range(10):
    model.loop()