Class: Roby::TaskEventGenerator

Inherits:
EventGenerator show all
Includes:
DRoby::V5::TaskEventGeneratorDumper, GUI::GraphvizTaskEventGenerator, GUI::RelationsCanvasTaskEventGenerator
Defined in:
lib/roby/task_event_generator.rb,
lib/roby/task_structure/error_handling.rb,
lib/roby/droby/enable.rb

Overview

Specialization of EventGenerator to represent task events

It gives access to the task-specific information (associated task, event name, …)

Instance Attribute Summary collapse

Attributes inherited from EventGenerator

#command, #event_model, #history, #unreachability_reason, #unreachable_handlers

Attributes inherited from PlanObject

#addition_time, #executable, #execution_engine, #finalization_handlers, #finalization_time, #model, #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

Instance Method Summary collapse

Methods included from DRoby::V5::TaskEventGeneratorDumper

#droby_dump

Methods included from GUI::GraphvizTaskEventGenerator

#dot_label

Methods included from GUI::GraphvizPlanObject

#apply_layout, #dot_label, #to_dot

Methods included from GUI::RelationsCanvasTaskEventGenerator

#display, #display_name, #display_parent

Methods included from GUI::RelationsCanvasEventGenerator

#display, #display_create, #display_name, #display_time_end, #display_time_start, priorities, style, styles

Methods included from GUI::RelationsCanvasPlanObject

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

Methods inherited from EventGenerator

#&, #achieve_asynchronously, #add_child_object, #call, #call_handlers, #call_unreachable_handlers, #call_without_propagation, #calling, #cancel, #delay, #emit, #emit_without_propagation, #emitting, #filter, #finalized!, #forward, #forward_once, #forward_to, #forward_to_once, #forwarded_to?, #garbage!, #happened?, #if_unreachable, #initialize_copy, #initialize_replacement, #last, #mark_unreachable!, match, #model, #name, #once, #precondition, #realize_with, #related_events, #replace_by, #signal, #signals, #signals_once, #to_event, #to_execution_exception, #to_execution_exception_matcher, #unreachable!, #unreachable_without_propagation, #until, #when_unreachable, #|

Methods included from DRoby::Identifiable

#droby_id, #initialize_copy

Methods included from DRoby::V5::DRobyConstant::Dump

#droby_dump, #droby_marshallable?

Methods included from DRoby::V5::EventGeneratorDumper

#droby_dump

Methods inherited from PlanObject

#add_child_object, #apply_relation_changes, #as_plan, #can_finalize?, #commit_transaction, #concrete_model, #connection_space, #each_finalization_handler, #each_in_neighbour_merged, #each_out_neighbour_merged, #each_plan_child, #engine, #executable?, #finalized!, #finalized?, #forget_peer, #fullfills?, #garbage!, #garbage?, #initialize_copy, #initialize_replacement, #merged_relations, #promise, #read_write?, #real_object, #remotely_useful?, #replace_by, #replace_subplan_by, #root_object, #root_object?, #subscribed?, #transaction_proxy?, #transaction_stack, #update_on?, #updated_by?, #when_finalized

Methods included from Models::PlanObject

#child_plan_object, #finalization_handler, #when_finalized

Methods included from Relations::DirectedRelationSupport

#[], #[]=, #add_child_object, #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, #initialize_copy, #owned_by?, #remove_owner

Constructor Details

#initialize(task, model) ⇒ TaskEventGenerator



19
20
21
22
23
24
# File 'lib/roby/task_event_generator.rb', line 19

def initialize(task, model)
    super(model.respond_to?(:call), plan: task.plan)
    @task = task
    @event_model = model
    @symbol = model.symbol
end

Instance Attribute Details

#planObject

The event plan. It is the same as task.plan and is actually updated by task.plan=. It is redefined here for performance reasons.



43
44
45
# File 'lib/roby/task_event_generator.rb', line 43

def plan
  @plan
end

#symbolObject (readonly)

The event symbol (its name as a Symbol object)



12
13
14
# File 'lib/roby/task_event_generator.rb', line 12

def symbol
  @symbol
end

#taskObject (readonly)

The task we are part of



10
11
12
# File 'lib/roby/task_event_generator.rb', line 10

def task
  @task
end

#terminal_flagObject

Returns the value for #terminal_flag, updating it if needed



130
131
132
133
# File 'lib/roby/task_event_generator.rb', line 130

def terminal_flag # :nodoc:
    task.update_terminal_flag if task.invalidated_terminal_flag?
    @terminal_flag
