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

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

Defined Under Namespace

Classes: ArgumentCountMismatch

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(m, *args) ⇒ Action

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

Returns:



274
275
276
277
278
279
280
281
282
# File 'lib/roby/actions/models/interface_base.rb', line 274

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

Instance Method Details

#action_script(name, options = Hash.new) { ... } ⇒ 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:



263
264
265
# File 'lib/roby/actions/models/interface_base.rb', line 263

def action_script(name, options = Hash.new, &block)
    create_and_register_coordination_action(name, Coordination::ActionScript, &block)
end

#action_state_machine(name) { ... } ⇒ Action, Coordination::Models::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:



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

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

#clear_modelObject

Clears everything stored on this model



64
65
66
67
# File 'lib/roby/actions/models/interface_base.rb', line 64

def clear_model
    super
    actions.clear
end

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



189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/roby/actions/models/interface_base.rb', line 189

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

    return action_model, coordination_model
end

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

Helper method for #action_state_machine and #action_script



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

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)
    return action_model, coordination_model
end

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



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/roby/actions/models/interface_base.rb', line 204

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)



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

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 = self.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



74
75
76
77
78
79
# File 'lib/roby/actions/models/interface_base.rb', line 74

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:



56
57
58
59
60
61
# File 'lib/roby/actions/models/interface_base.rb', line 56

def each_action
    return enum_for(:each_action) if !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>>)


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

inherited_attribute(:fault_response_table, :fault_response_tables) { Array.new }

#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:



153
154
155
# File 'lib/roby/actions/models/interface_base.rb', line 153

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:



162
163
164
165
166
167
168
169
170
# File 'lib/roby/actions/models/interface_base.rb', line 162

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



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/roby/actions/models/interface_base.rb', line 117

def method_added(method_name)
    super
    if @current_description
        name = method_name.to_s
        description, @current_description = @current_description, nil
        description = MethodAction.new(self, description)

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

        expected_argument_count =
            if description.arguments.empty? then 0
            else 1
            end
        begin
            check_arity(instance_method(name), expected_argument_count)
        rescue ArgumentError => e
            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
        register_action(name, description)
    end
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



16
17
18
# File 'lib/roby/actions/models/interface_base.rb', line 16

def promote_registered_action(name, action)
    action
end

#register_action(name, action_model) ⇒ Object

Registers a new action on this model

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.



87
88
89
90
91
92
# File 'lib/roby/actions/models/interface_base.rb', line 87

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

#registered_actionHash<String,Models::Action>

The set of actions defined on this interface

Returns:



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

inherited_attribute(:registered_action, :actions, map: true) { Hash.new }

#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



180
181
182
183
184
185
186
187
# File 'lib/roby/actions/models/interface_base.rb', line 180

def require_current_description
    action_model, @current_description = @current_description, nil
    if action_model
        action_model
    else
        raise ArgumentError, "you must describe the action with #describe before calling #action_coordination"
    end
end

#respond_to_missing?(m, include_private) ⇒ Boolean

Returns:

  • (Boolean)


267
268
269
# File 'lib/roby/actions/models/interface_base.rb', line 267

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

#state_machine(name, &block) ⇒ Object

Deprecated.

use #action_state_machine instead



250
251
252
# File 'lib/roby/actions/models/interface_base.rb', line 250

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

#use_fault_response_table(table_model, arguments = Hash.new) ⇒ Object

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



286
287
288
289
# File 'lib/roby/actions/models/interface_base.rb', line 286

def use_fault_response_table(table_model, arguments = Hash.new)
    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:



40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/roby/actions/models/interface_base.rb', line 40

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