0

I'm trying to use pytransitions to implement retransmit logic from an initialization state. The summary is that during the init state if the other party isn't responding after 1 second resend the packet. This is very similar to what I see here: https://github.com/pytransitions/transitions/pull/461

I tried this patch, and even though I see the timeouts/failures happening, my callback is only called the first time. This is true with before/after and on_enter/exit. No matter what I've tried, I can't get the retransmit to occur again. Any ideas?

1 Answers1

0

Even though this question is a bit dated I'd like to post an answer since Retry states have been added to transitions in release 0.9. Retry itself will only count how often a state has been re-entered meaning that the counter will increase when transition source and destination are equal and reset otherwise. It's entirely passive and need another mean to trigger events. The Timeout state extension is commonly used in addition to Retry to achieve this. In the example below a state machine is decorated with Retry and Timeout state extensions which allows to use a couple of keywords for state definitions:

  • timeout - time in seconds before a timeout is triggered after a state has been entered
  • on_timeout- the callback(s) called when timeout was triggered
  • retries - the number of retries before failure callbacks are called when a state is re-entered
  • on_failure - the callback(s) called when the re-entrance counter reaches retries

The example will re-enter pinging unless a randomly generated number between 0 and 1 is larger than 0.8. This can be interpreted as a server that roughly answers only every fifth request. When you execute the example the retries required to reach 'initialized' can vary or even fail when retries are reached.

from transitions import Machine
from transitions.extensions.states import add_state_features, Retry, Timeout

import random
import time

# create a custom machine with state extension features and also 
# add enter callbacks for the states 'pinging', 'initialized' and 'init_failed'
@add_state_features(Retry, Timeout)
class RetryMachine(Machine):
    def on_enter_pinging(self):
        print("pinging server...")
        if random.random() > 0.8:
            self.to_initialized()

    def on_enter_initialized(self):
        print("server answered")

    def on_enter_init_failed(self):
        print("server did not answer!")


states = ["init",
          {"name": "pinging",
           "timeout": 0.5,  # after 0.5s we assume the "server" wont answer
           "on_timeout": "to_pinging",  # when timeout enter 'pinging' again
           "retries": 3,  # three pinging attempts will be conducted
           "on_failure": "to_init_failed"},
          "initialized",
          "init_failed"]

# we don't pass a model to the machine which will result in the machine 
# itself acting as a model; if we add another model, the 'on_enter_<state>'
# methods must be defined on the model and not machine
m = RetryMachine(states=states, initial="init")
assert m.is_init()
m.to_pinging()
while m.is_pinging():
    time.sleep(0.2)
aleneum
  • 2,083
  • 12
  • 29