pytransitions / transitions

A lightweight, object-oriented finite state machine implementation in Python with many extensions
MIT License
5.5k stars 525 forks source link

Difficulties setting initial state on HSM #460

Closed rgov closed 3 years ago

rgov commented 3 years ago

Consider the following example:

    states = [
        { 'name': 'On',
          'initial': 'WaitForPrompt', 'children': [
            'WaitForPrompt',
            'Configure',
            'StartLogging',
            'Logging',
        ] },
        { 'name': 'Off',
          'initial': 'StopLogging', 'children': [
            'StopLogging',
            'Standby',
        ] },
        'CriticalFailure',
    ]

    machine = HierarchicalMachine(states=states)

I'd like to start this machine in the Off_Standby state.

If I pass initial='Off_Standby' to the machine constructor, it complains that the Standby state already exists. (I can set the initial state to simply Off through the constructor.)

If I do not pass an initial state, but then call machine.to_Off_Standby, this works only if I did not provide a model to the machine; if there is a model, it throws AttributeError: 'to_Off_Standby' does not exist on <Machine@4484860944>.

aleneum commented 3 years ago

Hello @rgov,

If I pass initial='Off_Standby' to the machine constructor, it complains that the Standby state already exists.

This is a limitation mentioned in the Readme:

Which substate to enter is defined by initial which should always point to a direct substate.

Your second issue:

If I do not pass an initial state, but then call machine.to_Off_Standby, this works only if I did not provide a model to the machine; if there is a model, it throws AttributeError: 'to_Off_Standby'

Convenience functions such as the auto functions to_<state> are always added to the model. If you add a model like Machine(model=model, ...) you need to call model.to_Off_Standby() instead of machine.to_<state>:

Notice the shiny new methods attached to the Matter instance (evaporate(), ionize(), etc.). Each method triggers the corresponding transition. You don't have to explicitly define these methods anywhere; the name of each transition is bound to the model passed to the Machine initializer...

rgov commented 3 years ago

Convenience functions such as the auto functions to_<state> are always added to the model.

This was my expectation but it caused an exception. Does it not reproduce for you?

aleneum commented 3 years ago

This works as expected:

from transitions.extensions.nesting import HierarchicalMachine

states = [
        { 'name': 'On',
          'initial': 'WaitForPrompt', 'children': [
            'WaitForPrompt',
            'Configure',
            'StartLogging',
            'Logging',
        ] },
        { 'name': 'Off',
          'initial': 'StopLogging', 'children': [
            'StopLogging',
            'Standby',
        ] },
        'CriticalFailure',
    ]

class Model:
    pass

model = Model()
machine = HierarchicalMachine(model=model, states=states, initial="Off")
print(model.state)  # >>> Off_StopLogging
model.to_Off_Standby()
print(model.state)  # >>> Off_Standby
aleneum commented 3 years ago

Closing this since there hasn't been feedback for quite a while. Being able to initialize a HSM in a nested state is on the list but I cannot make reliable assumptions when it will implemented. The next release -- which is due -- will definitely not contain that feature though. Feel free to comment if there is more to add. I will reopen the issue if necessary.