Module: Roby::Interface

Extended by:
Logger::Hierarchy
Includes:
V1
Defined in:
lib/roby/interface/base.rb,
lib/roby/interface.rb,
lib/roby/interface/v1.rb,
lib/roby/interface/v2.rb,
lib/roby/interface/job.rb,
lib/roby/interface/core.rb,
lib/roby/interface/async.rb,
lib/roby/interface/async.rb,
lib/roby/interface/v1/tcp.rb,
lib/roby/interface/v2/tcp.rb,
lib/roby/interface/command.rb,
lib/roby/interface/rest/api.rb,
lib/roby/interface/v1/async.rb,
lib/roby/interface/v2/async.rb,
lib/roby/interface/interface.rb,
lib/roby/interface/rest/task.rb,
lib/roby/interface/rest/test.rb,
lib/roby/interface/v1/client.rb,
lib/roby/interface/v1/server.rb,
lib/roby/interface/v2/client.rb,
lib/roby/interface/v2/server.rb,
lib/roby/interface/exceptions.rb,
lib/roby/interface/v2/channel.rb,
lib/roby/interface/rest/server.rb,
lib/roby/interface/v2/protocol.rb,
lib/roby/interface/rest/helpers.rb,
lib/roby/interface/v1/async/log.rb,
lib/roby/interface/v2/async/log.rb,
lib/roby/interface/command_library.rb,
lib/roby/interface/v1/shell_client.rb,
lib/roby/interface/v2/shell_client.rb,
lib/roby/interface/command_argument.rb,
lib/roby/interface/v1/droby_channel.rb,
lib/roby/interface/v1/async/interface.rb,
lib/roby/interface/v2/async/interface.rb,
lib/roby/interface/v1/shell_subcommand.rb,
lib/roby/interface/v2/shell_subcommand.rb,
lib/roby/interface/v1/async/job_monitor.rb,
lib/roby/interface/v1/subcommand_client.rb,
lib/roby/interface/v2/async/job_monitor.rb,
lib/roby/interface/v2/subcommand_client.rb,
lib/roby/interface/v1/async/ui_connector.rb,
lib/roby/interface/v2/async/ui_connector.rb,
lib/roby/interface/v1/async/action_monitor.rb,
lib/roby/interface/v2/async/action_monitor.rb,
lib/roby/interface/v1/async/new_job_listener.rb,
lib/roby/interface/v2/async/new_job_listener.rb

Overview

High-level command and control of a Roby controller

The Interface module provides a high-level control interface to a running Roby controller. It is the basis for all remote Roby UIs such as the Syskit IDE or the Roby shell. The following documentation aims at giving a bird eye’s view of the module’s structure

Jobs

The high-level construct used in the Roby interface is the job. Jobs are representation of the high-level goals that a user gave to the system. A task represents a job if:

  • it provides the Interface::Job service

  • it has a non-nil Interface::Job#job_id argument

  • itself or its planned task is a mission

In case a job task is a planning task, the job itself will be represented by the job’s planned task. Across the job-related APIs, one will see that jobs are therefore associated with two tasks: the task or placeholder task, and the job task itself.

The interface APIs provide ways to track the progress of jobs. Each job transition is represented by a Interface::JOB_* constant (e.g. JOB_READY), and notifications are sent to remote endpoints about the current state and progress of jobs.

Synchronous Client/Server API

A Roby application will in most cases create an Interface object, which is the endpoint for all interface-related matters. A client/server mechanism allows to access the app’s interface. V1::Server provides the server-side and V1::Client the client-side. Both classes are independent of the communication channel used. The communication is based on marshalling and demarshalling of an array that represents a method name and arguments on the Interface class. The marshalling/demarshalling and the exact packet format is left to the channel class given to Client and Server at construction time (see below)

The core of the method calls on V1::Client are the calls available on Interface. Check the latter to figure out what you can do with the former. In addition, it supports starting actions (and jobs) using an action_name!(arguments) syntax. This is meant as syntactic sugar for use in interactive implementations, but one should use Interface#start_job when starting jobs programmatically.

In addition to the remote method calls, the Client API provides notifications pushed by the interface:

Asynchronous API

To connect to the client/server API, one has to have a remote Roby app to connect to. Moreoover, the API is really designed as a request/response scheme, which is not a very nice format to build UIs from.

For these, reasons, a higher level, event-based API has been built on top of the client/server functionality. The main entrypoint for this asynchronous API is V1::Async::Interface. In addition to properly handling (re)connections, this API provides also a nicer interface to job tracking.

Jobs are represented by V1::Async::JobMonitor objects, which track the job state and provide operations on them such as killing, dropping and restarting them as well as registering hooks to track their progress. One usually gets these job monitor objects by listening for new jobs using V1::Async::Interface#on_job.

Note that in most cases, new job monitor objects are inactive (i.e. won’t get notifications) until you explicitely call V1::Async::JobMonitor#start on them. Whether this is the case or not is documented on each method that return or yield a job monitor object.

Asynchronous log stream API

