Skip to content

Create a new Behaviour

This tutorial explains how to create a new behaviour.

The behaviours are the basis of the Skydata. An agent is composed of many behaviours. Each behaviour has a specific goal, for example:

  • Regularly migrate to a new Harbour
  • Manage the replication process of agents
  • Maintain a replication goal
  • And so on

Different types of behaviours

We can classify these behaviours into different types depending on the way they are called.

This is a list of the different types:

  • TickerBehaviour: called every X milliseconds
  • WakerBehaviour: called once after X milliseconds
  • CyclicBehaviour: called each time a message is received
  • SKAgentBehaviour: a generic class to compose multiple behaviours

The three first types come directly from the Jade Framework, so we will not explain them.

This tutorial will show how interfacing these behaviours.

Internal update

In order to react to internal special event, a mechanism of callback is implemented in the prototype.

The idea is simple:

  1. The agents define the name of events (strings)
  2. The behaviours add callbacks to some events (by using the function addInternalUpdate)
  3. When the event occurs, the agents execute all callbacks listening on this event

Some events can execute callbacks with parameters (for example the event FAMILY_UPDATE).

When you write a SKAgentBehaviour, take in consideration that: - If there is no parameter, the method action will be called - If there is a parameter, the method actionWithParameters will be called

Currently, the list of the events is:

  • AFTER_MIGRATION: throw when the migration is finished
  • AFTER_REPLICATION: throw when the replication is finished
  • WANT_MIGRATE: throw when an agent wants to migrate (pass the destination as a parameter)
  • DELETION: throw when an agent wants to delete itself
  • FAMILY_UPDATE: throw when we update the position of a family member (pass its identity as parameter)
  • FAMILY_ADDED: throw when we add a family member
  • FAMILY_REMOVED: throw when we remove a family member

Implement a behaviour

In order to create a behaviour for an agent, we need to inherit the SKAgentBehaviour class.

Suppose we want to implement a behaviour that forces an SKD to migrate every time the family size changes or when the position of a family member is updated. In addition, we want to migrate every 30 seconds.

To do that, we need to know when the family size changed so we need to add a callback to FAMILY_ADDED and to FAMILY_REMOVED. To initiate the migration, we will use the function migrate (See here).

Let's create the behaviour in the class MigrateFamilyChanged:

public class MigrateFamilyChanged extends SKAgentBehaviour {

    /**
     * Create the behaviour and add it to the agent
     */
    public MigrateFamilyChanged(SKAgent agent) {
        super(agent);
    }

     /**
     * Get a random harbour for next migration
     * 
     * @return the identity of the next harbour
     */
    public SKAID getRandom() {
        return ...;
    }

    @Override
    public void action() {
        SKD agent = (SKD) this.agent;

        // Migrate every 30 seconds
        TickerBehaviour tickerBehaviour = new TickerBehaviour(agent, 30000) {
            @Override
            protected void onTick() {
                SKAID harbour = getRandom();
                agent.migrate(harbour.toAID());
            }
        };
        agent.addBehaviour(tickerBehaviour);

        // When the family is updated
        SKAgentBehaviour familyUpdated = new SKAgentBehaviour(agent) {
            // Without parameters
            public void action() {
                print("My family size changed, I migrate !");
                SKAID harbour = getRandom();
                agent.migrate(harbour.toAID());
            }

            // With parameters
            public void actionWithParameters(Object o) {
                SKAID member = (SKAID)o;
                print(member.getName() + " moved, I migrate ! ");
                SKAID harbour = getRandom();
                agent.migrate(harbour.toAID());
            }
        };

        // Add to callback manager
        agent.addInternalUpdate('FAMILY_ADDED', familyUpdated);
        agent.addInternalUpdate('FAMILY_REMOVED', familyUpdated);
        agent.addInternalUpdate('FAMILY_UPDATE', familyUpdated);

    }

}

Customizable behaviours

Generally, we would like to create a behaviour that will be customizable by adding some parameters to the behaviours. In the previous source code, it would be the delay between two migrations.

To do that, we can add arguments in the harbour description files (see here).

Then, to use it, we can use the array args in the SKAgent.

Let's modify the previous source code and assume the argument name is delay_migration.

The source code becomes:

public class MigrateFamilyChanged extends SKAgentBehaviour {
    ...

    @Override
    public void action() {
        SKD agent = (SKD) this.agent;

        int delay = (Integer)(agent.args.getOrDefault("delay_migration", 30000));

        // Migrate every 30 seconds
        TickerBehaviour tickerBehaviour = new TickerBehaviour(agent, 30000) {
            @Override
            protected void onTick() {
                SKAID harbour = getRandom();
                agent.migrate(harbour.toAID());
            }
        };
        agent.addBehaviour(tickerBehaviour);

        ...

    }

}

Some useful functions to code a new agent

This section lists useful functions for a new behaviour and references their documentation links:

To send or receive messages:

  • skSendNormal: send a message to another agent without using the reliable sending protocol. (See here)
  • skSendReliable: send a message to another agent using the reliable sending protocol. (See here)
  • broadcast: broadcast a message to a list of agents (you can specify if you want to use reliable sending protocol) (See here)
  • skReceive: receive a message matching a message template (See here)

To store permanently an object in the memory of migration (even after a replication):

  • createStorageObject: take a key and an object and store this object referenced by the key (See here)
  • getStorageObject: get an object referenced by a specific key (See here)
  • hasStorageObject: verify if an object is stored (See here)

Manage the migration:

  • afterMove: called just after a migration (See here)
  • doMove: called during the migration process (See here)
  • beforeMove: called just before the migration process (See here)
  • blockMigrate: block the migration temporarily (See here)
  • unblockMigrate: unblock the migration (See here)
  • canMigrate: verify if the agent can migrate (See here)
  • migrate: initiate a migration process on a specific Harbour (See here)

Manage the replication:

  • afterClone: called just after a replication (See here)
  • doClone: called during the replication process (See here)
  • beforeClone: called just before the replication process
  • blockReplicate: block the replication temporarily (See here)
  • unblockReplicate: unblock the replication (See here)
  • canReplicate: verify if the agent can replicate (See here)
  • clone: initiate a replication process on a specific Harbour (See here)

All function documentation is available here.