Menu

Resources

Tomassino Ferrauto Stefano Nolfi
Prev: Configuring components Up: Plugins, components and resources Next: Creating a new experiment

Table of contents

Declaring and accessing resources

This page contains information about resources in FARSA. Here we will show the typical use cases, for the complete documentation about resources and related classes take a look at the API reference guide.

Introduction

Resources are another important aspect of experiments in FARSA. They are a mechanism to share data among different components of a simulation, without the need to rely on complex interfaces or type casts. The components that make up an experiment in FARSA, in fact, are specified in a configuration file, that is loaded at runtime. This means that a component does not know which other components are part of the experiment until all components are loaded and the experiment is running. Yet, it is sometimes useful to share data among different components. Imagine, for example, a sensor that provides the robot with the distance from a certain landmark. The landmark must be available to both the sensor and the simulation controller (that might create or remove the landmark, move it to a different position and so on). The object modelling the landmark can be defined as a resource and given a name (a string) by the component that creates it. Any other component willing to access the landmark can use the name to obtain a reference to it, thus removing the need to use type casts or to force the implementation of a certain interface.

In this page we will first show which are the prerequisite to using resources and then we will describe how resources are declared and used. We will also explain how to access resources during the configuration phase and how to be notified when a resource changes. Finally, we will give a list of some of the most useful resources declared in FARSA experiments.

Prerequisites

To be able to use resources your class must inherit from the ConcurrentResourcesUser class. Moreover, all classes that want to share resources must be associated explicitly using the ConcurrentResourcesUser::shareResourcesWith() function. Most of the components present in FARSA already inherit from that class, comprising Evoga and EvoRobotExperiment (for evolutionary experiments), sensors, motors and others. This means that for the typical use cases you will be able to use resources automatically. Further details on the ConcurrentResourcesUser class and on how to share resources among different objects can be found in the API reference guide. There is only one thing that should be kept in mind: whenever the shareResourcesWith function is called, the resources of the object on which the function is called are lost. This means that in most cases you should not declare resources in the constructor, instead override the shareResourcesWith function and declare resources there, after calling the parent function. One notable exception are experiments (e.g. subclasses of EvoRobotExperiment), because they call shareResourcesWith of other objects, but theirs is never called.

Declaring resources

As explained above, a resource is basically a string associated to a pointer. To declare a resource you can use the ConcurrentResourcesUser::declareResource() function as in the examples below:

:::C++
int i = ...;
float f = ...;
ParameterSettable* c = ...;

declareResource("i", &i);
declareResource("f", &f);
declareResource("aComponent", c);

The declareResource() function takes two parameters: the first one is the name of the resource (case sensitive!), the second one is the pointer to a variable of a primitive type or to an object. To declare an object as a resource, it must inherit from one of the following classes:

* Resource
* ParameterSettable
* QObject

Once declared, it is possible to change the pointer associated to a resource simply by calling declareResource() with the same resource name. To delete a resource you can use the deleteResource() function, that only removes the association between the name and the pointer.

Here are some important things to remember on resources and memory manegement: the resource mechanism will never delete a pointer, so you must manage memory by yourself. Moreover the pointer should always refer to a valid memory area: you must never free memory of a resource before deleting or re-declaring it (i.e. first call deleteResource() or declareResource() and then free memory if needed). This also means that a resource pointing to a local variable is generally a bad idea.

Accessing resources

Once a resource has been declared, it can be accessed by all objects sharing resources (in most FARSA experiments this means that they can be accessed by all components). Before actually obtaining the pointer to the resource, there are two preliminary operations to be performed. The first one is to declare which are the resources that a class is going to use. This is in general done in the class constructor. Resources need not to exist already when you declare that you will access them. They must exist, however, when you try to access them (more on this below).

The second step is acquiring the lock on resources. Resources can be accesses in a thread-safe way by objects living in different threads. For this to be possible, it is necessary to explicitly acquire a mutex on resources before using them. The lock must then be released when you are done using the resource.

To actually get the pointer associated with a resource, you have to use the getResource() method. This is a template function whose template parameter is the type of the pointer to get and whose only parameter is the name of the resource. The following example shows how to access a resource, including the preliminary steps described above (suppose MyComponent inherits from ConcurrentResourcesUser either directly or not):

:::C++
MyComponent::MyComponent() : ...
{
    ...
    addUsableResource("arena");
    ...
}

MyComponent::f()
{
    ResourcesLocker locker(this);

    Arena* arena = getResource<Arena>("arena");

    ...
}

