neuray Services API Programmer's Manual

Commands

[neuray Services API]

Description

Commands form the core of neuray services and the user is able to install custom commands to provide enhanced functionality. These commands have full access to the neuray library and are able to call other service commands.

Implementing commands

Commands are implemented by deriving a class from the mi::nservices::IService_command interface and implementing all methods.

Arguments and results

Arguments are passed to commands as a set of named parameters that can be obtained from the mi::nservices::ICommand_context supplied during execution. These arguments can be any mi::IData derived neuray services type. Commands return a single result which can also be any supported type. Command implementations are able to specify the set of arguments they accept, their type and default values. If this is done then neuray Services will automatically convert incoming arguments to the expected type and provide default values if none are supplied.

Note:
It is recommended that when accessing arguments to always retrieve them using the interface closest to the base that supports the required functionality. This is especially important for numeric and user type arguments. For efficiency neuray Services does not convert to the exact numeric type specified by an argument. For example if an argument is specified as a mi::Uint8 it is not guaranteed that an mi::IUint8 will be supplied. Therefore extracting the argument as an mi::IUint8 will most likely fail even if (for example) the value 32 has been supplied. It is recommended that numeric arguments be obtained as the generic mi::INumber interface and then extracted to the expected type. For example:
‎// An argument called "index" of type "Uint8" has been specified via
// mi::nservices::IService_command::get_argument_description
mi::nservices::ICommand_context *context = ....;
const mi::INumber *arg = context->get_argument<mi::INumber>("index");
mi::Uint8 index;
if (arg) {
    arg->get_value(index);
    arg->release();
} else {
    // error, no "index" argument supplied
}
The same applies to Map and User Type arguments. These should all be accessed as mi::IData_collection as the exact implementation supplied is not guaranteed.

The execution environment

The ICommand_context provided to the command's execute handler supplies the execution environment to the command. This consists of three items.

  • The current transaction

Neuray services provides a current transaction as specfied by the hosting environment. Commands should generally use this transaction to access the neuray database unless their arguments specify a different scope to use. The current transaction can be obtained using ICommand_context::get_transaction(). The command should not commit or abort this transaction unless absolutely necessary as it is intended to be used by all commands within a single service request. If it is necessary to do so then ICommand_context::create_new_transaction() should be used. A command may also change the scope of the current transaction by using ICommand_context::change_scope() which will commit the current transaction and create a new transaction at the supplied scope. This new transaction can then be obtained by calling ICommand_context::get_transaction(). All remaining commands in the current service request will use this new transaction. If the ICommand_context::change_scope() call fails, for example, if the new scope does not exist, the scope is not changed and the existing transaction remains valid.

  • The user state transaction

Neuray services also supplies a user state transaction using ICommand_context::get_user_state(). This can be used to store and retrieve user specific data. The scope of this transaction is again specified by the hosting environment and cannot be changed by commands.

  • Direct neuray library access.

Commands can directly access an mi::neuraylib::IPlugin_api using ICommand_context::get_plugin_api(). This can be used to access any neuray library functionality not fulfilled by the current transaction.

Filesystem paths

Neuray services provides a system for mapping relative filesystem paths passed in as command arguments to absolute ones. An interface to do this is provided via an attachment called 'path_remapper'. The attachments can be accessed via ICommand_context::get_attachments() and the remapper retrieved via mi::IMap::get_value<mi::nservices::IPath_remapper>("path_remapper").

Errors

If a command encounters an error during execution it should specify the error using ICommand_response::set_error(). Neuray services reserves error codes less than 0 for internal use and positive error codes for command errors. Error code 0 indicates no error. See Errors.

Executing other commands

A command may execute another command by creating an ICommand_request with mi::nservices::IFactory::create("Command_request"). Set the name of the command to execute and it's arguments then pass it to ICommand_context::execute_command for execution. An ICommand_response is returned which contains the result of the command or any errors.

Sample command. Adds two vectors together.

Obviously nothing this trivial warrants its own command but it shows the basic structure.

