Class: Roby::Task

Inherits:
PlanObject show all
Extended by:
DRoby::Identifiable, DRoby::V5::Models::TaskDumper, Models::Task, TaskStateHelper
Includes:
MetaRuby::DSLs::FindThroughMethodMissing, DRoby::V5::TaskDumper, ExceptionHandlingObject, GUI::GraphvizTask, GUI::RelationsCanvasTask
Defined in:
lib/roby/task.rb,
lib/roby.rb,
lib/roby/state/task.rb,
lib/roby/coordination/task_script.rb,
lib/roby/coordination/task_state_machine.rb,
lib/roby/droby/enable.rb

Overview

In a plan, Task objects represent the system’s activities.

Task models

A task model is mainly described by:

a set of named arguments, which are required to parametrize the task instance. The argument list is described using Task.argument and arguments are either set at object creation by passing an argument hash to Task.new, or by calling Task#argument explicitely.

a set of events, which are situations describing the task progression. The base Roby::Task model defines the start,success,failed and stop events. Events can be defined on the models by using Task.event:

class MyTask < Roby::Task
    event :intermediate_event
end

defines a non-controllable event, i.e. an event which can be emitted, but cannot be triggered explicitely by the system. Controllable events are defined by associating a block of code with the event, this block being responsible for making the event emitted either in the future or just now. For instance,

class MyTask < Roby::Task
    event :intermediate_event do |context|
        emit :intermediate_event
    end

    event :other_event do |context|
        execution_engine.once { emit :other_event }
    end
end

define two controllable event. In the first case, the event is immediately emitted, and in the second case it will be emitted at the beginning of the next execution cycle.

Task relations

Task relations are defined in the TaskStructure Relations::Space instance. See TaskStructure documentation for the list of special methods defined by the various graphs, and the TaskStructure namespace for the name and purpose of the various relation graphs themselves.

Executability

By default, a task is not executable, which means that no event command can be called and no event can be emitted. A task becomes executable either because Task#executable= has explicitely been called or because it has been inserted in a Plan object. Note that forcing executability with #executable= is only useful for testing. When the Roby controller manages a real systems, the executability property enforces the constraint that a task cannot be executed outside of the plan supervision.

Finally, it is possible to describe abstract task models: tasks which do represent an action, but for which the means to perform that action are still unknown. This is done by calling Task.abstract in the task definition:

class AbstTask < Roby::Task
    abstract
end

An instance of an abstract model cannot be executed, even if it is included in a plan.

Inheritance rules

On task models, a submodel can inherit from a parent model if the actions described by the parent model are also performed by the child model. For instance, a Goto(x, y) model could be subclassed into a Goto::ByFoot(x, y) model.

The following constraints apply when subclassing a task model:

  • a task subclass has at least the same events than the parent class

  • changes to event attributes are limited. The rules are:

    • a controlable event must remain controlable. Nonetheless, a non-controlable event can become a controlable one

    • a terminal event (i.e. a terminal event which ends the task execution) cannot become non-terminal. Nonetheless, a non-terminal event can become terminal.

Defined Under Namespace

Classes: InternalError

Constant Summary

Constants included from Models::Arguments

Models::Arguments::NO_DEFAULT_ARGUMENT

Deprecated Event API collapse

Instance Attribute Summary collapse

Attributes included from GUI::RelationsCanvasTask

#displayed_state

Attributes inherited from PlanObject

#addition_time, #executable, #execution_engine, #finalization_handlers, #finalization_time, #model, #plan, #promise_executor, #removed_at

Attributes included from Roby::Transaction::Proxying::Cache

#transaction_forwarder_module, #transaction_proxy_module

Attributes included from Relations::DirectedRelationSupport

#relation_graphs

Attributes inherited from DistributedObject

#local_owner_id, #owners

Deprecated Event API collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Models::Task

abstract, all_models, causal_link, clear_model, compute_terminal_events, define_command_method, define_event_methods, discover_terminal_events, enum_events, find_event_model, forward, from, from_state, instantiate_event_relations, interruptible, invalidate_template, model_attribute_list, model_relation, on_exception, precondition, provided_services, query, signal, template, terminates, to_coordination_task, to_execution_exception_matcher, with_arguments

Methods included from Models::Arguments

#argument, #default_argument

Methods included from TaskStateHelper

import_events_to_roby, namespace, namespace=, refine_running_state

Methods included from DRoby::Identifiable

droby_id

Methods included from DRoby::V5::Models::TaskDumper

droby_dump

Methods included from DRoby::V5::ModelDumper

#droby_dump

Methods included from DRoby::V5::TaskDumper

#droby_dump

Methods included from GUI::GraphvizTask

#apply_layout, #dot_label, #to_dot_events

Methods included from GUI::GraphvizPlanObject

#apply_layout, #dot_label, #to_dot

Methods included from GUI::RelationsCanvasTask

#display, #display_create, #display_name, #display_time_end, #display_time_start, #layout_events, to_svg, #update_graphics

Methods included from GUI::RelationsCanvasPlanObject

#display, #display_create, #display_events, #display_name, #display_parent

Methods included from ExceptionHandlingObject

#add_error, #pass_exception

Methods inherited from PlanObject

#apply_relation_changes, #can_finalize?, #concrete_model, #connection_space, #each_finalization_handler, #each_in_neighbour_merged, #each_out_neighbour_merged, #engine, #finalized!, #finalized?, #forget_peer, #garbage?, #merged_relations, #read_write?, #real_object, #remotely_useful?, #root_object, #root_object?, #subscribed?, #transaction_proxy?, #transaction_stack, #update_on?, #updated_by?

Methods included from Models::PlanObject

#child_plan_object, #finalization_handler

Methods included from Relations::DirectedRelationSupport

#[], #[]=, #add_parent_object, #child_object?, #child_objects, #clear_vertex, #each_child_object, #each_in_neighbour, #each_out_neighbour, #each_parent_object, #each_relation, #each_relation_graph, #each_relation_sorted, #each_root_relation_graph, #enum_child_objects, #enum_parent_objects, #enum_relations, #leaf?, #parent_object?, #parent_objects, #related_object?, #related_objects, #relation_graph_for, #relations, #remove_child_object, #remove_children, #remove_parent_object, #remove_parents, #remove_relations, #root?, #sorted_relations

Methods inherited from DistributedObject

#add_owner, #clear_owners, #owned_by?, #remove_owner

Constructor Details

#initialize(plan: TemplatePlan.new, **arguments) ⇒ Task

Create a new task object

Parameters:

  • plan (Plan) (defaults to: TemplatePlan.new)

    the plan this task should be added two. The default is to add it to its own TemplatePlan object

  • arguments (Hash<Symbol,Object>)

    assignation to task arguments



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/roby/task.rb', line 222

