Module
From Agent Factory
Contents |
Editorial History
23/12/08 (Version 1): Initial version of page.
28/12/08 (Version 2): Added stuff on support methods, perceptors, and put a place holder for info on configurations.
Overview
A module is a private resource that can be associated with individual agents. It is private because it can only ever be accessed by a single agent - the agent in which it was declared. Modules are typically used to provide any additional data structures that may be necessary to support the activities of that agent. This may be because:
- the agent requires a more complex representation of some aspect of its environment that is not easily implementable through Beliefs and Belief Rules; or
- the agent must interface with some external application an the module is used to support interaction with that interface.
Support Methods
There are only a couple of support methods for Modules:
- getName(): returns the name of the module
- getConfiguration(): returns the configuration of the module
Writing a Module
Modules are implemented by extending the com.agentfactory.logic.agent.Module abstract base class, and where appropriate, overriding the init() method to include module specific initialisation code. For example, lets look at a simple module that implements a Stack data structure:
package module;
import com.agentfactory.logic.agent.Module;
import java.util.LinkedList;
public class Stack extends Module {
private LinkedList stack;
@Override
public void init() {
stack = new LinkedList();
}
public void push(Object item) {
stack.addFirst(item);
}
public Object peek() {
return stack.getFirst();
}
public Object pop() {
return stack.removeFirst();
}
public boolean isEmpty() {
return stack.isEmpty();
}
public int size() {
return stack.size();
}
}
As can be seen above, this implementation is fairly standard, and does not involve much agent specific code (beyond the required init() method).
Declaring a Module in AFAPL2 Code
In AFAPL2 modules are declared via the LOAD_MODULE construct:
LOAD_MODULE <id> <class>;
Each <id> must be unique within the scope of the overall agent program (i.e. the agent program and any imported agent programs), and is associated with a single instance of the <class> when the agent is created. Each agent that is an instance of the agent program holds a reference to its own instance of the <class>, and can access that instance by using either the <id> or the class name. For example, the following agent program creates a single instance of the Stack module (as is declared above):
IMPORT com.agentfactory.afapl2.core.agent.FIPACore; LOAD_MODULE myStack module.Stack;
In fact, so long as the identifiers are different, we can use multiple instances of the same module in an agent program. For example, the following agent program creates two instances of the Stack module, one of which can be referenced via the identifier myStack and the other via the identifier myOtherStack:
IMPORT com.agentfactory.afapl2.core.agent.FIPACore; LOAD_MODULE myStack module.Stack; LOAD_MODULE myOtherStack module.Stack;
Accessing a Module from an Actuator
Actuator units implement the basic Actions of an agent. As was discussed in the Actuator page, the com.agentfactory.logic.agent.Actuator abstract base class, which is used to implement all Actuators, provides three methods to support accessing of Modules:
- getModuleByName(String name): Returns a reference to the Module of the given name.
- getModuleByClass(String className): Returns a reference to the first Module found with the given class name.
- getModulesByClass(String className):Returns an array of references to the all the Modules with the given class name.
For the first of these methods, the parameter refers to the unique identifier that is associated with the Module when it is declared, and the latter two methods support the access of Modules based on their class.
For example, the Actuator code illustrates how you would create an Actuator that supports pushing of information onto the example Stack module discussed above:
package actuator;
import com.agentfactory.logic.agent.Actuator;
import com.agentfactory.logic.lang.FOS;
import module.Stack;
public class HelloWorld extends Actuator {
public boolean act(FOS action) {
String item = action.argAt(0).toString();
Stack stack = (Stack) getModuleByName("myStack");
stack.push(item);
return true;
}
}
Accessing Modules using Configurations
As can be seen, this actuator implementation assumes the existence of a module entitled myStack, which is a little limiting. A better solution is to make use of the configuration feature of Actuators:
package actuator;
import com.agentfactory.logic.agent.Actuator;
import com.agentfactory.logic.lang.FOS;
import module.Stack;
public class Push extends Actuator {
public boolean act(FOS action) {
String item = action.argAt(0).toString();
Stack stack = (Stack) getModuleByName(getConfiguration().get("module").toString());
stack.push(item);
return true;
}
}
To link this Actuator to an Action, you would use something like this:
IMPORT com.agentfactory.afapl2.core.agent.FIPACore;
LOAD_MODULE myStack module.Stack;
ACTION push(?item) {
PRECONDITION BELIEF(true);
POSTCONDITION BELIEF(true);
CLASS actuator.Push {
module=myStack;
}
}
Passing Module Identifiers as Action Parameters
One limitation of the above solution is that you must statically define which stack your action is to manipulate. In some cases this is not the best solution (if you want to manipulate many different types of stack), so an alternative is to pass the module name in as a parameter, for example:
IMPORT com.agentfactory.afapl2.core.agent.FIPACore;
LOAD_MODULE myStack module.Stack;
ACTION push(?item, ?stack) {
PRECONDITION BELIEF(true);
POSTCONDITION BELIEF(true);
CLASS actuator.Push;
}
COMMIT(?self, ?now, BELIEF(true), push(21, myStack));
The corresponding Actuator code would then need to be modified as follows:
package actuator;
import com.agentfactory.logic.agent.Actuator;
import com.agentfactory.logic.lang.FOS;
import module.Stack;
public class Push extends Actuator {
public boolean act(FOS action) {
String item = action.argAt(0).toString();
String stackId = action.argAt(1).toString();
Stack stack = (Stack) getModuleByName(stackId);
stack.push(item);
return true;
}
}
Accessing a Module from a Perceptor
All but the last of the techniques outlined above for Actuators can also be used with Perceptors. The last technique does not work because Perceptors are not called explicitly by the agent, instead they are invoked implicitly at the start of each execution cycle.
An obvious use of a Perceptor is to generate beliefs about the state of any stack modules that are currently associated with the agent. The example below creates Beliefs of the form: size(?size, ?stackId) and top(?item, ?stackId):
package perceptor;
import com.agentfactory.logic.agent.Perceptor;
import module.Stack;
public class StackState extends Perceptor {
public void perceive() {
Stack[] stacks = (Stack[]) getModulesByClass(Stack.class);
for (Stack stack: stacks) {
adoptBelief("BELIEF(size(" + stack.size() + "," + stack.getName() + "))");
adoptBelief("BELIEF(top(" + stack.top() + "," + stack.getName() + "))");
}
}
}
Using Configurations with Modules
T.B.C.
