Creating entities
From RifidiWiki
Introduction
Everything in Designer is an entity, be it an ID generator or a conveyorbelt. The whole classgraph for doing entities in Designer is centered around org.rifidi.designer.entities.Entity. Within the next chapters I will try to explain how to create an entity and explain the different interfaces that are available. But first I want to explain the different classes and their purposes.
Dependency Injection
Designer is a very complex application and it might be hard to find things.
That's why we decided to use DI to give all entities the things they need.
Right now our dependency injection is limited to services but that should be enough to satisfy all your needs.
@Inject annotation
Your entity is already prepared for dependency injection as all your entities need to extend org.rifidi.designer.entities.Entity or org.rifidi.designer.entities.VisualEntity.
The only thing you need to do is to add the @Inject annotation to the setter for the service you need:
@Inject
public void setFieldService(FieldService fieldService) {
this.fieldService = fieldService;
}
After you entity is created by designer the first thing that happens is that all @Inject annotations are resolved.
Multithreading
Eclipse plus a RenderThread plus an UpdateThread plus other Threads normally means a lot trouble and we already went through all that trouble.
You should never ever under any circumstances do calls directly into eclipse out of an entity. That will almost always result in an IllegalThreadAccess exception.
The other thing is that doing calls that manipulate the scene graph can only be done in certain places (the init() method being one of them).
If you really need to do something outside of these methods or there is no service (see Services API in the main documentation) that can help you will have to use GameTaskQueueManager.getManager().update(Callable) or GameTaskQueueManager.getManager().render(Callable).
If you have to submit a callable please remember that NONE of the methods provided by our interfaces are allowed to block. If you have to wait on a Future for some reasons you will have to do it in a seperate thread.
org.rifidi.designer.entities.Entity
javadoc
As mentioned before this is the base class for all entities.
It implements the IAdaptable interface from eclipse to enable representation of the entity inside an eclipse viewer.
Entity has only 3 properties:
- name: a human readable name for the entity (The name should have a default value, so do a setName("fancydefaultname") in your constructor)
- entityId: id used internally by designer, you are NEVEREVER supposed to change this by hand, getter and setter are only there for the purpose of persistance
- userData: a user defined field for holding data (e.g. a RifidiTag)
The important part of Entity is the lifecycle:
- the EMPTY constructor is used to create a new instance of the object
- the bean properties are set by JAXB
- the init()/loaded() method is the final step in every initialization (depending if the entity was loaded from a file or just created)
- it lives a happy life
- the destroy() method is hit to get rid of it
How to use it
Entity is an abstract class so it has to extended.
You need to implement:
init()
This method initializes the object. Be aware that this method gets hit after creation only.
The most important part of this method is that it is called inside the OpenGL thread so all scenegraph related operations for preparing the object should be done here.
loaded()
This method initializes the object after it is loaded back from XML.
This method is called inside the OpenGL thread.
destroy()
If you are running threads, opening network connections or anything else that has to be cleaned up when the object is being DELETED from the simulation this is the method to do it. In here you should also clean up the physics space (if you used it).
persistence
We are using JAXB to persist everything to a XML file.
There are basically only two annotations that are of interest for someone who implements a new entity:
- @XmlTransient: This annotations prevents the annotated property from being persisted.
- @XmlID: This one is only listed because you are NOT supposed to use it. Entity already has an XmlID annotation.
org.rifidi.designer.entities.annotations.Property
javadoc
To actually display properties we use the org.rifidi.designer.entities.annotations.Property annotation.
We annotate the setter method for properties.
/**
* @param name the name to set
*/
@Property(displayName="Name",description="guess what",readonly=true,unit="")
public void setName(String name) {
this.name = name;
}
The displayName is the name that is used in the properties view, the description is used for the tooltip, unit is used to show something like feet/s after the value and readonly tells the properties view that you are not allowed to change the value from there. [link title]
org.rifidi.designer.entities.VisualEntity
javadoc
VisualEntity extends Entity and is used for entitys that are displayed in the simulation.
It adds two more things:
- node: this property is the JME Node this VisualEntity owns.
- pattern: the layout of the VisualEntity.
- collides: If set to false the user will be able to place the entity inside/above/.. another entity.
node
When a new VisualEntity is created it's the obligation of the developer to create a new node. The best place to do this is the init() method.
/* (non-Javadoc)
* @see org.rifidi.designer.entities.Entity#init()
*/
@Override
public void init() {
Node node=new Node();
try {
URI modelpath = null;
try {
modelpath = getClass().getClassLoader().getResource("org/rifidi/designer/library/basemodels/cardbox/cardboardbox.jme").toURI();
} catch (URISyntaxException e) {
e.printStackTrace();
}
Node loaded=(Node)BinaryImporter.getInstance().load(modelpath.toURL());
node.attachChild(loaded);
node.updateModelBound();
setNode(node);
} catch (IOException e) {
logger.error("Unable to load jme: "+e);
}
}
This piece of code shows various things:
- the jme files are stored in the classpath (more on that in the deployment chapter)
- the node is ALWAYS added to the scene graph outside of the VisualEntity. The developer only has to create the node, Designer will take care of the rest.
- the entityID is set after the entity has been created, the node will also get this id as its name
pattern
pattern is an instance of BinaryPattern javadoc. A BinaryPattern is used for placement.
The sceneData holds a BitMap that contains a bit for each rectangle of the scene map.
To check if an entity can be placed on a certain spot it has to provide a BinaryPattern that is checked against the BitMap
Let's look at a 2x3 foot conveyor.
BinaryPattern pattern = new BinaryPattern();
pattern.setPattern(new boolean[][] { { true, true },
{ true, true}
{ true, true}
});
The conveyor is supposed to cover the whole are beneath it, so all bits are set to true. If you want to have a gate that should be able to be positioned over this conveyor it would have to look like this:
BinaryPattern pattern = new BinaryPattern();
pattern.setPattern(new boolean[][] { { true, false, false, true }});
important interfaces
The preceding chapters dealt with the two important baseclasses.
But every entity has different requirements. These requirements are covered by several interfaces.
org.rifidi.designer.entities.interfaces.ChildEntity & org.rifidi.designer.entities.interfaces.ParentEntity
ChildEntity
ParentEntity
This set of interfaces is used to describe a parent child relation.
If an entity has a strong relation to another entity (e.g. the antennas are mounted on a gate and cannot exist without the gate).
A strong relation is if an entity creates another entity.
org.rifidi.designer.entities.interfaces.Directional
Directional
This is a temporary interface and is here until we have found a better solution.
It is used as a marker for objects that can reverse direction.
The problem with this interface is that developers have to provide there own way to show the direction of an entity in the 3D view.
We need a more general approach for that.
org.rifidi.designer.entities.interfaces.Field
Field
Used to describe a field.
A field is automatically registered to the FieldService and its geometry is checked for collisions.
org.rifidi.designer.entities.interfaces.GPI & org.rifidi.designer.entities.interfaces.GPO
GPI
GPO
This pair of interfaces is used to describe GPIO entities.
A GPI entity is a target for GPO events.
org.rifidi.designer.entities.interfaces.IDGenerator
IDGenerator
IDGenerators are used by Producers to create entities with a unique id.
An IDGenerator must always return a new, unique ID.
org.rifidi.designer.entities.interfaces.InternalEntity
InternalEntity
This is a marker interface and tells designer that the entity is to be treated as an internal entity. Internal entites don't show up in the library view. They are also initialized in a different way (the initialization will be delegated to the registering plugin).
org.rifidi.designer.entities.interfaces.NeedsPhysics
NeedsPhysics
javadoc
Use this if your object needs access to the physical world (like checking for collisions or to create new physical objects).
org.rifidi.designer.entities.interfaces.Producer
Producer
This interface is used for Entities that create other Entites (normally VisualEntites).
org.rifidi.designer.entities.interfaces.RifidiEntity
RifidiEntity
Entities implementing this interface interact with RifidiEmulator.
org.rifidi.designer.entities.interfaces.SceneControl
SceneControl
Every entity that has its own threads or does timebased changes in the simulation needs to implement this interface. This interface allows Designer to pause/stop/start the simulation.
Note: An entity that implements this interface has to be able to resume at the position where it was when it got paused.
org.rifidi.designer.entities.interfaces.Switch
Switch
Switch is used to allow the user to turn an entity on and off. It could be used to turn certain machinerie on and off.
org.rifidi.designer.entities.interfaces.Trigger
Trigger
Not yet finalized: This interface will be used by entities to trigger other entites (or other objects that are not part of the simulation).
org.rifidi.designer.entities.interfaces.VisualEntityHolder
VisualEntityHolder
An entity implementing this interface has a weak relation to some visual entites. A clothing rack has a weak connection to the clothing on it. The clothing can be removed at any time.
packaging everything up
In Designer everything has to be organized into libraries.
An object in the library should conform to the following layout:
some.package.myentity+ entityclass entityWizard entityWizardPage entityWorkbenchAdapter entity.jme
entityclass
Your extension of either Entity or VisualEntity.
entityWizard
A wizard should only be provided for entities that really need it.
For all other entites provide reasonable default values and let the user edit the properties in the properties view.
A class that extends org.eclipse.jface.wizard.Wizard and implements org.rifidi.designer.library.EntityWizardIface.
This wizard is created by Designer when a user drags an entity from the library view into the 3d editor,
entityWizardPage
You need at least one org.eclipse.jface.wizard.WizardPage extension for the Wizard. Feel free to provide as many as you need.
entityWorkbenchAdapter
In the first version of designer all entities implemented IAdaptable.
As we don't want to pollute our objects with too much eclipse specific things we now use the AdapterFactory extension point.
As your library is already an eclipse/osgi plugin it is not too difficult to add the AdapterFactory extension point and use it to provide the appropriate adapters.
The gedAdapter() method of your factory should contain something like:
if (adaptableObject instanceof BoxproducerEntity) {
if (IWorkbenchAdapter.class.equals(adapterType)) {
return new BoxproducerEntityWorkbenchAdapter();
}
if (IActionFilter.class.equals(adapterType)) {
return new SwitchActionFilterAdapter();
}
if (IPropertySource.class.equals(adapterType)) {
return new DefaultPropertySource((Entity) adaptableObject);
}
} else if (adaptableObject instanceof CardboxEntity) {
if (IWorkbenchAdapter.class.equals(adapterType)) {
return new CardboxEntityWorkbenchAdapter();
}
if (IActionFilter.class.equals(adapterType)) {
return new DefaultEntityFilterAdapter();
}
if (IPropertySource.class.equals(adapterType)) {
return new DefaultPropertySource((Entity) adaptableObject);
}
}
First of all:
The factory has to provide an implementation of IWorkbenchAdapter for each entity. This makes sure the entity appears in the entit view.
Each entity should also provide an IPropertySource to display properties in the PropertiesView (if you use the annotation facillity provided by us you can use org.rifidi.designer.entities.adapters.DefaultPropertySource).
Designer also provides two action filters:
- org.rifidi.designer.entities.adapters.SwitchActionFilterAdapter: This one is used to display the start/stop context menu actions in the entity view and to allow the entity to be grouped.
- org.rifidi.designer.entities.adapters.DefaultEntityFilterAdapter: This one is used to allow the entity to be grouped.
entity.jme
This is the jme-model file. Please use shared meshes as often as possible.
putting it together
Libraries are basically Eclipse plugins:
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<plugin>
<extension
point="org.rifidi.designer.library">
<EntitiyLibrary class="org.rifidi.designer.library.basemodels.BasemodelsLibrary"/>
</extension>
</plugin>
This is an example plugin.xml. It shows the extension point that the plugin has to use org.rifidi.designer.library.
The extension point points to an implementation of org.rifidi.designer.library.EntityLibrary.
public class BasemodelsLibrary implements EntityLibrary {
private List<EntityLibraryReference> library;
public BasemodelsLibrary(){
this.library=new ArrayList<EntityLibraryReference>();
EntityLibraryReference conveyorRef=new EntityLibraryReference();
conveyorRef.setId(ConveyorEntity.class.getName());
conveyorRef.setImageDescriptor(null);
conveyorRef.setLibrary(BasemodelsLibrary.class);
conveyorRef.setName("Conveyor");
conveyorRef.setHidden(false);
conveyorRef.setWizard(null);
conveyorRef.setEntityClass(ConveyorEntity.class);
library.add(conveyorRef);
}
/* (non-Javadoc)
* @see org.rifidi.designer.library.EntityLibrary#getLibraryReferences()
*/
public List<EntityLibraryReference> getLibraryReferences() {
return library;
}
/* (non-Javadoc)
* @see org.rifidi.designer.library.EntityLibrary#getName()
*/
public String getName() {
return "RiFiDi Base Models";
}
}
This example shows what needs to be done. The important part is to create the EntityLibraryReferences. Those give Designer all information it needs to instantiate the appropriate wizard and create the entity afterwards.

