Let's build a more functional agent, one that uses an actual behavior to perform a task. As we stated earlier, the real programming of the SPADE agents is done mostly in the behaviors. Let's see how.
Let's create a cyclic behaviour that performs a task. In this case, a simple counter. We can modify our existing myagent.py
script:
import spade import time class MyAgent(spade.Agent.Agent): class MyBehav(spade.Behaviour.Behaviour): def onStart(self): print "Starting behaviour . . ." self.counter = 0 def _process(self): print "Counter:", self.counter self.counter = self.counter + 1 time.sleep(1) def _setup(self): print "MyAgent starting . . ." b = self.MyBehav() self.addBehaviour(b, None) if __name__ == "__main__": a = MyAgent("agent@myhost.myprovider.com", "secret") a.start()
As you can see, we have defined a custom behaviour called MyBehav
that inherits from the spade.Behaviour.Behaviour
class, the default class for all behaviours. This class represents a cyclic behaviour with no specific period, that is, a loop-like behaviour.
You can see that there is a method called onStart()
in the behaviour. This method is similar to the _setup()
method of the agent class. It is executed just before the main iteration of the behaviour begins and it is used for initialization code. In this case, we print a line and initialize the variable for the counter.
Also, there is the _process()
method, which is very important. In most behaviours, this is the method in which the core of the programming is done, because this method is called on each iteration of the behaviour loop. It acts as the body of the loop, sort of. In our example, the _process()
method prints the current value of the counter, increases it and then waits for a second (to iterate again).
Now look at the _setup()
method of the agent. There, we make an instance of MyBehav
and add it to the current agent by means of the addBehaviour()
method. The first parameter of this method is the behaviour we want to add, and the second one is the template associated to that behaviour, but we will talk later about templates.
Let's test our new agent:
$ python myagent.py MyAgent starting . . . Starting behaviour . . . Agent: agent@myhost.myprovider.com registered correctly (inform) Counter: 0 Counter: 1 Counter: 2 Counter: 3 Counter: 4 Counter: 5 Counter: 6 Counter: 7
. . . and so on. As we have not set any end condition, this agent would go on counting forever.
The default spade.Behaviour.Behaviour
class is great for cycling tasks that need to be done in a loop fashion. However, in this case we are expecting the behaviour to execute the body of the loop more or less every second. There is a much better way of doing this, and it is by using the spade.Behaviour.PeriodicBehaviour
class:
import spade class MyAgent(spade.Agent.Agent): class MyBehav(spade.Behaviour.PeriodicBehaviour): def onStart(self): print "Starting behaviour . . ." self.counter = 0 def _onTick(self): print "Counter:", self.counter self.counter = self.counter + 1 def _setup(self): print "MyAgent starting . . ." b = self.MyBehav(1) self.addBehaviour(b, None) if __name__ == "__main__": a = MyAgent("agent@myhost.myprovider.com", "secret") a.start()
Note the new parameter we pass on to the MyBehav
object upon instantiation. It's the period of the execution of the task, measured in seconds. Also, note that in this case we do not use the _process()
method (as it is internally used by the behaviour), but instead we use the _onTick()
method that is executed exactly every period.
So what happens if you want a behaviour that doesn't execute neither a cyclic nor a periodic task? What if you want a behaviour that performs a given task once and then finishes? Is it possible? Well, SPADE is here to help you out. Please welcome the spade.Behaviour.OneShotBehaviour
class.
As the name says, OneShot behaviours execute an spontaneous task once and then finish. Let's see this example:
import spade class MyAgent(spade.Agent.Agent): class MyBehav(spade.Behaviour.OneShotBehaviour): def onStart(self): print "Starting behaviour . . ." def _process(self): print "Hello World from a OneShot" def onEnd(self): print "Ending behaviour . . ." def _setup(self): print "MyAgent starting . . ." b = self.MyBehav() self.addBehaviour(b, None) if __name__ == "__main__": a = MyAgent("agent@myhost.myprovider.com", "secret") a.start()
The _process()
method returns, as it is where the main code of this kind of behaviours go. Note that a new method of the behaviours has appeared: onEnd()
. It is very similar to onStart()
and it's there to work as a finalizer method. It is executed once the code on _process()
has ended and just before the behaviour terminates its execution. Let's see the result of this script:
$ python myagent.py Agent: agent@myhost.myprovider.com registered correctly (inform) MyAgent starting . . . Starting behaviour . . . Hello World from a OneShot Ending behaviour . . .
Pretty straightforward, eh? Let's move on to something else.
spade.Behaviour.TimeOutBehaviour
is the class to build behaviours that execute a task once, but only after a specific amount of seconds have passed since the behaviour becomes active (i.e. a timeout is triggered). The code of the task must be placed in a timeOut()
method of the behaviour:
import spade class MyAgent(spade.Agent.Agent): class MyBehav(spade.Behaviour.TimeOutBehaviour): def onStart(self): print "Starting behaviour . . ." def timeOut(self): print "The timeout has ended" def onEnd(self): print "Ending behaviour . . ." def _setup(self): print "MyAgent starting . . ." b = self.MyBehav(5) self.addBehaviour(b, None) if __name__ == "__main__": a = MyAgent("agent@thx1138.dsic.upv.es", "secret") a.start()
Other behaviour types are spade.Behaviour.FSMBehaviour
, which represents a Finite State Machine, and spade.Behaviour.EventBehaviour
, which is a special type of behaviour set to be triggered after an event happens in the agent. These are advanced types of behaviours and are covered in chapter 6.