Вложение в Pytransitions

Я просматривал закрытые проблемы на github, SO и искал решение этой проблемы в Google. Но я не смог решить свою проблему, и это, кажется, правильное место. Я уже открыл вопрос на github, но я не уверен, что это было правильно. Я делаю конечный автомат, который может включать в себя несколько подсостояний, которые также являются конечными автоматами. Таким образом, это в основном сводится к повторному использованию HSM в соответствии с readme.

мой SM самого высокого уровня выглядит так:

from transitions.extensions import LockedHierarchicalMachine as Machine
from coordination.running import RunningStateMachine

logging.basicConfig(level=logging.ERROR)
logging.getLogger("transitions").setLevel(logging.INFO)

class RPPStateMachine(Machine):
    def __init__(self, name):
        self._running = RunningStateMachine()
        self.name = name
        states = [
            "init",
            {"name": "running", "children": self._running},
            "stop",
        ]

        Machine.__init__(self, states=states, initial="init")

        self.add_transition("e_run", "init", "run", after=self.run_machine)
        self.add_transition("e_stop", "*", "stop")

    def run_machine(self):
        self._running.initialize()

Как вы видите конечный автомат с тремя состояниями init, running и stop. Как только событие e_run() отправляется через что-то вроде

machine = RPPStateMachine("my_machine")
machine.e_run()

машина переходит в состояние running.

Я делаю это косвенно, потому что хочу, чтобы все происходило автоматически. e_run() вызывает переход к running, а затем run_machine вызывает initialize метод запущенного класса, который запускает событие для запуска цепочки событий. Ниже я показываю running, и это проясняет ситуацию.

Таким образом, рабочее состояние определяется как

from transitions.extensions import LockedHierarchicalMachine as Machine
from coordination.test_mode import TestingStateMachine
from coordination.release_mode import ReleaseStateMachine

class RunningStateMachine(Machine):
    def __init__(self):
        self._test_mode = TestingStateMachine()
        self._release_demo = ReleaseStateMachine()
        states = [
            "init",
            "configuration",
            "idle",
            {"name": "test_mode", "children": self._test_mode},
            {"name": "release_mode", "children": self._release_mode},
        ]

        Machine.__init__(self, states=states, initial="init")
        self.add_transition("e_start_running", "init", "configuration", after=self.configuration)
        self.add_transition("e_success_config", "configuration", "idle")
        self.add_transition("e_test_mode", "idle", "test_mode")
        self.add_transition("e_release_mode", "idle", "release_mode")
        self.add_transition("e_start_running", "idle", "init")

    def initialize(self):
        print("Initialization step for running, emitting e_start.")
        self.e_start_running()

    def configuration(self):
        print("Configuring...")
        print( "Current state: " + self.state)

        self.e_success_config()

который, как и его родитель, состоит из нескольких состояний и нескольких подсостояний. Я также включил ведение журнала, чтобы видеть, в какие состояния я вхожу и выхожу. По моему опыту, вложенные конечные автоматы очень полезны, поскольку вы можете повторно использовать состояния, которые вы написали ранее. Кроме того, по мере роста вашего конечного автомата это помогает сохранять модульность. Таким образом, никакое состояние не становится огромным и трудным для чтения/понимания.

Таким образом, необычное поведение заключается в том, что когда вызывается e_run(), я получаю отпечатки

INFO:transitions.core:Entered state running
INFO:transitions.core:Entered state running_init
Initialization step for running, emitting e_start.
INFO:transitions.core:Exited state init
INFO:transitions.core:Entered state configuration
Configuring...
current state: configuration
INFO:transitions.core:Exited state configuration
INFO:transitions.core:Entered state idle

Как вы видите

machine.state
>>> 'running_init'

пока

machine._running.state
>>> 'idle'

Конечно, я могу переместить определения перехода в родительское состояние, но это неудобно. Я не могу сделать это для всех подсостояний. Очевидно, я хочу, чтобы каждое подсостояние отвечало за свое поведение. Какая здесь распространенная практика? Это ошибка или предполагаемое поведение?

Как я могу аккуратно вложить конечные автоматы друг под друга?


person Keivan    schedule 22.01.2019    source источник


Ответы (1)


Начиная с transitions 0.7.1, передача конечного автомата в качестве дочернего по отношению к другому конечному автомату будет копировать все состояния переданного автомата в родительский. Переданный конечный автомат остается неизменным (как мы обсуждали здесь).

from transitions.extensions import MachineFactory

HSM = MachineFactory.get_predefined(nested=True)

fsm = HSM(states=['A', 'B'], initial='A')
hsm = HSM(states=['1', {'name': '2', 'children': fsm}])

# states object have been copied instead of referenced, they are not identical
assert fsm.states['A'] is not hsm.states['2_A']
hsm.to_2_A()

# both machines work with different models
assert fsm.models[0] is not hsm.models[0]
assert fsm.state is not hsm.state

В настоящее время рекомендуемый рабочий процесс состоит в том, чтобы разделить модели и машины и рассматривать машины только как своего рода «чертеж» для своего родителя:

from transitions.extensions import MachineFactory


class Model:
    pass


HSM = MachineFactory.get_predefined(nested=True)

# creating fsm as a blueprint, it does not need a model
fsm = HSM(model=None, states=['A', 'B'], initial='A')
# use a model AND also
model = Model()
hsm = HSM(model=['self', model], states=['1', {'name': '2', 'children': fsm}])

# will only update the machine's state
hsm.to_1()
assert model.state != hsm.state
# will update ALL model states
hsm.dispatch("to_2_B")
assert model.state == hsm.state

Однако это не заменяет должным образом изолированное (и/или ограниченное) вложение компьютеров в родительские компьютеры. Функция черновик была создана и, надеюсь, будет реализована в обозримом будущем.

person aleneum    schedule 16.09.2019