The code inside the constructor shows how to declare that the "arena" resource will be used. You can call the addUsableResource multiple times in case you need to access more than one resource, or you can use other functions (see the documentation of the ConcurrentResourcesUser class). If you try to access a resource that hasn't been added to the set of resources that will be used, getResource will throw an exception.

The body of the member function f() shows how to acquire the lock and obtain the pointer. The first line of the function declares an object of type ResourcesLocker. The constructor takes a pointer to the ConcurrentResourcesUser that is going to use the resources. The lock is automatically acquired by the constructor of locker and released by its destructor. This means that you do not have to remember to perform the unlock by hand, it will be automatically performed at the end of the f() function. If you try to access a resource without first acquiring the lock, getResource will throw an exception.

The instruction following the acquisition of the lock in the f() method shows how to actually get the pointer associated to the "arena" resource. The getResource() function has one template parameter (the type of the pointer) and one parameter (the name of the resource). If the resource does not exist or has not the requested type, an exception is thrown. Once you have acquired the lock, you can request all the resources you need with additional calls to getResource().

NOTE: if you store the pointer to a resource and use that pointer, bypassing the call to getResource(), you should nonetheless acquire the lock when you use the resource. This is needed to prevent concurrent accesses to the resource (getResource() would force you to behave in a thread-safe way because an exception is thrown is the lock is not acquired). If you want to store a pointer to a resource, you should also use the notification mechanism (explained below) to make sure to change or invalidate the pointer when the resource is changed or deleted.

Accessing resources during the configuration phase (optional)

Sometimes it is necessary to use resources during the configuration phase of components, either in the constructor or in the configure() function. At that time it is not possible to use the method described above because, generally, the call to ConcurrentResourcesUser::shareResourcesWith() hasn't been done yet: this means that resources are not yet shared among different components. It is possible, however, to access a shared set of resources through the ConfigurationParameters object, using the getResourcesUserForResource() function. The function has one parameter that is the name of the resource and returns a pointer to a SimpleResourcesUser object that can be used to access the resource. The API documentation of the ConfigurationParameters class provides a detailed description of the process.

Notification on resource changes (optional)

Declaring which resources a class is going to use has the side effect that whenever one of those resources changes, the object is notified. The ConcurrentResourcesUser class has a virtual member function, resourceChanged(), that can be re-implemented to handle these notifications. Here is an example implementation of the function:

:::C++
MyComponent::MyComponent() : ...
{
    ...
protected:
    virtual void resourceChanged(QString name, ResourceChangeType changeType);
    ...
}

MyComponent::resourceChanged(QString name, ResourceChangeType changeType)
{
    // Calling parent function
    MyParentComponent::resourceChanged(resourceName, changeType);

    if (resourceName == "arena") {
        if (changeType == Created) {
            // "arena" resource created
            Arena* arena = getResource<Arena>();
            ...
        } else if (changeType == Modified) {
            // "arena" resource modified
            Arena* arena = getResource<Arena>();
            ...
        } else if (changeType == Deleted) {
            // "arena" resource deleted
            ...
        }
    }
    ...
}

The function has two parameters: name is the name of the resource that has changed, and changeType is the kind of change that happened (the possible types of changes are shown in the function body). Inside the resourceChanged() function you can only access the resource that has changed and you can obtain a pointer (unless the resource has been deleted) by calling getResource() without any argument. As it is possible to declare the use of a resource that does not yet exists, this function can be used to understand when the resource is actually created (the changeType parameter will be set to Created).

An important thing to note is that what changes is the resource, not the resource value. If, for example, there is a resource that points to an integer variable, this function is not called when the variable changes value from, say, 10 to 20. It is only called if the resource is created for the first time, it is deleted, or if declareResource is called again to change the variable to which the resource is associated.

Commonly used resources in evolutionary experiments

Here is a list of resources that are defined in evolutionary experiments (those using the Evoga and EvoRobotExperiment components). The description of components in the API documentation should list which are the resources declared or required by various other components.

  • experiment - the instance of the experiment (subclass of EvoRobotExperiment).
  • robot - the robot being simulated (or the current robot in case of multi-agent simulations) .
  • evonet - the neural network controlling the robot (or the current robot in case of multi-agent simulations).
  • arena - the Arena in which robots are simulated (this only exists if the Arena component has been created).
  • world - the simulated world.
  • agent[i]:robot - the i-th robot in a multi-agent simulation.
  • agent[i]:evonet - the neural network controlling the i-th robot in a multi-agent simulation.

Related

Manual: APIDoc
Manual: ComponentsConfig
Manual: ComponentsPluginAndResources
Manual: CreatingNewExperiment
Manual: CustomSensorMotor
Manual: Home

Discussion

Anonymous
Anonymous

Add attachments
Cancel





MongoDB Logo MongoDB