def initialize(plan: TemplatePlan.new, **arguments)
    @bound_events = Hash.new
    super(plan: plan)

    @model   = self.class
    @abstract = @model.abstract?
    
    @failed_to_start = false
    @pending = true
    @started = false
    @running = false
    @starting = false
    @finished = false
    @finishing = false
    @success = nil
    @reusable = true
    @history = Array.new
    @coordination_objects = Array.new

    @arguments = TaskArguments.new(self)
    assign_arguments(**arguments)
    # Now assign default values for the arguments that have not yet been
    # set
    model.arguments.each do |argname|
        next if @arguments.has_key?(argname)

        has_default, default = model.default_argument(argname)
        if has_default
            assign_argument(argname, default)
        end
    end

    @poll_handlers = []
    @execute_handlers = []

    initialize_events
    plan.register_task(self)
    template = self.model.template

    mappings = Hash.new
    template.events_by_name.each do |name, template_event|
        mappings[template_event] = bound_events[name]
    end
    template.copy_relation_graphs_to(plan, mappings)
    apply_terminal_flags(
        template.terminal_events.map(&mappings.method(:[])),
        template.success_events.map(&mappings.method(:[])),
        template.failure_events.map(&mappings.method(:[])))
    @terminal_flag_invalid = false

    if self.model.state_machine
        @state_machine = TaskStateMachine.new(self.model.state_machine)
    end
end

Instance Attribute Details

#argumentsTaskArguments (readonly)

The task arguments

Returns:



98
99
100
# File 'lib/roby/task.rb', line 98

def arguments
  @arguments
end

#bound_eventsObject (readonly)

List of EventGenerator objects bound to this task



759
760
761
# File 'lib/roby/task.rb', line 759

def bound_events
  @bound_events
end

#dataObject

The internal data for this task



990
991
992
# File 'lib/roby/task.rb', line 990

def data
  @data
end

#execute_handlersArray<InstanceHandler> (readonly)

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.

The set of instance-level execute blocks

Returns:



1017
1018
1019
# File 'lib/roby/task.rb', line 1017

def execute_handlers
  @execute_handlers
end

#failed_to_start_timeObject (readonly)

The time at which the task failed to start



714
715
716
# File 'lib/roby/task.rb', line 714

def failed_to_start_time
  @failed_to_start_time
end

#failure_eventObject (readonly)

The event that caused this task to fail. This is equivalent to taking the first emitted element of

task.event(:failed).last.task_sources

It is only much more efficient



721
722
723
# File 'lib/roby/task.rb', line 721

def failure_event
  @failure_event
end

#failure_reasonObject (readonly)

The reason for which this task failed.

It can either be an event or a LocalizedError object.

If it is an event, it is the most specialized event whose emission has been forwarded to :failed

If it is a LocalizedError object, it is the exception that caused the task failure.



711
712
713
# File 'lib/roby/task.rb', line 711

def failure_reason
  @failure_reason
end

#historyObject (readonly)

Returns a list of Event objects, for all events that have been fired by this task. The list is sorted by emission times.



106
107
108
# File 'lib/roby/task.rb', line 106

def history
  @history
end

#poll_handlersArray<InstanceHandler> (readonly)

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.

The set of instance-level poll blocks

Returns:



1024
1025
1026
# File 'lib/roby/task.rb', line 1024

def poll_handlers
  @poll_handlers
end

#state_machineObject (readonly)

Returns the value of attribute state_machine.



302
303
304
# File 'lib/roby/coordination/task_state_machine.rb', line 302

def state_machine
  @state_machine
end

#terminal_eventObject (readonly)

The most specialized event that caused this task to end



700
701
702
# File 'lib/roby/task.rb', line 700

def terminal_event
  @terminal_event
end

Class Method Details

.create_script(*task, &block) ⇒ Object



251
252
253
254
255
256
257
258
# File 'lib/roby/coordination/task_script.rb', line 251

def self.create_script(*task, &block)
    script_model = Coordination::TaskScript.new_submodel(root: self)
    script = script_model.new(*task)
    if block_given?
        script.parse(&block)
    end
    script
end

.goalObject



21
22
23
24
25
26
27
28
29
# File 'lib/roby/state/task.rb', line 21

def self.goal
    if !@goal
        if superclass.respond_to?(:goal)
            supermodel = superclass.goal
        end
        @goal = GoalModel.new(self.state, supermodel)
    end
    @goal
end

.script(&block) ⇒ Object

Adds a script that is going to be executed for every instance of this task model



262
263
264
265
266
# File 'lib/roby/coordination/task_script.rb', line 262

def self.script(&block)
    s = create_script(&block)
    scripts << s
    s
end

.stateObject



3
4
5
6
7
8
9
10
11
# File 'lib/roby/state/task.rb', line 3

def self.state
    if !@state
        if superclass.respond_to?(:state)
            supermodel = superclass.state
        end
        @state = StateModel.new(supermodel)
    end
    @state
end

Instance Method Details

#+(task) ⇒ Object

Creates a sequence where self will be started first, and task is started if self finished successfully. The returned value is an instance of Sequence.

Note that this operator always creates a new Sequence object, so

a + b + c + d

will create 3 Sequence instances. If more than two tasks should be organized in a sequence, one should instead use Sequence#<<:

Sequence.new << a << b << c << d


1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
# File 'lib/roby/task.rb', line 1554

def +(task)
    # !!!! + is NOT commutative
    if task.null?
        self
    elsif self.null?
        task
    else
        Tasks::Sequence.new << self << task
    end
end

#abstract?Object

:method:abstract?

If true, this instance is marked as abstract, i.e. as a placeholder for future actions.

By default, it takes the value of its model, i.e. through model.abstract, set by calling abstract in a task model definition as in

class MyModel < Roby::Task
  abstract
end

It can also be overriden on a per instance basis with

task.abstract = <value>


438
# File 'lib/roby/task.rb', line 438

attr_predicate :abstract?, true

#add_child_object(child, type, info) ⇒ 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.

Validates that both self and the child object are owned by the local instance



1247
1248
1249
1250
1251
1252
1253
# File 'lib/roby/task.rb', line 1247

def add_child_object(child, type, info)
    unless read_write? && child.read_write?
        raise OwnershipError, "cannot add a relation between tasks we don't own.  #{self} by #{owners.to_a} and #{child} is owned by #{child.owners.to_a}"
    end

    super
end

#add_coordination_object(object) ⇒ 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.

Declare that a coordination object is attached to this task

Parameters:



1617
1618
1619
# File 'lib/roby/task.rb', line 1617

def add_coordination_object(object)
    @coordination_objects.push(object)
end

#apply_replacement_operations(edges) ⇒ 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.



1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
# File 'lib/roby/task.rb', line 1355

def apply_replacement_operations(edges)
    edges.each do |g, removed_parent, removed_child, added_parent, added_child|
        info = g.edge_info(removed_parent, removed_child)
        g.add_relation(plan[added_parent], plan[added_child], info)
    end
    edges.each do |g, removed_parent, removed_child, added_parent, added_child|
        if !g.copy_on_replace?
            g.remove_relation(plan[removed_parent], plan[removed_child])
        end
    end
end

#apply_terminal_flags(terminal_events, success_events, failure_events) ⇒ Object



636
637
638
639
640
641
642
643
644
645
646
647
648
649
# File 'lib/roby/task.rb', line 636

