【问题标题】:Terminal/sink state in pytransitionspytransitions 中的终端/接收器状态
【发布时间】:2021-07-26 09:20:45
【问题描述】:

例如,我将pytransitions 与状态机一起使用

from transitions import Machine
from transitions import EventData


class Matter(object):
    def __init__(self):
        transitions = [
            {'trigger': 'heat', 'source': 'solid', 'dest': 'liquid'},
            {'trigger': 'heat', 'source': 'liquid', 'dest': 'gas'},
            {'trigger': 'cool', 'source': 'gas', 'dest': 'liquid'},
            {'trigger': 'cool', 'source': 'liquid', 'dest': 'solid'}
        ]
        self.machine = Machine(
                model=self,
                states=['solid', 'liquid', 'gas'],
                transitions=transitions,
                initial='solid',
                send_event=True
        )

    def on_enter_gas(self, event: EventData):
        print(f"entering gas from {event.transition.source}")

    def on_enter_liquid(self, event: EventData):
        print(f"entering liquid from {event.transition.source}")

    def on_enter_solid(self, event: EventData):
        print(f"entering solid from {event.transition.source}")

我想添加一个状态,任何触发器都保持相同的状态,不调用转换,不明确指定每个可能的触发器,也不忽略所有无效触发器(因为这非常适合调试) .

例如,我想要一个状态crystal,可以通过从liquid 触发crystalize 来达到此状态,对此任何事件都不会执行任何操作。

这可以通过库实现吗?

表达这个问题的另一种方式是ignore_invalid_triggers=True 仅针对特定州,而不是所有州。

【问题讨论】:

    标签: python transition state-machine pytransitions


    【解决方案1】:

    与转换类似,状态也可以用字典来定义:

    from transitions import Machine, MachineError
    
    
    class Matter(object):
        def __init__(self):
            transitions = [
                {'trigger': 'heat', 'source': 'solid', 'dest': 'liquid'},
                {'trigger': 'heat', 'source': 'liquid', 'dest': 'gas'},
                {'trigger': 'cool', 'source': 'gas', 'dest': 'liquid'},
                {'trigger': 'cool', 'source': 'liquid', 'dest': 'solid'},
                # add a transition to 'crystal' which is valid from anywhere
                {'trigger': 'crystallize', 'source': '*', 'dest': 'crystal'},
            ]
            self.machine = Machine(
                    model=self,
                    states=['solid', 'liquid', 'gas',
                            # initialized 'crystal' with dictionary
                            {'name': 'crystal', 'ignore_invalid_triggers': True}],
                    transitions=transitions,
                    initial='solid',
                    send_event=True
            )
    
    
    m = Matter()
    assert m.is_solid()
    try:
        m.cool()  # raises a machine error since cool cannot be called from 'solid'
        assert False
    except MachineError:
        pass
    assert m.crystallize()  # transitions to 'crystal'
    assert m.is_crystal()
    assert not m.heat()  # note that the transition will return 'False' since it did not happen but no exception was thrown
    assert m.is_crystal()  # state machine is still in state 'crystal'
    

    您也可以通过 State(name='crystal', ignore_invalid_triggers=True) 代替 {'name': 'crystal', 'ignore_invalid_triggers': True}documentation's state section中提到了这个表格:

    但在某些情况下,您可能希望默默地忽略无效触发器。您可以通过设置 ignore_invalid_triggers=True 来做到这一点(在逐个州的基础上,或对所有州全局):

    【讨论】:

    • 谢谢!当状态被定义为枚举时,我如何使用这两种方法?
    • 对于枚举,您需要单独传递值并将States.CRYSTAL替换为适当的dictstates=[s if s != States.CRYSTAL else dict(name=States.CRYSTAL, ignore_invalid_triggers=True) for s in States]
    【解决方案2】:

    我最终做的是在机器已经用它的转换实例化之后:

    self.machine.get_state(states.crystal.name).ignore_invalid_triggers = True
    

    我真的不喜欢那个解决方案,因为它破坏了转换初始化作为列表的非常简洁的设计,但这是我能找到的所有东西,它确实有效。

    【讨论】:

    • 使用Enums时可以省略.nameself.machine.get_state(states.crystal).ignore_invalid_triggers = True
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 2022-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-01
    • 1970-01-01
    相关资源
    最近更新 更多