Skip to content

TRADE Overview

Evan Krause edited this page Sep 17, 2024 · 2 revisions

TRADE in less than 200 words

TRADE is a flexible reflective service-oriented middleware for the Java JVM and higher which provide service interfaces to methods offered by registered components anywhere in a TRADE system. A TRADE system consists of any number of TRADE containers, i.e., TRADE instances running on a heterogeneous network of heterogeneous computers with potentially different operatings systems and networking topologies.

TRADE is automatically initialized in a JVM whenever an instantiated class imports the ai.thinkingrobots.trade package (hence there can only be one instance in each JVM). TRADE containers can automatically discover each other on the same subnet and connect to each other if they have the same system ID, forming a fully connected graph with one bidirectional socket connection between each pair of containers. Alternatively, the can connect directly to a set of given hosts. Any Java object can register any of its public methods annotated by the @TRADEService annotation which will then become available systemwide across all JVMs for any Java object to be consumed, without that object having to be registered itself.

Quick HowTo TRADE

TRADE is essentially about services: (1) how to make them available, (2) how to group them, (3) how to discover them, and (4) how to use them.

Services can be made available and used anywhere anytime in a TRADE system but also altered or removed anytime. They can be chained by defining other services to be called before or after a service, and they can be locked so that only the owner of the lock can call them (chaining and locking are advanced operations that will be discussed later).

A service in TRADE is any public Java method with the @TRADEService annotation, either in a Java class or in a Java interface which makes the method in the class (and all implementing methods of interfaces) eligible in the TRADE system. To be also available in the system, services must be registered.

How to make services available

Suppose we have defined the following class RobotArm that implements various actions for a robotic arm:

public class RobotArm {
...

    // opens the robot's gripper at given speed  
    @TRADEService
    public boolean openGripper(int speed) {
    
    }

...
}

The RobotArm arm class consists of a method openGripper which can be registered and thus made available systemwide:

...
RobotArm arm = new RootArm(...);
TRADE.register(arm,null);
...

Here, TRADE.register takes an object and a String (or a Collection<String>) which is the name of an (optional) {\em group} to which all methods annoted with @TRADEService should belong (see below). In this case, no group names are supplied and openGripper is thus made available system-wide without any specific group labels.

How to group services

Services can be grouped when they logically belong together which later allows other services and processes to discover the services they need to perform their operations. We could have added the MyRobot'' group identifier to the registration to indicate that the registered method belonged to group called MyRobot'', possibly with other methods from different registered Java objects.

...
RobotArm arm = new RootArm(...);
TRADE.register(arm,"MyRobot");
...

In the above case, for example, a vision component with a checkFor(String objecttype)'' could also be registered and added to the MyRobot'' group to indicate that the service uses the camera on the particular robot arm (even though the vision code is implemented in a separate class). This allows users to find the right set of services, e.g., when there are more arms and cameras available in the system.

How to discover services

Groups in TRADE can added and modified at run-time and used either directly used or connected to identifiers that are meaningful to an agent. For example, a multi-agent planning algorithm generating an action for a particular robot will need an identifier to make reference to that robot in the plan and that identifier needs to be linked to the actual physical hardware on which the action is supposed to be executed. Groups make this link easy by allowing processes to discover serives at run-time, for example all methods available for ``MyRobot'':

...
Collection<TRADEServiceInfo> myRobotServices = TRADE.getAvailableServices(
    new TRADEServiceConstraints().inGroups("MyRobot")
);
...

Here, the variable myRobotServices will consist of a set of returned handles of the type TRADEServiceInfo which can be used to consume those services if the user has the appropriate access priviledges. The TRADEServiceInfo contains information about the service name, the service arguments and the return type, and the groups it belongs to which user processes can obtain. The above TRADE.getAvailable method allows for defining search constraints in terms of TRADEServiceConstraints to select particular services (if no constraints are given, handles for all available services are returned).

How to use services

Suppose we want to use the openGripper service:

...
for(TRADEServiceInfo service : myrobotservices) {
  if (service.serviceString.equals("openGripper(int)") {
    try {
      // open the gripper with speed=3
      service.call(void.class,3);
    } catch(TRADEException te) {
      // handle the failure
      ...
    }
  }
}
...

Note that we iterate through the various services associated with the MyRobot'' group to find the one we want and then call it with the class of its return type (in this case void.class'' as it does not return any values) and its integer speed argument. The call must be enclosed in a try-catch statement to be able to react to different call failures indicated by a TRADEException. If calls have to be made more than once, then it is advisable to save the handle and simply reuse it, e.g.,

...
TRADEServiceInfo myGripper;
for(TRADEServiceInfo service : myrobotservices) {
  if (service.serviceString.equals("openGripper(int)") {
    myGripper = service;
    break;
  }
}
try {
  myGripper.call(void.class,3)
} ...

TRADE configurations

We distinguish three types of TRADE systems: (1) one container on one computer, (2) multiple containers (i.e., separate JVMs in computational processes) on one computer, and (3) multiple containers on multiple computers (possibly with different operating systems).

Case (1) will always work without the need for any configurations if autodiscovery is turned off and no connection information is specified in the TRADE.properties. With autodiscovery and/or IP connection information the caveat is to ensure that the TRADESYSID specified in TRADE.properties which is used to identify which distributed TRADE containers belong to a given TRADE system is unique to prevent other running containers from connecting.

Case (2) will either require autodiscovery to be enabled or explicit IP addresses set with all containers that are supposed to be connected having the same TRADESYSID (the same caveat as above applies).

Case (3) can require more complex configurations depending on the network topology and the sequence in which containers are started. The simplest case is that of all containers listening on the same subnet which allows autodiscovery to connect them. If autodiscovery is turned off, then explicit IP addresses need to be provided for containers acting as "servers" and others as "clients" connecting to those servers (the details of how to configure more complex TRADE systems will be described separately).

Clone this wiki locally