Module: Roby::Actions::Models::InterfaceBase

Extended by:
MetaRuby::Attributes
Included in:
Interface, Library
Defined in:
lib/roby/actions/models/interface_base.rb

Overview

Model-level API for Interface

Defined Under Namespace

Classes: ArgumentCountMismatch

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, **kw) ⇒ Action

Returns an action description if ‘m’ is the name of a known action

Returns:



361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/roby/actions/models/interface_base.rb', line 361

def method_missing(name, *args, **kw)
    if (model = find_action_by_name(name.to_s))
        if args.size > 1
            raise ArgumentError,
                  "expected zero or one argument, got #{args.size}"
        end

        return model.new(*args, **kw)
    end

    super
end

Instance Method Details

#action_script(name, **options) { ... } ⇒ Action, Coordination::Models::ActionScript

Defines an action from an Coordination::Models::ActionScript

The action script model can later be retrieved using Action#coordination_model

Yields:

  • the action script definition

Parameters:

  • the name of the new action

Returns:



348
349
350
351
352
# File 'lib/roby/actions/models/interface_base.rb', line 348

def action_script(name, **options, &block)
    create_and_register_coordination_action(
        name, Coordination::ActionScript, **options, &block
    )
end

#action_state_machine(name) { ... } ⇒ (CoordinationAction,ActionStateMachine)

Defines an action from an Coordination::Models::ActionStateMachine

The action state machine model can later be retrieved using Action#coordination_model

Examples:

creating an action state machine model

class Main < Roby::Actions::Interface
  action_state_machine 'example_action' do
    move  = state move(speed: 0.1)
    stand = state move(speed: 0)
    # This monitor triggers each time the system moves more than
    # 0.1 meters
    d_monitor = task monitor_movement_threshold(d: 0.1)
    # This monitor triggers after 20 seconds
    t_monitor = task monitor_time_threshold(t: 20)
    # Make the distance monitor run in the move state
    move.depends_on d_monitor
    # Make the time monitor run in the stand state
    stand.depends_on t_monitor
    start move
    transition move, d_monitor.success_event, stand
    transition stand, t_monitor.success_event, move
  end
end

retrieving a state machine model from an action

Main.find_action_by_name('example_action').coordination_model

Yields:

  • the action state machine definition

Parameters:

  • the name of the new action

Returns:



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/roby/actions/models/interface_base.rb', line 312

def action_state_machine(name, &block)
    action_model = require_current_description

    # Define user-possible starting states, this will override
    # the default starting state
    if action_model.has_arg?("start_state")
        raise ArgumentError,
              "A argument \"start_state\" has defined for the "\
              "statemachine, but this keyword is reserved"
    end

    action_model.optional_arg(
        "start_state",
        "name of the state in which the state machine should start",
        nil
    )
    create_and_register_coordination_action(
        name, Coordination::ActionStateMachine,
        action_model: action_model, &block
    )
end

#added_method_action_check_arity(name, description) ⇒ Object



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/roby/actions/models/interface_base.rb', line 181

def added_method_action_check_arity(name, description)
    expected_argument_count =
        if description.arguments.empty? then 0
        else
            1
        end

    begin
        check_arity(instance_method(name), expected_argument_count)
    rescue ArgumentError
        if expected_argument_count == 0
            raise ArgumentCountMismatch,
                  "action #{name} has been declared to have no "\
                  "arguments, the #{name} method must be callable "\
                  "without any arguments"
        else
            raise ArgumentCountMismatch,
                  "action #{name} has been declared to have "\
                  "arguments, the #{name} method must be "\
                  "callable with a single Hash argument"
        end
    end
end

#clear_modelObject

Clears everything stored on this model



68
69
70
71
# File 'lib/roby/actions/models/interface_base.rb', line 68

def clear_model
    super
    actions.clear
end

#create_and_register_coordination_action(name, coordination_class, action_model: require_current_description, &block) ⇒ (CoordinationAction,Action)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Create an action that will instanciate a coordination model

Returns:

API:

  • private



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/roby/actions/models/interface_base.rb', line 255