def apply_terminal_flags(terminal_events, success_events, failure_events)
    for ev in bound_events.each_value
        ev.terminal_flag = nil
        if terminal_events.include?(ev)
            if success_events.include?(ev)
                ev.terminal_flag = :success
            elsif failure_events.include?(ev)
                ev.terminal_flag = :failure
            else
                ev.terminal_flag = true
            end
        end
    end
end

#as_planObject



1486
1487
1488
# File 'lib/roby/task.rb', line 1486

def as_plan
    self
end

#as_servicePlanService

Returns an object that will allow to track this task’s role in the plan regardless of replacements

The returning object will point to the replacing object when self is replaced by something. In effect, it points to the task’s role in the plan instead of to the actual task itself.

Returns:



1482
1483
1484
# File 'lib/roby/task.rb', line 1482

def as_service
    @service ||= (plan.find_plan_service(self) || PlanService.new(self))
end

#assign_argument(key, value) ⇒ 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.

Sets one of this task’s arguments



201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/roby/task.rb', line 201

def assign_argument(key, value)
    key = key.to_sym
    if TaskArguments.delayed_argument?(value)
        @arguments[key] = value
    else
        if self.respond_to?("#{key}=")
            self.send("#{key}=", value)
        end
        if @arguments.writable?(key, value)
            # The accessor did not write the argument. That's alright
            @arguments[key] = value
        end
    end
end

#assign_arguments(**arguments) ⇒ 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.

Helper to assign multiple argument values at once

It differs from calling assign_argument in a loop in two ways:

  • it is common for subclasses to define a high-level argument that is, in the end, propagated to lower-level arguments. This method handles the fact that, when doing this, one will get parallel assignment of the high-level and low-level values during e.g. log replay which would fail in assign_arguments since arguments are single-assignation

  • assignation is all-or-nothing



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/roby/task.rb', line 175

def assign_arguments(**arguments)
    initial_arguments = @arguments
    initial_set_arguments = initial_arguments.assigned_arguments
    current_arguments = initial_set_arguments.dup

    # First assign normal values
    arguments.each do |key, value|
        @arguments = TaskArguments.new(self)
        @arguments.merge!(initial_set_arguments)
        assign_argument(key, value)
        current_arguments.merge!(@arguments) do |k, v1, v2|
            if v1 != v2
                raise ArgumentError, "trying to override #{k}=#{v1} to #{v2}"
            end
            v1
        end
    end
    initial_arguments.merge!(current_arguments)

ensure
    @arguments = initial_arguments
end

#can_merge?(target) ⇒ Boolean

Tests if a task could be merged within self

Unlike a replacement, a merge implies that self is modified to match both its current role and the target’s role. Roby has no built-in merge logic (no merge method). This method is a helper for Roby extensions that implement such a scheme, to check for attributes common to all tasks that would forbid a merge

Returns:

  • (Boolean)


1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
# File 'lib/roby/task.rb', line 1187

def can_merge?(target)
    if defined?(super) && !super
        return
    end

    if finished? || target.finished?
        return false
    end

    if !model.can_merge?(target.model)
        return false
    end

    target.arguments.each_assigned_argument do |key, val|
        if arguments.set?(key) && arguments[key] != val
            return false
        end
    end
    true
end

#can_replace?(target) ⇒ Boolean

True if self can be used to replace target

Returns:

  • (Boolean)


1176
1177
1178
# File 'lib/roby/task.rb', line 1176

def can_replace?(target)
    fullfills?(*target.fullfilled_model)
end

#check_emission_validity(event) ⇒ Object

This method is called by TaskEventGenerator#fire just before the event handlers and commands are called



679
680
681
682
683
684
685
686
687
688
689
690
# File 'lib/roby/task.rb', line 679

def check_emission_validity(event) # :nodoc:
    if finished? && !event.terminal?
        EmissionRejected.new(event).
            exception("#{self}.emit(#{event.symbol}) called by #{execution_engine.propagation_sources.to_a} but the task has finished. Task has been terminated by #{stop_event.last.sources.to_a}.")
    elsif pending? && event.symbol != :start
        EmissionRejected.new(event).
            exception("#{self}.emit(#{event.symbol}) called by #{execution_engine.propagation_sources.to_a} but the task has never been started")
    elsif running? && event.symbol == :start
        EmissionRejected.new(event).
            exception("#{self}.emit(#{event.symbol}) called by #{execution_engine.propagation_sources.to_a} but the task is already running. Task has been started by #{start_event.last.sources.to_a}.")
    end
end

#clear_events_external_relations(remove_strong: true) ⇒ Object

Clear relations events of this task have with events outside the task



574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
# File 'lib/roby/task.rb', line 574

def clear_events_external_relations(remove_strong: true)
    removed = false
    task_events = bound_events.values
    each_event do |event|
        for rel in event.sorted_relations
            graph = plan.event_relation_graph_for(rel)
            next if !remove_strong && graph.strong?

            to_remove = Array.new
            graph.each_in_neighbour(event) do |neighbour|
                if !task_events.include?(neighbour)
                    to_remove << neighbour << event
                end
            end
            graph.each_out_neighbour(event) do |neighbour|
                if !task_events.include?(neighbour)
                    to_remove << event << neighbour
                end
            end
            to_remove.each_slice(2) do |from, to|
                graph.remove_edge(from, to)
            end
            removed ||= !to_remove.empty?
        end
    end
    removed
end

#clear_relations(remove_internal: false, remove_strong: true) ⇒ Object

Remove all relations in which self or its event are involved

Parameters:

  • remove_internal (Boolean) (defaults to: false)

    if true, remove in-task relations between events

  • remove_strong (Boolean) (defaults to: true)

    if true, remove strong relations as well



607
608
609
610
611
612
613
614
615
616
617
618
619
# File 'lib/roby/task.rb', line 607

def clear_relations(remove_internal: false, remove_strong: true)
    modified_plan = false
    if remove_internal
        each_event do |ev|
            if ev.clear_relations(remove_strong: remove_strong)
                modified_plan = true
            end
        end
    else
        modified_plan = clear_events_external_relations(remove_strong: remove_strong)
    end
    super(remove_strong: remove_strong) || modified_plan
end

#commit_transactionObject

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.

This method is called during the commit process to apply changes stored in a proxy



1259
1260
1261
1262
1263
1264
1265
1266
1267
# File 'lib/roby/task.rb', line 1259

def commit_transaction
    super

    arguments.dup.each do |key, value|
        if value.respond_to?(:transaction_proxy?) && value.transaction_proxy?
            arguments.update!(key, value.__getobj__)
        end
    end
end

#compatible_state?(task) ⇒ Boolean

Checks if task is in the same execution state than self Returns true if they are either both running or both pending

Returns:

  • (Boolean)


1008
1009
1010
# File 'lib/roby/task.rb', line 1008

def compatible_state?(task)
    finished? || !(running? ^ task.running?)
end

#compute_object_replacement_operation(object) ⇒ 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.



1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
# File 'lib/roby/task.rb', line 1388