In addition to the notifications provided by V1::Client, one can use the Roby logging to build a complete representation of a plan. The V1::Async::Log class gives easy-to-use access to such a rebuilt plan, along with the ability to disconnect and reconnect to a remote Roby app.

Event Loop Integration

Interface hooks itself in the app’s main event loop, as does V1::TCPServer. On the client side, processing is done in V1::Client#poll which therefore needs to be called periodically within your app’s main loop. In Qt, it usually means starting a timer

timer = Qt::Timer.new(self)
timer.connect(SIGNAL('timeout()')) do
    client.poll
end

Communication Channel

V1::DRobyChannel provides a default implementation, using the DRoby marshalling/demarshalling for object-to-binary translation, WebSockets for framing and a subclass of IO as the underlying communication medium. The most common usage is to spawn a TCP server based on this channel with V1::TCPServer, and connect to it from the client side with Interface.connect_with_tcp_to. A Roby application spawns such a server automatically by calling Application#setup_shell_interface if Application#public_shell_interface? is true.

Defined Under Namespace

Modules: Async, REST, V1, V2 Classes: ComError, Command, CommandArgument, CommandLibrary, ConnectionError, Interface, InvalidState, ProtocolError

Constant Summary collapse

DEFAULT_PORT =
20_201
DEFAULT_REST_PORT =
20_202
DEFAULT_PORT_V2 =
20_203
JOB_PLANNING_READY =

The job’s planning task is ready to be executed

:planning_ready
JOB_PLANNING =

The job’s planning task is running

:planning
JOB_PLANNING_FAILED =

The job’s planning task has failed

:planning_failed
JOB_READY =

The job’s main task is ready to be executed

:ready
JOB_STARTED =

The job is started

:started
JOB_SUCCESS =

The job has finished successfully

:success
JOB_FAILED =

The job has failed

:failed
JOB_FINISHED =

The job has finished

:finished
JOB_FINALIZED =

The job has been finalized (i.e. removed from plan)

:finalized
JOB_DROPPED =

The job has been dropped, i.e. its mission status has been removed

:dropped
JOB_MONITORED =

Initial notification, when the interface starts monitoring a job

:monitored
JOB_LOST =

The job got replaced by a task that is not this job

:lost
JOB_REPLACED =

The job placeholder task got replaced, and the replacement is managed under the same job

:replaced

Class Method Summary collapse

Class Method Details

.connect_with_tcp_to(host, port = DEFAULT_PORT, marshaller: DRoby::Marshal.new(auto_create_plans: true), handshake: %i[actions commands]) ⇒ Object



23
24
25
26
27
28
29
30
31
# File 'lib/roby/interface.rb', line 23

def self.connect_with_tcp_to(
    host, port = DEFAULT_PORT,
    marshaller: DRoby::Marshal.new(auto_create_plans: true),
    handshake: %i[actions commands]
)
    V1.connect_with_tcp_to(
        host, port, marshaller: marshaller, handshake: handshake
    )
end

.error_state?(state) ⇒ Boolean

Tests if the given state (one of the JOB_ constants) means that the job finished with error

Returns:

  • (Boolean)


54
55
56
# File 'lib/roby/interface/interface.rb', line 54

def self.error_state?(state)
    [JOB_PLANNING_FAILED, JOB_FAILED].include?(state)
end

.finalized_state?(state) ⇒ Boolean

Tests if the given state (one of the JOB_ constants) means that the job has been finalized (removed from plan)

Returns:

  • (Boolean)


66
67
68
# File 'lib/roby/interface/interface.rb', line 66

def self.finalized_state?(state)
    [JOB_FINALIZED].include?(state)
end

.planning_finished_state?(state) ⇒ Boolean

Whether the given state indicates that the job’s planning is finished

Returns:

  • (Boolean)


36
37
38
# File 'lib/roby/interface/interface.rb', line 36

def self.planning_finished_state?(state)
    ![JOB_PLANNING_READY, JOB_PLANNING, JOB_FINALIZED].include?(state)
end

.running_state?(state) ⇒ Boolean

Tests if the given state (one of the JOB_ constants) means that the job is still running

Returns:

  • (Boolean)


60
61
62
# File 'lib/roby/interface/interface.rb', line 60

def self.running_state?(state)
    [JOB_STARTED].include?(state)
end

.success_state?(state) ⇒ Boolean

Tests if the given state (one of the JOB_ constants) means that the job finished successfully

Returns:

  • (Boolean)


48
49
50
# File 'lib/roby/interface/interface.rb', line 48

def self.success_state?(state)
    [JOB_SUCCESS].include?(state)
end

.terminal_state?(state) ⇒ Boolean

Tests if the given state (one of the JOB_ constants) is terminal, e.g. means that the job is finished

Returns:

  • (Boolean)


42
43
44
# File 'lib/roby/interface/interface.rb', line 42

def self.terminal_state?(state)
    [JOB_PLANNING_FAILED, JOB_FAILED, JOB_SUCCESS, JOB_FINISHED, JOB_FINALIZED].include?(state)
end