首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >pyTransitions触发器()方法块

pyTransitions触发器()方法块
EN

Stack Overflow用户
提问于 2021-04-02 01:13:44
回答 1查看 136关注 0票数 1

我有一个相当复杂的应用程序,包括一个图形用户界面前端和其他几个类,其中几个是基于优秀的pytransitions库的状态机。应用程序在不同的时间挂起,由于是多线程的,很难调试,但我在这里重现了一个最小的例子:

代码语言:javascript
复制
from transitions import Machine
import time

States = ["OFF", "ON"]
Transitions = [{"trigger": "PowerUp", "source": "OFF", "dest": "ON"}]

class GUI:
    def __init__(self):
        self.machine1 = Machine_1(self)
        self.machine2 = Machine_2(self)

    def on_button_pressed(self):
        self.machine2.trigger("PowerUp")
        self.machine1.trigger("PowerUp")


class Machine_1:
    def __init__(self, parent):
        self.parent = parent
        self.fsm = Machine(model=self, states=States, transitions=Transitions, initial="OFF")

    def on_enter_ON(self):
        print("machine1 is on!")
        self.parent.machine2.signal = True


class Machine_2:
    def __init__(self, parent):
        self.parent = parent
        self.fsm = Machine(model=self, states=States, transitions=Transitions, initial="OFF")
        self.signal = False

    def on_enter_ON(self):
        print("machine2 waits for machine1 ...")
        while self.signal is False:
            time.sleep(0.1)
        print("machine2 on!")

即,当machine1被“打开”时,它向machine2发送信号。machine2必须等待该信号,然后才能执行"on“状态的逻辑。

使用Python REPL作为主循环:

代码语言:javascript
复制
>>> import test
>>> gui = test.GUI()        
>>> gui.machine1.state      
'off'
>>> gui.machine2.state      
'off'
>>> gui.on_button_pressed()
machine2 waits for machine1 ...
(never returns)

machine2被困在while循环中,等待来自machine1的信号,该信号从未到达,因为machine1从未被触发。

如果我在gui.on_button_pressed()中对触发器序列进行重新排序

代码语言:javascript
复制
self.machine1.trigger("PowerUp")
self.machine2.trigger("PowerUp")

..。一切正常:

代码语言:javascript
复制
>>> gui.on_button_pressed()
machine1 is on!
machine2 waits for machine1 ...
machine2 is on!

这里发生什么事情?trigger()方法应该阻塞吗?它是否记录在某个地方,它什么时候返回?或者我正在以不受支持的方式使用on_enter_state回调?有没有我应该使用的推荐模式?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-04-06 18:53:40

pytransitions的核心Machine既不是线程的,也不是异步的。这意味着当回调没有返回时,整个事件处理以及触发器函数将被阻塞。有各种各样的方法来处理这个问题。选择哪种方式取决于您的体系结构。为所有方法提供MWE对于这样的答案来说有点太多了,但我可以简要地概述一下解决方案可能是什么样子的。

事件驱动

由于Machine_1已经有了对Machine_2的引用,而Machine_2正在等待来自Machine_1的事件,因此您可以显式地对该转换进行建模:

代码语言:javascript
复制
Transitions = [{"trigger": "PowerUp", "source": "OFF", "dest": "ON"}]
# ...
self.machine2 = Machine_2(self)
self.machine2.add_transition("Machine1PoweredUp", "OFF", "ON")
# ... 
    def on_enter_ON(self):
        print("machine1 is on!")
        self.parent.machine2.Machine1PoweredUp()

请注意,当您的计算机共享相同的状态和转换时,您可以使用一台计算机和多个模型。

代码语言:javascript
复制
def Model_1():

    def on_enter_ON(self):
        # a dispatched event is forwarded to all models attached to the 
        # machine in question. We consider `parent` to be such a machine here.
        self.parent.dispatch("Machine1PoweredUp")

s1 = Model_1()
s2 = Model_2()
# we set `ignore_invalid_triggers` since `s1` need to process the fired
# event as well even though it is already in state `ON`
m = Machine(model=[s1, s2], ignore_invalid_triggers=True)
s1.parent = m
s1.PowerUp()

您可以将共享机器用作某种“事件总线”,这可以减少耦合和依赖。

轮询

你可以让Machine_2‘尝试’一个带有条件的转换并返回,而不是在你的主循环中阻塞。条件可以是模型方法的名称,也可以是对可调用对象的引用。由于pytransitions总是向模型添加方便的状态检查功能,因此我们可以只将状态检查is_ONmachine1传递到转换定义。当不满足该条件时,该条件将停止并返回,从而启用顺序工作流。

代码语言:javascript
复制
self.machine2.add_transition("PowerUp", "OFF", "ON", conditions=machine1.is_ON)
# ...

while not machine2.is_ON:
  time.sleep(0.1)
  machine2.PowerUp()

但是,您应该注意到,当执行on_enter_<state>回调时,机器/模型已经被认为处于该状态。对于您的特定用例,这意味着machine1.is_ON将返回True,即使Machine_1on_enter_ON回调仍在处理中。

线程化

你也可以在机器2中为机器1的状态检查创建一个线程。为了“解锁”转换,我建议添加一个临时状态,比如Booting,以表明Machine_2还没有准备好。

代码语言:javascript
复制
class Machine_2:

    def check_state(self):
        while self.signal is False:
            time.sleep(0.1)
        self.Machine1Ready()  # transition from 'Booting' to 'ON'

    def on_enter_ON(self):
        print("machine2 on!")

    def on_enter_BOOTING(self):
        print("machine2 waits for machine1 ...")
        threading.Thread(target=self.check_state).start()

transitions提供了一个LockedMachine,以防多个线程需要访问同一台机器。但是,以线程方式访问状态机有一些缺陷,并且在我的经验中调试起来相当困难。

异步

这里有一个简短的示例,说明如何使用AsyncMachine来实现这样的功能。此示例用于说明目的,不应以此方式重现:

代码语言:javascript
复制
from transitions.extensions.asyncio import AsyncMachine
import asyncio


class Model1:

    done = False

    async def on_enter_ON(self):
        await asyncio.sleep(0.1)
        print("Machine 1 done")
        self.done = True


model1 = Model1()


class Model2:

    done = False

    async def on_enter_ON(self):
        while not model1.done:
            print("Machine 1 not ready yet")
            await asyncio.sleep(0.05)
        print("Machine 2 done")


model2 = Model2()

m = AsyncMachine(model=[model1, model2], states=["OFF", "ON"], initial="OFF")
asyncio.get_event_loop().run_until_complete(asyncio.gather(model1.to_ON(), model2.to_ON()))
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66908898

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档