def compute_object_replacement_operation(object)
    edges = []
    plan.each_task_relation_graph do |g|
        next if g.strong?

        g.each_in_neighbour(self) do |parent|
            if parent != object
                edges << [g, parent, self, parent, object]
            end
        end
        g.each_out_neighbour(self) do |child|
            if object != child
                edges << [g, self, child, object, child]
            end
        end
    end

    plan.each_event_relation_graph do |g|
        next if g.strong?

        each_event do |event|
            object_event = nil
            g.each_in_neighbour(event) do |parent|
                if !parent.respond_to?(:task) || (parent.task != self && parent.task != object)
                    object_event ||= object.event(event.symbol)
                    edges << [g, parent, event, parent, object_event]
                end
            end
            g.each_out_neighbour(event) do |child|
                if !child.respond_to?(:task) || (child.task != self && child.task != object)
                    object_event ||= object.event(event.symbol)
                    edges << [g, event, child, object_event, child]
                end
            end
        end
    end
    edges
end

#compute_subplan_replacement_operation(object) ⇒ 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.



1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
# File 'lib/roby/task.rb', line 1279

def compute_subplan_replacement_operation(object)
    edges, edges_candidates = [], []
    subplan_tasks = Set[self, object]
    parent_tasks  = Set.new
    plan.each_task_relation_graph do |g|
        next if g.strong?
        rel = g.class

        each_in_neighbour_merged(rel, intrusive: true) do |parent|
            parent_tasks << parent
            edges << [g, parent, self, parent, object]
        end
        object.each_in_neighbour_merged(rel, intrusive: true) do |parent|
            parent_tasks << parent
        end

        if g.weak?
            each_out_neighbour_merged(rel, intrusive: true) do |child|
                edges_candidates << [child, [g, self, child, object, child]]
            end
        else
            object.each_out_neighbour_merged(rel, intrusive: true) do |child|
                subplan_tasks << child
            end
            each_out_neighbour_merged(rel, intrusive: true) do |child|
                subplan_tasks << child
            end
        end
    end

    plan.each_event_relation_graph do |g|
        next if g.strong?
        rel = g.class

        model.each_event do |_, event|
            event = plan.each_object_in_transaction_stack(self).
                find { |_, o| o.find_event(event.symbol) }.
                last.event(event.symbol)
            object_event = plan.each_object_in_transaction_stack(object).
                find { |_, o| o.find_event(event.symbol) }.
                last.event(event.symbol)

            event.each_in_neighbour_merged(rel, intrusive: false) do |_, parent|
                if parent.respond_to?(:task)
                    edges_candidates <<
                        [plan[parent.task], [g, parent, event, parent, object_event]]
                end
            end
            event.each_out_neighbour_merged(rel, intrusive: false) do |_, child|
                if child.respond_to?(:task)
                    edges_candidates <<
                        [plan[child.task], [g, event, child, object_event, child]]
                end
            end
        end
    end

    edges_candidates.each do |reference_task, op|
        if subplan_tasks.include?(reference_task)
            next
        elsif parent_tasks.include?(reference_task)
            edges << op
        elsif plan.in_useful_subplan?(self, reference_task) || plan.in_useful_subplan?(object, reference_task)
            subplan_tasks << reference_task
        else
            edges << op
        end
    end

    edges = edges.map do |g, removed_parent, removed_child, added_parent, added_child|
        [g, plan[removed_parent], plan[removed_child], plan[added_parent], plan[added_child]]
    end
    edges
end

#create_fresh_copyObject



383
384
385
# File 'lib/roby/task.rb', line 383

def create_fresh_copy
    model.new(arguments.dup)
end

#create_transaction_proxy(transaction) ⇒ 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.



1594
1595
1596
# File 'lib/roby/task.rb', line 1594

def create_transaction_proxy(transaction)
    transaction.create_and_register_proxy_task(self)
end

#current_stateSymbol

Retrieve the current state of the task

Can be one of the core states: pending, failed_to_start, starting, started, running, finishing, succeeded or failed

If the task has a state machine defined with Roby::TaskStateHelper#refine_running_state, the state machine’s current state will be returned in place of :running

Returns:

  • (Symbol)


287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/roby/task.rb', line 287

def current_state
    # Started and not finished
    if running? 
        if respond_to?("state_machine")
            # state_machine.status # => String
            # state_machine.status_name # => Symbol
            return state_machine.status_name
        else
            return :running
        end
    end

    # True, when task has never been started
    if pending? 
        return :pending 
    elsif failed_to_start? 
        return :failed_to_start
    elsif starting?
        return :starting
    # True, when terminal event is pending
    elsif finishing? 
        return :finishing
    # Terminated with success or failure
    elsif success? 
        return :succeeded
    elsif failed? 
        return :failed 
    end
end

#current_state?(state) ⇒ Boolean

Test if that current state corresponds to the provided state (symbol)

Parameters:

  • state (Symbol)

Returns:

  • (Boolean)


321
322
323
# File 'lib/roby/task.rb', line 321

def current_state?(state) 
    return state == current_state.to_sym
end

#do_not_reusevoid

This method returns an undefined value.

Call to force the value of #reusable? to false



522
523
524
# File 'lib/roby/task.rb', line 522

def do_not_reuse
    @reusable = false
end

#do_poll(plan) ⇒ 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 method used to register the poll blocks in the engine execution cycle



1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
# File 'lib/roby/task.rb', line 1079

def do_poll(plan) # :nodoc:
    return unless self_owned?
    # Don't call if we are terminating
    return if finished?
    # Don't call if we already had an error in the poll block
    return if event(:internal_error).emitted?

    begin
        while execute_block = @execute_handlers.pop
            execute_block.block.call(self)
        end

        if respond_to?(:poll_handler)
            poll_handler
        end
        
        if machine = state_machine
           machine.do_poll(self)
        end

        @poll_handlers.each do |poll_block|
            poll_block.block.call(self)
        end
    rescue LocalizedError => e
        execution_engine.add_error(e)
    rescue Exception => e
        execution_engine.add_error(CodeError.new(e, self))
    end
end

#each_coordination_object {|object| ... } ⇒ Object

Enumerate the coordination objects currently attached to this task

Yield Parameters:



1608
1609
1610
# File 'lib/roby/task.rb', line 1608

def each_coordination_object(&block)
    @coordination_objects.each(&block)
end

#each_event(only_wrapped = true) {|generator| ... } ⇒ Object Also known as: each_plan_child

Iterates on all the events defined for this task

Parameters:

  • only_wrapped (Boolean) (defaults to: true)

    For consistency with transaction proxies. Should not be used in user code.

Yields:

  • (generator)

Yield Parameters:

Returns:

  • self



856
857
858
859
860
861
862
# File 'lib/roby/task.rb', line 856

def each_event(only_wrapped = true)
    return enum_for(__method__, only_wrapped) if !block_given?
    for ev in bound_events.each_value
        yield(ev)
    end
    self
end

#each_exception_handler(&iterator) ⇒ Object

Lists all exception handlers attached to this task



1239
1240
1241
# File 'lib/roby/task.rb', line 1239