‎#include <mi/nservices.h>
#include <mi/base.h>

using namespace mi;
using namespace nservices;

void set_argument_description(
    IArgument_description *arg,
    const char *name,
    const char *type_name,
    const char *description)
{
    if (arg) {
        arg->set_name(name);
        arg->set_type_name(type_name);
        arg->set_description(description);
    }
}

class Service_command_vector3_add : public
    mi::base::Interface_implement<IService_command>
{
public:
    virtual const char *get_name() const { return "vector3_add"; };

    virtual const char *get_namespace() const { return NULL; };

    virtual const char *get_description() const {
        return "adds the two vector3 arguments together";
    };

    virtual mi::Sint32 get_argument_description(
            IFactory *factory,
            const char *argument_name,
            IArgument_description *argument) const {
        if (strcmp(argument_name,"op1") == 0) {
            set_argument_description(argument,"op1","Float32<3>","operand1");
            return NRS_ERROR_NONE;
        };
        if (strcmp(argument_name,"op2") == 0) {
            set_argument_description(argument,"op2","Float32<3>","operand2");
            return NRS_ERROR_NONE;
        };
        return NRS_ERROR_NOT_FOUND;
    };

    virtual mi::Sint32 get_argument_description(
            IFactory *factory,
            mi::Uint32 index,
            IArgument_description *argument) const {
        switch (index) {
            case(0): return get_argument_description(factory,"op1",argument);
                     break;
            case(1): return get_argument_description(factory,"op2",argument);
                     break;
        }
        return NRS_ERROR_NOT_FOUND;
    };

    virtual const char *get_return_type_name() const { return "Float32<3>"; };

    virtual const ICommand_response *execute(ICommand_context *context) const {

        ICommand_response *response = NULL;

        if (context) {
            mi::base::Handle<IFactory> factory( context->get_factory() );
            response = context->create_command_response();
            if (response) {
                // sanity check
                if (strcmp(context->get_command_name(),get_name()) != 0) {
                    // this isn't me and shouldn't happen
                    response->set_error(NRS_ERROR_COMMAND_NEURAY_ERROR,
                                                            "Incorrect command called");
                    return response;
                }
                // find arguments
                mi::base::Handle<const IFloat32_3> op1(
                    context->get_argument<IFloat32_3>("op1"));
                if (op1.is_valid_interface()) {
                    mi::base::Handle<const IFloat32_3> op2(
                        context->get_argument<IFloat32_3>("op2"));
                    if (op2.is_valid_interface()) {
                        // create result
                        mi::base::Handle<IFloat32_3> result(
                                factory->create<IFloat32_3>("Float32<3>"));
                        if (result.is_valid_interface()) {
                            // add them up
                            Float32_3_struct result_v;

                            result_v.x = op1->get_value().x + op2->get_value().x;
                            result_v.y = op1->get_value().y + op2->get_value().y;
                            result_v.z = op1->get_value().z + op2->get_value().z;

                            // set the result
                            result->set_value(result_v);

                            // pass the result into the response
                            response->set_result(result.get());
                        } else {
                            response->set_error(NRS_ERROR_COMMAND_NEURAY_ERROR,
                                                        "Cannot create result");
                        }
                    } else {
                        response->set_error(NRS_ERROR_COMMAND_INVALID_PARAMS,
                                                    "Cannot find Float32<3> parameter 'op2'");
                    }
                } else {
                    response->set_error(NRS_ERROR_COMMAND_INVALID_PARAMS,
                                                "Cannot find Float32<3> parameter 'op1'");
                }
            }
        }
        return response;
    };
};

Classes

class 
Describes an argument to a service command. More...
class 
The context is which a command is executed. More...
class 
The context is which neuray commands are executed. More...
class 
A request to execute a web service command. More...
class 
A response from a neuray service command. More...
class 
This class is used to map relative paths passed in as arguments to commands to absolute paths on the local filesystem. More...
class 
Interface for Neuray Service Commands. More...