end

Instance Method Details

#achieve_with(obj) ⇒ Object

See EventGenerator#achieve_with



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/roby/task_event_generator.rb', line 214

def achieve_with(obj) # :nodoc:
    child_task, child_event =
        case obj
        when Roby::Task then [obj, obj.event(:success)]
        when Roby::TaskEventGenerator then [obj.task, obj]
        end

    if child_task
        unless task.depends_on?(child_task)
            task.depends_on(
                child_task,
                success: [child_event.symbol],
                remove_when_done: true
            )
        end
        super(child_event)
    else
        super(obj)
    end
end

#added_forwarding(child, info) ⇒ Object

Invalidates the task’s terminal flag when the Forwarding and/or the Signal relation gets modified.



170
171
172
173
# File 'lib/roby/task_event_generator.rb', line 170

def added_forwarding(child, info) # :nodoc:
    super
    invalidate_task_terminal_flag_if_needed(child)
end

#added_signal(child, info) ⇒ Object

Invalidates the task’s terminal flag when the Forwarding and/or the Signal relation gets modified.



163
164
165
166
# File 'lib/roby/task_event_generator.rb', line 163

def added_signal(child, info) # :nodoc:
    super
    invalidate_task_terminal_flag_if_needed(child)
end

#called(context) ⇒ Object



75
76
77
78
79
# File 'lib/roby/task_event_generator.rb', line 75

def called(context)
    super

    task.finishing = true if terminal? && pending?
end

#check_call_validityObject

Checks that the event can be called. Raises various exception when it is not the case.



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
276
277
278
279
# File 'lib/roby/task_event_generator.rb', line 243

def check_call_validity
    if (error = super) && !error.kind_of?(UnreachableEvent)
        return refine_call_exception(error)
    end

    if task.failed_to_start?
        CommandRejected.new(self).exception(
            "#{symbol}! called by #{execution_engine.propagation_sources.to_a} "\
            "but the task has failed to start: #{task.failure_reason}"
        )
    elsif task.event(:stop).emitted?
        CommandRejected.new(self).exception(
            "#{symbol}! called by #{execution_engine.propagation_sources.to_a} "\
            "but the task has finished. Task has been terminated by "\
            "#{task.event(:stop).history.first.sources.to_a}."
        )
    elsif task.finished? && !terminal?
        CommandRejected.new(self).exception(
            "#{symbol}! called by #{execution_engine.propagation_sources.to_a} "\
            "but the task has finished. Task has been terminated by "\
            "#{task.event(:stop).history.first.sources.to_a}."
        )
    elsif task.pending? && symbol != :start
        CommandRejected.new(self).exception(
            "#{symbol}! called by #{execution_engine.propagation_sources.to_a} "\
            "but the task has never been started"
        )
    elsif task.running? && symbol == :start
        CommandRejected.new(self).exception(
            "#{symbol}! called by #{execution_engine.propagation_sources.to_a} "\
            "but the task is already running. Task has been started by "\
            "#{task.event(:start).history.first.sources.to_a}."
        )
    else
        error
    end
end

#check_call_validity_after_callingObject



281
282
283
284
285
# File 'lib/roby/task_event_generator.rb', line 281

def check_call_validity_after_calling
    if (error = super)
        refine_call_exception(error)
    end
end

#check_emission_validityObject

Checks that the event can be emitted. Raises various exception when it is not the case.



289
290
291
292
293
294
295
# File 'lib/roby/task_event_generator.rb', line 289

def check_emission_validity # :nodoc:
    if (error = super)
        refine_emit_exception(error)
    else
        task.check_emission_validity(self)
    end
end

#clear_pendingObject



65
66
67
68
69
70
71
72
73
# File 'lib/roby/task_event_generator.rb', line 65

def clear_pending
    if @pending && symbol == :start && !emitted? && !task.failed_to_start?
        plan.task_index.set_state(task, :pending?) if plan && !plan.template?
        task.pending = true
        task.starting = false
    end

    super
end

#command=(block) ⇒ Object



32
33
34
35
36
# File 'lib/roby/task_event_generator.rb', line 32

def command=(block)
    event_model.singleton_class.class_eval do
        define_method(:call, &block)
    end
end

#controlable?Boolean

See EventGenerator#controlable?



122
123
124
# File 'lib/roby/task_event_generator.rb', line 122

def controlable? # :nodoc:
    event_model.controlable?
end

#create_transaction_proxy(transaction) ⇒ Object