def each_exception_handler(&iterator)
    model.each_exception_handler(&iterator)
end

#emit(event_model, *context) ⇒ Object

Deprecated.

use EventGenerator#emit instead (e.g. task.start_event.emit)



786
787
788
789
790
# File 'lib/roby/task.rb', line 786

def emit(event_model, *context)
    Roby.warn_deprecated "Roby::Task#emit(event_name) is deprecated, use EventGenerator#emit (e.g. task.start_event.emit or task.event(:start).emit)"
    event(event_model).emit(*context)
    self
end

#end_timeObject

Returns when this task has finished



370
371
372
373
374
# File 'lib/roby/task.rb', line 370

def end_time
    if ev = stop_event.last
        ev.time
    end
end

#ensure_poll_handler_calledObject

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.

Helper for #execute and #poll that ensures that the #do_poll is called by the execution engine



1069
1070
1071
1072
1073
# File 'lib/roby/task.rb', line 1069

def ensure_poll_handler_called
    if !transaction_proxy? && running?
        @poll_handler_id ||= execution_engine.add_propagation_handler(description: "poll block for #{self}", type: :external_events, &method(:do_poll))
    end
end

#event(event_model) ⇒ TaskEventGenerator?

Returns the event generator by its name or model

Parameters:

  • name (Symbol)

    the event name

Returns:

Raises:

  • (ArgumentError)

    if the event does not exist



775
776
777
778
779
780
781
# File 'lib/roby/task.rb', line 775

def event(event_model)
    if event = find_event(event_model)
        event
    else
        raise ArgumentError, "cannot find #{event_model} in the set of bound events in #{self}. Known events are #{bound_events}."
    end
end

#event_model(model) ⇒ Model<TaskEvent>

Accesses an event model

This method gives access to this task’s event models. If given a name, it returns the corresponding event model. If given an event model, it verifies that the model is part of the events of self and returns it.

Returns:

  • (Model<TaskEvent>)

    a subclass of Roby::TaskEvent

Raises:

  • (ArgumentError)

    if the provided event name or model does not exist on self



877
878
879
# File 'lib/roby/task.rb', line 877

def event_model(model)
    self.model.event_model(model)
end

#executable=(flag) ⇒ Object

Set the executable flag. executable cannot be set to false if the task is running, and cannot be set to true on a finished task.



456
457
458
459
460
461
462
463
464
465
# File 'lib/roby/task.rb', line 456

def executable=(flag)
    return if flag == @executable
    return unless self_owned?
    if flag && !pending? 
        raise ModelViolation, "cannot set the executable flag of #{self} since it is not pending"
    elsif !flag && running?
        raise ModelViolation, "cannot unset the executable flag of #{self} since it is running"
    end
    super
end

#executable?Boolean

True if this task is executable. A task is not executable if it is abstract or partially instanciated.

Returns:

  • (Boolean)

See Also:



444
445
446
447
448
449
450
# File 'lib/roby/task.rb', line 444

def executable?
    if @executable == true
        true
    elsif @executable.nil?
        (!abstract? && !partially_instanciated? && super)
    end
end

#execute(options = Hash.new, &block) ⇒ void

This method returns an undefined value.

Add a block that is going to be executed once, either at the next cycle if the task is already running, or when the task is started

Parameters:

  • options (Hash) (defaults to: Hash.new)

    a customizable set of options

Options Hash (options):

  • :on_replace (:copy, :drop)

    defines the behaviour when this object gets replaced in the plan. If :copy is used, the handler is added to the replacing task and is also kept in the original task. If :drop, it is not copied (but is kept).



1031
1032
1033
1034
1035
1036
1037
1038
# File 'lib/roby/task.rb', line 1031

def execute(options = Hash.new, &block)
    default_on_replace = if abstract? then :copy else :drop end
    options = InstanceHandler.validate_options(options, on_replace: default_on_replace)

    check_arity(block, 1)
    @execute_handlers << InstanceHandler.new(block, (options[:on_replace] == :copy))
    ensure_poll_handler_called
end

#failed_to_start!(reason, time = Time.now) ⇒ Object



565
566
567
568
569
570
571
# File 'lib/roby/task.rb', line 565

def failed_to_start!(reason, time = Time.now)
    mark_failed_to_start(reason, time)
    each_event do |ev|
        ev.unreachable!(reason)
    end
    execution_engine.log(:task_failed_to_start, self, reason)
end

#failed_to_start?Boolean

Returns:

  • (Boolean)


548
# File 'lib/roby/task.rb', line 548

def failed_to_start?; @failed_to_start end

#find_event(name) ⇒ TaskEventGenerator?

Returns the event generator by its name

Parameters:

  • name (Symbol)

    the event name

Returns:



765
766
767
768
# File 'lib/roby/task.rb', line 765

def find_event(name)
    bound_events[name] ||
        bound_events[event_model(name).symbol]
end

#fired_event(event) ⇒ Object

Hook called by TaskEventGenerator#fired when one of this task’s events has been fired.



694
695
696
697
# File 'lib/roby/task.rb', line 694

def fired_event(event)
    history << event
    update_task_status(event)
end

#forcefully_terminateObject

“Simply” mark this task as terminated. This is meant to be used on quarantined tasks in tests.

Do not use this unless you really know what you are doing



1212
1213
1214
# File 'lib/roby/task.rb', line 1212

def forcefully_terminate
    update_task_status(event(:stop).new([]))
end

#forward_to(event_model, to, *to_task_events) ⇒ Object

Deprecated.

use EventGenerator#forward_to instead (e.g. task.start_event.forward_to other_task.stop_event)



824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
# File 'lib/roby/task.rb', line 824

def forward_to(event_model, to, *to_task_events)
    Roby.warn_deprecated "Task#forward_to is deprecated, use EventGenerator#forward_to instead (e.g. #{event_model}_event.forward_to other_event)"

    generator = event(event_model)
    if Hash === to_task_events.last
        delay = to_task_events.pop
    end
    to_events = case
                when Task
                    to_task_events.map { |ev| to.event(ev) }
                when EventGenerator then [to]
                else
                    raise ArgumentError, "expected Task or EventGenerator, got #{to}(#{to.class}: #{to.class.ancestors})"
                end

    to_events.each do |ev|
        generator.forward_to ev, delay
    end
end

#freeze_delayed_argumentsObject

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.

Evaluate delayed arguments, and replace in #arguments the ones that currently have a value



119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/roby/task.rb', line 119

def freeze_delayed_arguments
    if !arguments.static?
        result = Hash.new
        arguments.each do |key, value|
            if TaskArguments.delayed_argument?(value)
                catch(:no_value) do
                    result[key] = value.evaluate_delayed_argument(self)
                end
            end
        end
        assign_arguments(**result)
    end
end

#fullfills?(models, args = nil) ⇒ Boolean

Whether this task instance provides a set of models and arguments

The fullfills? predicate checks if this task can be used to fullfill the need of the given model and arguments The default is to check if

* the needed task model is an ancestor of this task
* the task 
* +args+ is included in the task arguments

Returns:

  • (Boolean)


