Search code examples
pythonfsm

TypeError - unbound method ToTransition() must be called with FSM instance as first argument (got str instance instead)


I have a Finite State Machine that should randomly select a state to enter however I am getting the following Type Error;

unbound method ToTransition() must be called with FSM instance as first argument (got str instance instead)

Im unsure as to why this is happening. When initiated it can call the first state but then fails. Any advise would be appreciated.

from random import randint
from time import clock

##========================================
##TRANSITIONS

class Transition(object):
def __init__(self, toState):
    self.toState = toState

def Execute(self):
    print ("Transitioning...")

##=======================================
##STATES

class State(object):
    def __init__(self, FMS):
        self.FSM = FSM
        self.timer = 0
        self.startTime = 0

    def Enter(self):
        self.timer = randint(0,5)
        self.startTime = int(clock())

    def Execute(self):
        pass

    def Exit(self):
        pass

class CleanDishes(State):
    def __init__(self, FSM):
        super(CleanDishes, self).__init__(FSM)

    def Enter(self):
        print ("Preparing to clean dishes")
        super(CleanDishes, self).Enter()

    def Execute(self):
        print ("Cleaning Dishes.")
        if(self.startTime + self.timer <= clock()):
            if not(randint(1,3) %2):
                self.FSM.ToTransition("toVacuum")
            else:
                self.FSM.ToTransition("toSleep")

    def Exit(self):
        print ("Finished the Dishes.")

class Vacuum(State):
    def __init__(self, FSM):
        super(Vacuum, self).__init__(FSM)

    def Enter(self):
        print ("Preparing to Vacumm")
        super(Vacuum, self).Enter()

    def Execute(self):
        print ("Vacuuming.")
        if(self.startTime + self.timer <= clock()):
            if not(randint(1, 3) %2):
                self.FSM.ToTransition('toSleep')
            else:
                self.FSM.ToTransition('toCleanDishes')

    def Exit(self):
        print ("Finished Vacuuming.")

class Sleep(State):
    def __init__(self, FSM):
        super(Sleep, self).__init__(FSM)

    def Enter(self):
        print ("falling asleep")
        super(Sleep, self).Enter()

    def Execute(self):
        print ("Sleeping.")
        if(self.startTime + self.timer <= clock()):
            if not(randint(1,3) %2):
                self.FSM.ToTransition("toVacuum")
            else:
                self.FSM.ToTransition("toCleanDishes")

    def Exit(self):
        print ("Waking Up.")

##======================================
##FSM

class FSM(object):
    def __init__(self, character):
        self.char = character
        self.states = {}
        self.transitions = {}
        self.curState = None
        self.prevState = None
        self.trans = None

    def AddTransition(self, transName, transition):
        self.transitions[transName] = transition

    def AddState(self, stateName, state):
        self.states[stateName] = state

    def SetState(self, stateName):
        self.prevState = self.curState
        self.curState = self.states[stateName]

    def ToTransition(self, toTrans):
        self.trans = self.transitions[toTrans]

    def Execute(self):
        if(self.trans):
            self.curState.Exit()
            self.trans.Execute()
            self.SetState(self.trans.toState)
            self.curState.Enter()
            self.trans = None
        self.curState.Execute()

##===================================
##IMPLEMENTATION

Char = type("Char", (object,), {})

class RobotMaid(Char):
    def __init__(self):
        self.FSM = FSM(self)

        ##STATES
        self.FSM.AddState("Sleep", Sleep(self.FSM))
        self.FSM.AddState("CleanDishes", CleanDishes(self.FSM))
        self.FSM.AddState("Vacuum", Vacuum(self.FSM))

        ##TRANSISITONS
        self.FSM.AddTransition("toSleep", Transition(Sleep))
        self.FSM.AddTransition("toVacuum", Transition(Vacuum))
        self.FSM.AddTransition("toCleanDishes", Transition(CleanDishes))

        self.FSM.SetState("Vacuum")

    def Execute(self):
        self.FSM.Execute()

if __name__ == '__main__':
    r = RobotMaid()
    for i in range(20):
        startTime = clock()
        timeInterval = 1
        while(startTime + timeInterval > clock()):
            pass
        r.Execute()

Solution

  • Here:

    class State(object):
        def __init__(self, FMS):
            self.FSM = FSM
    

    The param name is "FMS" but you set self.FSM to FSM, which is then resolved as the (global) FSM class.

    This wouldn't have happened if you followed Python naming conventions (classes names in CamelCase, variables and method names in all_lower) or at least some consistant naming convention where you don't use the same exact name for a class and an instance...