def create_and_register_coordination_action(
    name, coordination_class,
    action_model: require_current_description, &block
)
    name = name.to_s
    create_default_action_return_type(name, action_model)
    action_model, coordination_model = create_coordination_action(
        action_model, coordination_class, &block
    )
    register_action!(name, action_model)
    coordination_model.name = name

    define_method(name) do |**arguments|
        action_model.instanciate(plan, **arguments)
    end

    [action_model, coordination_model]
end

#create_coordination_action(action_model, coordination_class, &block) ⇒ Object

Helper method for #action_state_machine and #action_script



292
293
294
295
296
297
298
299
# File 'lib/roby/actions/models/interface_base.rb', line 292

def create_coordination_action(action_model, coordination_class, &block)
    coordination_model = create_coordination_model(
        action_model, coordination_class, &block
    )
    action_model =
        CoordinationAction.new(coordination_model, action_model)
    [action_model, coordination_model]
end

#create_coordination_model(action_model, coordination_class, &block) ⇒ Object



274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/roby/actions/models/interface_base.rb', line 274

def create_coordination_model(action_model, coordination_class, &block)
    root_m = action_model.returned_type
    coordination_model =
        coordination_class
        .new_submodel(action_interface: self, root: root_m)

    action_model.arguments.each do |arg|
        if !arg.required
            coordination_model.argument arg.name, default: arg.default
        else
            coordination_model.argument arg.name
        end
    end
    coordination_model.parse(&block)
    coordination_model
end

#create_default_action_return_type(name, action_model) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Create and register the default task model for a given action model if needed (i.e. if the action model does not have an explicit return type yet)

API:

  • private



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/roby/actions/models/interface_base.rb', line 136

def create_default_action_return_type(name, action_model)
    return if action_model.returned_type != Roby::Task

    task_model_name = name.camelcase(:upper)
    if const_defined_here?(task_model_name)
        action_model.returns(const_get(task_model_name))
    else
        task_model = Roby::Task.new_submodel do
            terminates
        end
        const_set task_model_name, task_model
        task_model.permanent_model = permanent_model?
        action_model.returns(task_model)
    end
end

#describe(doc = nil) ⇒ Object

Create a new action description that is going to be used to describe the next method. Note that only methods that have a description are exported as actions

Returns:

  • Action



78
79
80
81
82
83
84
85
86
87
# File 'lib/roby/actions/models/interface_base.rb', line 78

def describe(doc = nil)
    if @current_description
        Actions::Interface.warn(
            "the last #describe call was not followed by an action "\
            "definition. Did you forget to add a method to your "\
            "action interface ?"
        )
    end
    @current_description = Models::Action.new(doc)
end

#each_action {|action| ... } ⇒ Object

Enumerates the actions registered on this interface

Yield Parameters:



59
60
61
62
63
64
65
# File 'lib/roby/actions/models/interface_base.rb', line 59

def each_action
    return enum_for(:each_action) unless block_given?

    each_registered_action do |_, description|
        yield(description)
    end
end

#fault_response_tableArray<Model<FaultResponseTable>>

The set of fault response tables added to this action interface

Returns:



31
# File 'lib/roby/actions/models/interface_base.rb', line 31

inherited_attribute(:fault_response_table, :fault_response_tables) { [] }

#find_action_by_name(name) ⇒ Models::Action?

Returns the action description for the given action name, or nil if there are none with that name

Parameters:

Returns:



210
211
212
# File 'lib/roby/actions/models/interface_base.rb', line 210

def find_action_by_name(name)
    find_registered_action(name.to_s)
end

#find_all_actions_by_type(type) ⇒ Array<Models::Action>

Returns all the action description for the actions that can produce such a task

Parameters:

Returns:



219
220
221
222
223
224
225
226
227
# File 'lib/roby/actions/models/interface_base.rb', line 219

def find_all_actions_by_type(type)
    result = []
    each_action do |description|
        if description.returned_type.fullfills?(type)
            result << description
        end
    end
    result
end

#method_added(method_name) ⇒ Object

Hook used to export methods for which there is a description



153
154
155
156
157
158
159
160
# File 'lib/roby/actions/models/interface_base.rb', line 153

def method_added(method_name)
    super
    return unless @current_description

    description = @current_description
    @current_description = nil
    register_added_method_as_action(method_name, description)