1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
# File 'lib/roby/task.rb', line 1149

def fullfills?(models, args = nil)
    if models.kind_of?(Roby::Task)
        args ||= models.meaningful_arguments
        models = models.model
    end
    if !model.fullfills?(models)
        return false
    end

    if args
        args.each do |key, name|
            if self.arguments[key] != name
                return false
            end
        end
    end

    true
end

#fully_instanciated?Boolean

True if all arguments defined by Task.argument on the task model are either explicitely set or have a default value.

Returns:

  • (Boolean)


486
487
488
489
490
491
492
# File 'lib/roby/task.rb', line 486

def fully_instanciated?
    if arguments.static?
        @fully_instanciated ||= list_unset_arguments.empty?
    else
        list_unset_arguments.empty?
    end
end

#garbage!Object



531
532
533
534
# File 'lib/roby/task.rb', line 531

def garbage!
    bound_events.each_value(&:garbage!)
    super
end

#goalObject



31
32
33
# File 'lib/roby/state/task.rb', line 31

def goal
    @goal ||= GoalSpace.new(self.model.goal)
end

#handle_exception(e) ⇒ 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.

Handles the given exception.

In addition to the exception handlers provided by ExceptionHandlingObject, it checks for repair tasks (as defined by TaskStructure::ErrorHandling)

Parameters:



1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
# File 'lib/roby/task.rb', line 1227

def handle_exception(e)
    return if !plan

    tasks = find_all_matching_repair_tasks(e)
    return super if tasks.empty?
    if !tasks.any? { |t| t.running? }
        tasks.first.start!
    end
    true
end

#has_argument?(key) ⇒ Boolean

True if this model requires an argument named key and that argument is set

Returns:

  • (Boolean)


1171
1172
1173
# File 'lib/roby/task.rb', line 1171

def has_argument?(key)
    self.arguments.set?(key)
end

#has_event?(event_model) ⇒ Boolean

True if this task has an event of the required model. The event model can either be a event class or an event name.

Returns:

  • (Boolean)


500
501
502
# File 'lib/roby/task.rb', line 500

def has_event?(event_model)
    bound_events.has_key?(event_model)
end

#initialize_copy(old) ⇒ Object

:nodoc:



387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
# File 'lib/roby/task.rb', line 387

def initialize_copy(old) # :nodoc:
    super

    @name    = nil

    @arguments = TaskArguments.new(self)
    arguments.force_merge! old.arguments
    arguments.instance_variable_set(:@task, self)

    @instantiated_model_events = false

    # Create all event generators
    @bound_events = Hash.new
    @execute_handlers = old.execute_handlers.dup
    @poll_handlers = old.poll_handlers.dup
    if m = old.instance_variable_get(:@fullfilled_model)
        @fullfilled_model = m.dup
    end
end

#initialize_replacement(task) ⇒ 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.



1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
# File 'lib/roby/task.rb', line 1451

def initialize_replacement(task)
    super

    execute_handlers.each do |handler|
        if handler.copy_on_replace?
            task.execute(handler.as_options, &handler.block)
        end
    end

    poll_handlers.each do |handler|
        if handler.copy_on_replace?
            task.poll(handler.as_options, &handler.block)
        end
    end
end

#inspectObject



151
152
153
154
155
156
157
158
159
160
# File 'lib/roby/task.rb', line 151

def inspect
    state = if pending? then 'pending'
            elsif failed_to_start? then 'failed to start'
            elsif starting? then 'starting'
            elsif running? then 'running'
            elsif finishing? then 'finishing'
            else 'finished'
            end
    "#<#{to_s} executable=#{executable?} state=#{state} plan=#{plan.to_s}>"
end

#interruptible?Boolean

Returns true if this task’s stop event is controlable

Returns:

  • (Boolean)


453
# File 'lib/roby/task.rb', line 453

def interruptible?; stop_event.controlable? end

#invalidate_terminal_flagObject



622
# File 'lib/roby/task.rb', line 622

def invalidate_terminal_flag; @terminal_flag_invalid = true end

#invalidated_terminal_flag?Boolean

Returns:

  • (Boolean)


621
# File 'lib/roby/task.rb', line 621

def invalidated_terminal_flag?; !!@terminal_flag_invalid end

#last_eventTaskEvent?

The last event emitted by this task

Returns:



379
380
381
# File 'lib/roby/task.rb', line 379

def last_event
    history.last
end

#lifetimeObject

Returns for how many seconds this task is running. Returns nil if the task is not running.



354
355
356
357
358
359
360
# File 'lib/roby/task.rb', line 354

def lifetime
    if running?
        Time.now - start_time
    elsif finished?
        end_time - start_time
    end
end

#list_unset_argumentsObject

Lists all arguments, that are set to be needed via the :argument syntax but are not set.

This is needed for debugging purposes.



471
472
473
474
475
476
477
478
479
480
481
482
# File 'lib/roby/task.rb', line 471

def list_unset_arguments # :nodoc:
    actual_arguments =
        if arguments.static?
            arguments
        else
            arguments.evaluate_delayed_arguments
        end

    model.arguments.find_all do |name|
        !actual_arguments.has_key?(name)
    end
end

#mark_failed_to_start(reason, time) ⇒ Object



550
551
552
553
554
555
556
557
558
559
560
561
562
563
# File 'lib/roby/task.rb', line 550

def mark_failed_to_start(reason, time)
    if failed_to_start?
        return
    elsif !pending? && !starting?
        raise Roby::InternalError, "#{self} is neither pending nor starting, cannot mark as failed_to_start!"
    end
    @failed_to_start = true
    @failed_to_start_time = time
    @failure_reason = reason
    @pending  = false
    @starting = false
    @failed   = true
    plan.task_index.set_state(self, :failed?)
end

#matchQueries::TaskMatcher

Return a task match object that matches self



1601
1602
1603
# File 'lib/roby/task.rb', line 1601

def match
    self.class.match.with_instance(self)
end

#meaningful_arguments(task_model = self.model) ⇒ Object

The part of #arguments that is meaningful for this task model. I.e. it returns the set of elements in #arguments that are listed in the task model



111
112
113
# File 'lib/roby/task.rb', line 111

def meaningful_arguments(task_model = self.model)
    task_model.meaningful_arguments(arguments)
end

#nameString

The task name

Returns:

  • (String)


136
137
138
139
140
141
142
143
# File 'lib/roby/task.rb', line 136

def name
    return @name if @name
    name = "#{model.name || self.class.name}:0x#{address.to_s(16)}"
    if !frozen?
        @name = name
    end
    name
end

#null?Boolean

True if this task is a null task. See NullTask.

Returns:

  • (Boolean)


918
# File 'lib/roby/task.rb', line 918

def null?; false end

#on(event_model, options = Hash.new, &user_handler) ⇒ Object

Deprecated.

use Roby::TaskEventGenerator#on on the event object, e.g. task.start_event.on { |event| … }



795
796
797
798
799
# File 'lib/roby/task.rb', line 795

