Skip to content
misza edited this page Feb 11, 2013 · 17 revisions

The following describes steps for building the Convoy example with use of jDEECo.

For convenience the following package containing prepared Eclipse IDE, necessary libraries and Ant scripts for running your application is provided.

Scenario Description:

There are two robots named "Leader" and "Follower". The leader moves along some arbitrary path (composed by the list of integers, which reflect fields on some abstract map, being a grid). The follower remains idle until the leader passes next to it (Leader crosses Follower's current position). When that happens, the follower starts to move following the leader.

Code snippets

  • Launcher:
public class LauncherLocal {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		List<Class<?>> components = Arrays.asList(new Class<?>[] {
				Leader.class, Follower.class });
		List<Class<?>> ensembles = Arrays
				.asList(new Class<?>[] { ConvoyEnsemble.class });
		KnowledgeManager km = new RepositoryKnowledgeManager(
				new LocalKnowledgeRepository()); //new TSKnowledgeRepository()
		Scheduler scheduler = new MultithreadedScheduler();
		AbstractDEECoObjectProvider provider = new ClassDEECoObjectProvider(
				components, ensembles);
		Runtime rt = new Runtime(km, scheduler);
		rt.registerComponentsAndEnsembles(provider);
		rt.startRuntime();
	}

}
  • Leader:
@DEECoComponent
public class Leader extends ComponentKnowledge {
	public Path path;
	
	@DEECoInitialize
	public static Leader initializeLeader() {
		Leader result = new Leader();
		result.id = "Leader";
		Path p = new Path();
		p.remainingPath = new ArrayList<Integer>();
		for (int i = 0; i < 10 ; i++) {
			p.remainingPath.add(i);
		}
		result.path = p;
		return result;
	}
	
	@DEECoProcess
	@DEECoPeriodicScheduling(4000)
	public static void process(
			@DEECoInOut("path.remainingPath") List<Integer> rp,
			@DEECoIn("id") String id
			) {
		if (rp.size() > 1) {
			rp.remove(0);
			System.out.println(id + ": remaining path is " + rp);
		} else {
			System.out.println(id + ": Reached my destination at " + rp.get(0));
		}
	}
}


public class Path extends Knowledge {
	public List<Integer> remainingPath;
	
	public Integer getCurrentPosition() {
		if (remainingPath == null || remainingPath.size() == 0)
			return null;
		return remainingPath.get(0);
	}
}
  • Follower:
@DEECoComponent
public class Follower extends ComponentKnowledge{

	public Integer currentPosition;
	public Integer nextPosition;
	
	@DEECoInitialize
	public static Follower initializeFollower() {
		Follower result = new Follower();
		result.id = "Follower";
		result.currentPosition = new Integer(5);
		result.nextPosition = null;
		return result;
	}
	
	@DEECoProcess
	@DEECoPeriodicScheduling(3000)
	public static void process(
		@DEECoInOut("currentPosition") OutWrapper<Integer> cp,
		@DEECoIn("nextPosition") Integer np,
		@DEECoIn("id") String id
			) {
		if (np != null && !np.equals(cp.item)) {
			cp.item = np;
			System.out.println(id+": I am moving to " + cp.item);
		} else {
			System.out.println(id+": I am waiting at " + cp.item);
		}
	}
}
  • Convoy ensemble:
@DEECoEnsemble
@DEECoPeriodicScheduling(2000)
public class ConvoyEnsemble extends Ensemble {

	@DEECoEnsembleMembership
	public static boolean membership(
			@DEECoIn("member.currentPosition") Integer cp,
			@DEECoIn("member.nextPosition") Integer np,
			@DEECoIn("coord.path") Path path) {
		return ((np != null && !np.equals(path.getCurrentPosition())) 
				|| (np == null && cp.equals(path.getCurrentPosition())));
	}

	@DEECoEnsembleMapper
	public static void map(@DEECoOut("member.nextPosition") OutWrapper<Integer> np,
			@DEECoIn("coord.path") Path path) {
		np.item = path.getCurrentPosition();
	}
}