end

#promote_registered_action(_name, action) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Internal handler for MetaRuby’s inherited-attribute functionality. This method does nothing, it’s here so that the registered_action attribute gets promotion enabled. It’s overloaded in Interface to actually rebind the action to the interface

API:

  • private



19
20
21
# File 'lib/roby/actions/models/interface_base.rb', line 19

def promote_registered_action(_name, action)
    action
end

#register_action(name, action_model) ⇒ Object

Registers an action on this model

This is used to selectively reuse an action from a different action interface, for instance:

class Navigation < Roby::Actions::Interface
    action_state_machine 'go_to' do
    end
end

class UI < Roby::Actions::Interface
    register_action 'go_to', Navigation.go_to.model
end


103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/roby/actions/models/interface_base.rb', line 103

def register_action(name, action_model)
    unless action_model.respond_to?(:to_action_model)
        raise ArgumentError, "register_action expects an action model, "\
                             "got #{action_model.class} instead"
    end

    # Copy is performed to avoid changing the original models and
    # really only share the action to another interface
    action_model = action_model.to_action_model.dup
    register_action!(name, action_model)
    action_model
end

#register_action!(name, action_model) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Internal, no-check version of #register_action

If no specific return type has been specified, one is created automatically and registered as a constant on this action interface. For instance, the start_all_devices action would create a simple StartAllDevices task model.

API:

  • private



124
125
126
127
128
129
# File 'lib/roby/actions/models/interface_base.rb', line 124

def register_action!(name, action_model)
    name = name.to_s
    create_default_action_return_type(name, action_model)
    action_model.name = name
    actions[action_model.name] = action_model
end

#register_added_method_as_action(method_name, description) ⇒ Object

Register an existing instance method as action

Parameters:

  • the method name

  • the action model



166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/roby/actions/models/interface_base.rb', line 166

def register_added_method_as_action(method_name, description)
    name = method_name.to_s
    action_m = MethodAction.new(self, description)

    if (existing = find_action_by_name(name))
        if action_m.returned_type == Roby::Task
            action_m.returns(existing.returned_type)
        end
        action_m.overloads(existing)
    end

    added_method_action_check_arity(name, action_m)
    register_action!(name, action_m)
end

#registered_actionHash<String,Models::Action>

The set of actions defined on this interface

Returns:



27
# File 'lib/roby/actions/models/interface_base.rb', line 27

inherited_attribute(:registered_action, :actions, map: true) { {} }

#require_current_descriptionObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Verifies that #describe was called to describe an action that is now being defined, and returns it

The current description is nil after this call

Raises:

  • if describe was not called

API:

  • private



237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/roby/actions/models/interface_base.rb', line 237

def require_current_description
    action_model = @current_description
    @current_description = nil

    unless action_model
        raise ArgumentError,
              "you must describe the action with #describe "\
              "before calling #action_coordination"
    end

    action_model
end

#respond_to_missing?(name, include_private) ⇒ Boolean

Returns:



354
355
356
# File 'lib/roby/actions/models/interface_base.rb', line 354

def respond_to_missing?(name, include_private)
    find_action_by_name(name.to_s) || super
end

#state_machine(name, &block) ⇒ Object

Deprecated.

use #action_state_machine instead



335
336
337
# File 'lib/roby/actions/models/interface_base.rb', line 335

def state_machine(name, &block)
    action_state_machine(name, &block)
end

#use_fault_response_table(table_model, arguments = {}) ⇒ Object

Declare that this fault response table should be used on all plans that are going to use this action interface



376
377
378
379
# File 'lib/roby/actions/models/interface_base.rb', line 376

def use_fault_response_table(table_model, arguments = {})
    table_model.validate_arguments(arguments)
    fault_response_tables << [table_model, arguments]
end

#use_library(library) ⇒ void

This method returns an undefined value.

Adds all actions defined in another action interface or in an action library in this interface

Parameters:



43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/roby/actions/models/interface_base.rb', line 43

def use_library(library)
    if library <= Actions::Interface
        library.each_registered_action do |name, action|
            actions[name] ||= action
        end
        library.each_fault_response_table do |table|
            use_fault_response_table table
        end
    else
        include library
    end
end