def on(event_model, options = Hash.new, &user_handler)
    Roby.warn_deprecated "Task#on is deprecated, use EventGenerator#on instead (e.g. #{event_model}_event.signals other_event)"
    event(event_model).on(options, &user_handler)
    self
end

#partially_instanciated?Boolean

True if at least one argument required by the task model is not set. See Task.argument.

Returns:

  • (Boolean)


496
# File 'lib/roby/task.rb', line 496

def partially_instanciated?; !fully_instanciated? end

#plan=(new_plan) ⇒ Object

:nodoc:



407
408
409
410
411
412
413
414
415
416
# File 'lib/roby/task.rb', line 407

def plan=(new_plan) # :nodoc:
    super

    @relation_graphs =
        if plan then plan.task_relation_graphs
        end
    for ev in bound_events.each_value
        ev.plan = plan
    end
end

#poll(options = Hash.new) {|task| ... } ⇒ Object

Adds a new poll block on this instance

Parameters:

  • options (Hash) (defaults to: Hash.new)

    a customizable set of options

Options Hash (options):

  • :on_replace (:copy, :drop)

    defines the behaviour when this object gets replaced in the plan. If :copy is used, the handler is added to the replacing task and is also kept in the original task. If :drop, it is not copied (but is kept).

Yield Parameters:

  • task (Roby::Task)

    the task on which the poll block is executed. It might be different than the one on which it has been added because of replacements.

Returns:



1047
1048
1049
1050
1051
1052
1053
1054
1055
# File 'lib/roby/task.rb', line 1047

def poll(options = Hash.new, &block)
    default_on_replace = if abstract? then :copy else :drop end
    options = InstanceHandler.validate_options(options, on_replace: default_on_replace)
    
    check_arity(block, 1)
    @poll_handlers << (handler = InstanceHandler.new(block, (options[:on_replace] == :copy)))
    ensure_poll_handler_called
    handler
end

#pretty_print(pp, with_owners = true) ⇒ Object

:nodoc:



894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
# File 'lib/roby/task.rb', line 894

def pretty_print(pp, with_owners = true) # :nodoc:
    pp.text "#{model.name}:0x#{self.address.to_s(16)}"
    if with_owners
        pp.nest(2) do
            pp.breakable
            pp.text "owners: "
            pp.nest(2) do
                pp.seplist(owners) { |r| pp.text r.to_s }
            end
        end
    end
    pp.nest(2) do
        pp.breakable
        pp.text "arguments: "
        if !arguments.empty?
            pp.nest(2) do
                pp.breakable
                arguments.pretty_print(pp)
            end
        end
    end
end

#promise(description: "#{self}.promise", executor: promise_executor, &block) ⇒ Promise

Create a promise that is serialized with all promises created for this object

Parameters:

  • description (String) (defaults to: "#{self}.promise")

    a textual description of the promise’s role (used for debugging and timing)

Returns:

Raises:

  • (PromiseInFinishedTask)

    if attempting to create a promise on a task that is either finished, or failed to start



343
344
345
346
347
348
349
350
# File 'lib/roby/task.rb', line 343

def promise(description: "#{self}.promise", executor: promise_executor, &block)
    if failed_to_start?
        raise PromiseInFinishedTask, "attempting to create a promise on #{self} that has failed to start"
    elsif finished?
        raise PromiseInFinishedTask, "attempting to create a promise on #{self} that is finished"
    end
    super
end

#quarantined!Object

Mark the task as quarantined

Once set it cannot be unset



544
545
546
# File 'lib/roby/task.rb', line 544

def quarantined!
    @quarantined = true
end

#quarantined?Object

Whether this task has been quarantined



539
# File 'lib/roby/task.rb', line 539

attr_predicate :quarantined?

Returns the set of events directly related to this task



668
669
670
671
672
673
674
675
# File 'lib/roby/task.rb', line 668

def related_events(result = Set.new)
    each_event do |ev|
        ev.related_events(result)
    end

    result.reject { |ev| ev.respond_to?(:task) && ev.task == self }.
        to_set
end

Returns the set of tasks directly related to this task, either because of task relations or because of task events that are related to other task events



658
659
660
661
662
663
664
665
# File 'lib/roby/task.rb', line 658

def related_tasks(result = Set.new)
    result = related_objects(nil, result)
    each_event do |ev|
        ev.related_tasks(result)
    end

    result
end

#remove_coordination_object(object) ⇒ 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.

Declare that a coordination object is no longer attached to this task

Parameters:



1626
1627
1628
# File 'lib/roby/task.rb', line 1626

def remove_coordination_object(object)
    @coordination_objects.delete(object)
end

#remove_poll_handler(handler) ⇒ void

This method returns an undefined value.

Remove a poll handler from this instance

Parameters:



1061
1062
1063
# File 'lib/roby/task.rb', line 1061

def remove_poll_handler(handler)
    @poll_handlers.delete(handler)
end

#replace_by(object) ⇒ Object

Replaces self by object

It replaces self by object in all relations self is part of, and do the same for the task’s event generators.



1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
# File 'lib/roby/task.rb', line 1433

def replace_by(object)
    event_mappings = Hash.new
    event_resolver = ->(e) { object.event(e.symbol) }
    each_event do |ev|
        event_mappings[ev] = [nil, event_resolver]
    end
    object.each_event do |ev|
        event_mappings[ev] = nil
    end
    plan.replace_subplan(Hash[self => object, object => nil], event_mappings)

    initialize_replacement(object)
    each_event do |event|
        event.initialize_replacement(nil) { object.event(event.symbol) }
    end
end

#replace_subplan_by(object) ⇒ Object

Replaces self’s subplan by another subplan

Replaces the subplan generated by self by the one generated by object. In practice, it means that we transfer all parent edges whose target is self from the receiver to object. It calls the various add/remove hooks defined in DirectedRelationSupport.

Relations to free events are not copied during replacement

See Also:



1377
1378
1379
1380
1381
1382
1383
1384
1385
# File 'lib/roby/task.rb', line 1377

def replace_subplan_by(object)
    edges = compute_subplan_replacement_operation(object)
    apply_replacement_operations(edges)

    initialize_replacement(object)
    each_event do |event|
        event.initialize_replacement(object.event(event.symbol))
    end
end

#resolve_goalsObject



35
36
37
38
39
40
# File 'lib/roby/state/task.rb', line 35

def resolve_goals
    if !fully_instanciated?
        raise ArgumentError, "cannot resolve goals on a task that is not fully instanciated"
    end
    self.model.goal.resolve_goals(self, self.goal)
end

#resolve_state_sourcesObject



17
18
19
# File 'lib/roby/state/task.rb', line 17

def resolve_state_sources
    model.state.resolve_data_sources(self, state)
end

#respawnObject

Create a new task of the same model and with the same arguments than this one. Insert this task in the plan and make it replace the fresh one.

See Plan#respawn



1274
1275
1276
# File 'lib/roby/task.rb', line 1274

def respawn
    plan.respawn(self)
end

#reusable?Boolean

True if this task can be reused by some other parts in the plan

Returns:

  • (Boolean)


527
528
529
# File 'lib/roby/task.rb', line 527