372
373
374
375
# File 'lib/roby/task_event_generator.rb', line 372

def create_transaction_proxy(transaction)
    # Ensure that the task is proxied already
    transaction.wrap_task(task).event(symbol)
end

#default_command(context) ⇒ Object

The default command if the event is created with controlable: true. It emits the event on the task.



28
29
30
# File 'lib/roby/task_event_generator.rb', line 28

def default_command(context)
    event_model.call(task, context)
end

#default_on_replaceObject



361
362
363
364
365
366
# File 'lib/roby/task_event_generator.rb', line 361

def default_on_replace
    if task.abstract? then :copy
    else
        :drop
    end
end

#each_handler(&block) ⇒ Object

See EventGenerator#each_handler



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

def each_handler(&block) # :nodoc:
    task.model.each_handler(event_model.symbol, &block) if self_owned?
    super
end

#each_precondition(&block) ⇒ Object

See EventGenerator#each_precondition



116
117
118
119
# File 'lib/roby/task_event_generator.rb', line 116

def each_precondition(&block) # :nodoc:
    task.model.each_precondition(event_model.symbol, &block)
    super
end

#emit_failed(error = nil, message = nil) ⇒ Object



235
236
237
238
239
# File 'lib/roby/task_event_generator.rb', line 235

def emit_failed(error = nil, message = nil)
    exception = super
    task.failed_to_start!(exception) if symbol == :start && !task.failed_to_start?
    nil
end

#failure?Boolean

True if this event is either forwarded to or signals the task’s :failed event



146
147
148
# File 'lib/roby/task_event_generator.rb', line 146

def failure?
    terminal_flag == :failure
end

#fire(event) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/roby/task_event_generator.rb', line 81

def fire(event)
    super

    case symbol
    when :start
        task.do_poll(plan)
    when :stop
        task.each_event do |ev|
            ev.unreachable!(task.terminal_event)
        end
    end
end

#fired(event) ⇒ Object

Actually emits the event. This should not be used directly.

It forwards the call to Task#fire



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

def fired(event) # :nodoc:
    super
    task.fired_event(event)
end

#handle_with(repairing_task, remove_when_done: true) ⇒ Object

Mark this event as being handled by the task task



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/roby/task_structure/error_handling.rb', line 6

def handle_with(repairing_task, remove_when_done: true)
    if repairing_task.respond_to?(:as_plan)
        repairing_task = repairing_task.as_plan
    end

    unless task.child_object?(repairing_task, ErrorHandling)
        task.add_error_handler repairing_task, Set.new
    end

    if remove_when_done
        repairing_task.stop_event.on do |event|
            repairing_task = event.task
            if task.plan && task.child_object?(repairing_task, ErrorHandling)
                task.remove_error_handler(repairing_task)
            end
        end
    end

    task[repairing_task, ErrorHandling] << Roby::Queries::ExecutionExceptionMatcher.new.with_origin(task.model.find_event(symbol))
    repairing_task
end

#inspectObject

:nodoc:



197
198
199
# File 'lib/roby/task_event_generator.rb', line 197

def inspect # :nodoc:
    "#{task.inspect}/#{symbol}: #{history}"
end

#invalidate_task_terminal_flag_if_needed(child) ⇒ 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 for the signal and forward relation hooks that invalidates the events terminal flags when the event structure is changed



154
155
156
157
158
159
# File 'lib/roby/task_event_generator.rb', line 154

def invalidate_task_terminal_flag_if_needed(child)
    if child.respond_to?(:task) && child.task == task
        task.invalidate_terminal_flag
    end
    nil
end

#matchObject



377
378
379
# File 'lib/roby/task_event_generator.rb', line 377

def match
    Queries::TaskEventGeneratorMatcher.new(task, symbol)
end

#new(context, propagation_id = nil, time = nil) ⇒ Object

See EventGenerator#new



188
189
190
191
# File 'lib/roby/task_event_generator.rb', line 188

def new(context, propagation_id = nil, time = nil) # :nodoc:
    event_model.new(task, self, propagation_id ||
        execution_engine.propagation_id, context, time || Time.now)
end

#on(on_replace: default_on_replace, once: false, &block) ⇒ Object



368
369
370
# File 'lib/roby/task_event_generator.rb', line 368

def on(on_replace: default_on_replace, once: false, &block)
    super(on_replace: on_replace, once: once, &block)
end

#override_model(model) ⇒ Object

Changes the underlying model



15
16
17
# File 'lib/roby/task_event_generator.rb', line 15