Launcher

The Launcher class main method instantiates the DEECo runtime, which automatically starts its operation - classes are "interpreted" and appropriate processes are started.

Robot Follower and robot Leader

Basically there are two robot roles "Leader" and "Follower", which have their own Java class definitions. Each of those classes extends the ComponentKnowledge type, which describes knowledge of a particular component (i.e. top-level node in the tree knowledge structure). Additionally they are marked with the @DEECoComponent annotation, allowing the system to properly identify component classes. In this case both components put additional public fields to their definitions, which are initialized in the static (and also public) method tagged with the @DEECoInitialize annotation. This method does not take any parameters and returns an instance of the containing class. It is used to retrieve an initial state of component knowledge, and as it is depicted in the code above all declared fields are assigned some initial values. Apart from the knowledge initializing method, each component is expected to have at least one process method which executes relying on component data. Such method is tagged with @DEECoProcess annotation and can take a random number of properly tagged parameters. Allowed annotations for the process method parameters are: @DEECoIn, @DEECoOut and @DEECoInOut. Parameters marked as an input will be retrieved from the knowledge but never stored back after the execution. Those marked as an output, will be first instantiated (using non-parameterized constructors for the class) and pass to the method before its execution. When the execution completes, they are stored in the knowledge. Parameters, annotated as both input and output, are first retrieved from the knowledge and then (after the method execution) stored back to it. Each of those annotations contains the value attribute, which specifies the parameter name together with the knowledge nesting path. To explain it, let's consider the following parameter: @DEECoInOut("currentPosition") OutWrapper<Integer> cp. For this parameter the value attribute is the"currentPosition", which means that the required (as it is also an input parameter) knowledge value will be searched in the knowledge structure under the node: Follower.currentPosition. The "Follower" at the beginning denotes the particular knowledge id. As it was mentioned before, it is possible to use nesting paths in a parameter annotation attribute. Let's consider the Leader, containing nested knowledge (as it contains a field extending the Knowledge class) and its process, which uses @DEECoInOut("path.remainingPath") List<Integer> rp parameter: Similarly to the previous case the search will be performed by prepending the value attribute with the components id (in this case Leader.path.currentPosition). The last but not least important thing regarding the process method parameters are the outputs. Very often it is necessary to change value of an object, which type is immutable (for example Integer). As such, JDEECo provides the OutWrapper generic class, which allows for modification of immutable types, and storing those changes in the knowledge repository.

Moreover, both component processes and the ConvoyEnsemble have additional annotation, above the process method header, called @DEECoPeriodicScheduling. As the name indicates, this annotation allows for describing process method execution scheme, which in this case is periodic with time interval (milliseconds) set in the annotation attribute.

Convoy ensemble

To let the follower robot to follow the leader we need to establish an ensemble between those robots. In jDEECo ensembles are defined as the first class objects. Similarly to the knowledge component class, it is tagged with an annotation. In this case it is @DEECoEnsemble. It also extends the Ensemble class. The ConvoyEnsemble is executed periodically, which is expressed by As ensembling process is be either triggered and periodic, the proper scheduling describing annotation can be used to denote an ensemble execution scheme. In the ensemble definition there are two important methods: an ensemble membership checking function and mapping function. To properly identify them, annotations (@DEECoEnsembleMembership and @DEECoEnsembleMapper respectively) are used, before the method headers. In both cases, the methods can accept a random number of parameters, but in case of the membership checking function those parameters can be of input type only. Additionally, method tagged with @DEECoEnsembleMembership should return boolean value indicating if the ensemble can be established. As can be seen from the code above, method parameters are also tagged. The rules from the process method parameters apply here as well and the only difference is the additional distinguishment between coordinator and member parameters. To find out more details about what it means, please refer to the DEECo component model description (Ensemble describing part).


An important thing to mention is that, the parameters for both process methods and ensemble methods are retrieved using duck typing. As such it is possible to define your own structures (extending the Knowledge class), with fields which are actually needed for those method execution.

Clone this wiki locally