def reusable?
    plan && @reusable && !quarantined? && !garbage? && !failed_to_start? && !finished? && !finishing?
end

#running?Boolean

True if this task is currently running (i.e. is has already started, and is not finished)

Returns:

  • (Boolean)


511
# File 'lib/roby/task.rb', line 511

def running?; started? && !finished? end

#script(options = Hash.new, &block) ⇒ Object

Adds a task script that is going to be executed while this task instance runs.



277
278
279
280
281
282
283
284
# File 'lib/roby/coordination/task_script.rb', line 277

def script(options = Hash.new, &block)
    execute do |task|
        script = model.create_script(task, &block)
        script.prepare
        script.step
    end
    model.create_script(self, &block)
end

#signals(event_model, to, *to_task_events) ⇒ Object

Deprecated.

use EventGenerator#signal instead (e.g. task.start_event.signal other_task.stop_event)



802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
# File 'lib/roby/task.rb', line 802

def signals(event_model, to, *to_task_events)
    Roby.warn_deprecated "Task#signals is deprecated, use EventGenerator#signal instead (e.g. #{event_model}_event.signals other_event)"

    generator = event(event_model)
    if Hash === to_task_events.last
        delay = to_task_events.pop
    end
    to_events = case to
                when Task
                    to_task_events.map { |ev_model| to.event(ev_model) }
                when EventGenerator then [to]
                else
                    raise ArgumentError, "expected Task or EventGenerator, got #{to}(#{to.class}: #{to.class.ancestors})"
                end

    to_events.each do |event|
        generator.signals event, delay
    end
    self
end

#simulateObject

Deprecated.

this has no equivalent. It really has never seen proper support



1468
1469
1470
1471
1472
# File 'lib/roby/task.rb', line 1468

def simulate
    simulation_task = self.model.simulation_model.new(arguments.to_hash)
    plan.force_replace(self, simulation_task)
    simulation_task
end

#start_timeObject

Returns when this task has been started



363
364
365
366
367
# File 'lib/roby/task.rb', line 363

def start_time
    if ev = start_event.last
        ev.time
    end
end

#stateObject



13
14
15
# File 'lib/roby/state/task.rb', line 13

def state
    @state ||= StateSpace.new(self.model.state)
end

#terminal_eventsArray<TaskEventGenerator>

Returns this task’s set of terminal events.

A terminal event is an event whose emission announces the end of the task. In most case, it is an event which is forwarded directly on indirectly to stop.

Returns:



872
873
874
# File 'lib/roby/task.rb', line 872

def terminal_events
    bound_events.each_value.find_all { |ev| ev.terminal? }
end

#to_execution_exceptionObject



1589
1590
1591
# File 'lib/roby/task.rb', line 1589

def to_execution_exception
    ExecutionException.new(LocalizedError.new(self))
end

#to_sObject

:nodoc:



881
882
883
884
885
886
887
888
889
890
891
892
# File 'lib/roby/task.rb', line 881

def to_s # :nodoc:
    s = "#{name}<id:#{droby_id.id}>(#{arguments})"
    id = owners.map do |owner|
        next if plan && (owner == plan.local_owner)
        sibling = remote_siblings[owner]
        "#{sibling ? Object.address_from_id(sibling.ref).to_s(16) : 'nil'}@#{owner.remote_name}"
    end
    unless id.empty?
        s << "[" << id.join(",") << "]"
    end
    s
end

#to_taskObject

Converts this object into a task object



920
# File 'lib/roby/task.rb', line 920

def to_task; self end

#transition!Object



286
287
288
# File 'lib/roby/coordination/task_script.rb', line 286

def transition!
    poll_transition_event.emit
end

#update_task_status(event) ⇒ Object

Call to update the task status because of event



724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
# File 'lib/roby/task.rb', line 724

def update_task_status(event) # :nodoc:
    if event.symbol == :start
        plan.task_index.set_state(self, :running?)
        @starting = false
        @pending  = false
        @started  = true
        @running  = true
        @executable = true
    end

    if event.success?
        plan.task_index.add_state(self, :success?)
        @success = true
    elsif event.failure?
        plan.task_index.add_state(self, :failed?)
        @failed = true
        @failure_reason ||= event
        @failure_event  ||= event
    end

    if event.terminal?
        @terminal_event ||= event
    end
    
    if event.symbol == :stop
        plan.task_index.remove_state(self, :running?)
        plan.task_index.add_state(self, :finished?)
        @running    = false
        @finishing  = false
        @finished   = true
        @executable = false
    end
end

#update_terminal_flagObject

Updates the terminal flag for all events in the task. An event is terminal if the stop event of the task will be called because this event is.



627
628
629
630
631
632
633
634
# File 'lib/roby/task.rb', line 627

def update_terminal_flag # :nodoc:
    return if !invalidated_terminal_flag?
    terminal_events, success_events, failure_events =
        self.model.compute_terminal_events(bound_events)
    apply_terminal_flags(terminal_events, success_events, failure_events)
    @terminal_flag_invalid = false
    return terminal_events, success_events, failure_events
end

#updated_dataObject

This hook is called whenever the internal data of this task is updated. See #data, #data= and the updated_data event



1002
1003
# File 'lib/roby/task.rb', line 1002

def updated_data
end

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

Declares that this fault response table should be made active when this task starts, and deactivated when it ends



1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
# File 'lib/roby/task.rb', line 1129

def use_fault_response_table(table_model, arguments = Hash.new)
    arguments = table_model.validate_arguments(arguments)

    table = nil
    execute do |task|
        table = task.plan.use_fault_response_table(table_model, arguments)
    end
    stop_event.on do |event|
        plan.remove_fault_response_table(table)
    end
end

#when_finalized(options = Hash.new, &block) ⇒ Object

Register a hook that is called when this task is finalized (removed from its plan)

Parameters:

  • options (Hash) (defaults to: Hash.new)

    a customizable set of options

Options Hash (options):

  • :on_replace (:copy, :drop)

    defines the behaviour when this object gets replaced in the plan. If :copy is used, the handler is added to the replacing task and is also kept in the original task. If :drop, it is not copied (but is kept).



1493
1494
1495
1496
1497
# File 'lib/roby/task.rb', line 1493

def when_finalized(options = Hash.new, &block)
    default = if abstract? then :copy else :drop end
    options, remaining = InstanceHandler.filter_options options, on_replace: default
    super(options.merge(remaining), &block)
end

#|(task) ⇒ Object

Creates a parallel aggregation between self and task. Both tasks are started at the same time, and the returned instance finishes when both tasks are finished. The returned value is an instance of Parallel.

Note that this operator always creates a new Parallel object, so

a | b | c | d

will create three instances of Parallel. If more than two tasks should be organized that way, one should instead use Parallel#<<:

Parallel.new << a << b << c << d


1579
1580
1581
1582
1583
1584
1585
1586
1587
# File 'lib/roby/task.rb', line 1579

def |(task)
    if self.null?
        task
    elsif task.null?
        self
    else
        Tasks::Parallel.new << self << task
    end
end