Python 状态机和在循环中切换状态
我有一个应用程序在BeagleBone Black上运行,控制一些硬件。用户界面由一个LCD显示屏和一个带有集成按钮的旋转编码器开关组成 就状态转换而言,应用程序所做的只是对按钮事件做出反应,在它们之间进行切换:Python 状态机和在循环中切换状态,python,transitions,state-machine,Python,Transitions,State Machine,我有一个应用程序在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()