Я просматривал закрытые проблемы на 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'
Конечно, я могу переместить определения перехода в родительское состояние, но это неудобно. Я не могу сделать это для всех подсостояний. Очевидно, я хочу, чтобы каждое подсостояние отвечало за свое поведение. Какая здесь распространенная практика? Это ошибка или предполагаемое поведение?
Как я могу аккуратно вложить конечные автоматы друг под друга?