def override_model(model)
    @event_model = model
end

#pending(sources) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
# File 'lib/roby/task_event_generator.rb', line 53

def pending(sources)
    super

    if symbol == :start
        task.freeze_delayed_arguments
        plan.task_index.set_state(task, :starting?) if plan && !plan.template?
        task.pending  = false
        task.starting = true
    end
    nil
end

#pretty_print(pp, context_task: nil) ⇒ Object

:nodoc:



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

def pretty_print(pp, context_task: nil) # :nodoc:
    pp.text "event '#{symbol}'"
    if !context_task || context_task != task
        pp.text " of"
        pp.nest(2) do
            pp.breakable
            task.pretty_print(pp)
        end
    end
    nil
end

#refine_call_exception(e) ⇒ Object

When an emissio and/or call exception is raised by the base EventGenerator methods, this method is used to transform it to the relevant task-related error.



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/roby/task_event_generator.rb', line 300

def refine_call_exception(e)
    if task.partially_instanciated?
        TaskEventNotExecutable.new(self).exception(
            "#{symbol}_event.call on #{task} which is partially instanciated\n"\
            "The following arguments were not set:\n  "\
            "#{task.list_unset_arguments.map(&:to_s).join('\n  ')}"
        )
    elsif !plan
        TaskEventNotExecutable.new(self).exception(
            "#{symbol}_event.call on #{task} but "\
            "the task has been removed from its plan"
        )
    elsif !plan.executable?
        TaskEventNotExecutable.new(self).exception(
            "#{symbol}_event.call on #{task} but its plan is not executable"
        )
    elsif task.abstract?
        TaskEventNotExecutable.new(self).exception(
            "#{symbol}_event.call on #{task} but the task is abstract"
        )
    elsif e.kind_of?(EventNotExecutable)
        TaskEventNotExecutable.new(self).exception(
            "#{symbol}_event.call on #{task} which is not executable"
        )
    else
        e
    end
end

#refine_emit_exception(e) ⇒ Object

When an emissio and/or call exception is raised by the base EventGenerator methods, this method is used to transform it to the relevant task-related error.



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/roby/task_event_generator.rb', line 332

def refine_emit_exception(e) # :nodoc:
    if task.partially_instanciated?
        TaskEventNotExecutable.new(self).exception(
            "#{symbol}_event.emit on #{task} which is partially instanciated\n"\
            "The following arguments were not set:\n  " +
            task.list_unset_arguments.map(&:to_s).join("\n")
        )
    elsif !plan
        TaskEventNotExecutable.new(self).exception(
            "#{symbol}_event.emit on #{task} but "\
            "the task has been removed from its plan"
        )
    elsif !plan.executable?
        TaskEventNotExecutable.new(self).exception(
            "#{symbol}_event.emit on #{task} but its plan is not executable"
        )
    elsif task.abstract?
        TaskEventNotExecutable.new(self).exception(
            "#{symbol}_event.emit on #{task} but the task is abstract"
        )
    elsif e.kind_of?(EventNotExecutable)
        TaskEventNotExecutable.new(self).exception(
            "#{symbol}_event.emit on #{task} which is not executable"
        )
    else
        e
    end
end

See EventGenerator#related_tasks



103
104
105
106
107
# File 'lib/roby/task_event_generator.rb', line 103

def related_tasks(result = nil) # :nodoc:
    tasks = super
    tasks.delete(task)
    tasks
end

#removed_forwarding(child) ⇒ Object



182
183
184
185
# File 'lib/roby/task_event_generator.rb', line 182

def removed_forwarding(child)
    super
    invalidate_task_terminal_flag_if_needed(child)
end

#removed_signal(child) ⇒ Object

Invalidates the task’s terminal flag when the Forwarding and/or the Signal relation gets modified.



177
178
179
180
# File 'lib/roby/task_event_generator.rb', line 177

def removed_signal(child)
    super
    invalidate_task_terminal_flag_if_needed(child)
end

#success?Boolean

True if this event is either forwarded to or signals the task’s :success event



141
142
143
# File 'lib/roby/task_event_generator.rb', line 141

def success?
    terminal_flag == :success
end

#terminal?Boolean

True if this event is either forwarded to or signals the task’s :stop event



136
137
138
# File 'lib/roby/task_event_generator.rb', line 136

def terminal?
    terminal_flag
end

#to_sObject

:nodoc:



193
194
195
# File 'lib/roby/task_event_generator.rb', line 193

def to_s # :nodoc:
    "#{task}/#{symbol}"
end