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

Parameters:

  • name (String)

    the name of the new action

Yields:

  • the action script definition

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

Parameters:

  • name (String)

    the name of the new action

Yields:

  • the action state machine definition

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:



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)



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:

  • (Array<Model<FaultResponseTable>>)


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:

  • name (String)

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



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.



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:

  • method_name (String)

    the method name

  • description (Models::Action)

    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:

  • (ArgumentError)

    if describe was not called



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:

  • (Boolean)


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