Class: Roby::Plan

Inherits:
BasicObject
Extended by:
Logger::Forward, Logger::Hierarchy
Includes:
Distributed::EventNotifications::PlanCacheCleanup, Distributed::PlanModificationHooks, EventGenerator::FinalizedEventHook, Log::PlanHooks, Roby::Propagation::RemoveDelayedOnFinalized, TaskStructure::ExecutionAgentSpawn, Transactions::PlanUpdates
Defined in:
lib/roby/plan.rb,
lib/roby.rb,
lib/roby/query.rb,
lib/roby/distributed/proxy.rb

Overview

A plan object is a collection of tasks and events. In plans, tasks can be missions (#missions, #mission?), which means that they are the final goals of the system. A task is useful if it helps into the realization of a mission (it is linked to a mission by #hierarchy_relation or one of the #service_relations), and is not useful otherwise. #garbage_collect removes the tasks that are not useful.

The following event hooks are defined:

* #inserted
* #discarded
* #discovered_tasks
* #discovered_events
* #replaced
* #added_transaction
* #removed_transaction
* #garbage
* #finalized_task
* #finalized_event

Direct Known Subclasses

Transaction

Defined Under Namespace

Classes: DRoby

Constant Summary

Constants included from Log::PlanHooks

Log::PlanHooks::HOOKS

Constants included from Log::BasicObjectHooks

Log::BasicObjectHooks::HOOKS

Instance Attribute Summary collapse

Attributes inherited from BasicObject

#distribute

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Distributed::PlanModificationHooks

discovered_objects, finalized_object

Methods included from Transactions::PlanUpdates

finalized_object

Methods inherited from BasicObject

#add_sibling_for, #distribute?, distribute?, #finalized?, #forget_peer, #has_sibling_on?, #initialize_copy, local_only, #read_write?, #remotely_useful?, #remove_sibling_for, #self_owned?, #sibling_of, #sibling_on, #subscribe, #subscribed?, #update_on?, #updated?, #updated_by?, #updated_peers

Methods included from Log::BasicObjectHooks

#added_owner, #removed_owner

Constructor Details

#initializePlan

Returns a new instance of Plan.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/roby/plan.rb', line 73

def initialize
    @missions  = ValueSet.new
    @keepalive   = ValueSet.new
    @known_tasks = ValueSet.new
    @free_events = ValueSet.new
    @task_events = ValueSet.new
    @force_gc    = ValueSet.new
    @gc_quarantine = ValueSet.new
    @transactions = ValueSet.new
    @repairs     = Hash.new

    @task_index  = Roby::TaskIndex.new

    super() if defined? super
end

Instance Attribute Details

#force_gcObject (readonly)

A set of tasks which are useful (and as such would not been garbage collected), but we want to GC anyway



55
56
57
# File 'lib/roby/plan.rb', line 55

def force_gc
  @force_gc
end

#free_eventsObject (readonly)

The list of events that are not included in a task



41
42
43
# File 'lib/roby/plan.rb', line 41

def free_events
  @free_events
end

#gc_quarantineObject (readonly)

A set of task for which GC should not be attempted, either because they are not interruptible or because their start or stop command failed



60
61
62
# File 'lib/roby/plan.rb', line 60

def gc_quarantine
  @gc_quarantine
end

#keepaliveObject (readonly)

directly, use #permanent and #auto instead.



44
45
46
# File 'lib/roby/plan.rb', line 44

def keepalive
  @keepalive
end

#known_tasksObject (readonly)

The list of tasks that are included in this plan



34
35
36
# File 'lib/roby/plan.rb', line 34

def known_tasks
  @known_tasks
end

#missionsObject (readonly)

The list of the robot’s missions. Do not change that set directly, use #insert and #discard instead.



39
40
41
# File 'lib/roby/plan.rb', line 39

def missions
  @missions
end

#repairsObject (readonly)

See also #add_repair and #remove_repair



51
52
53
# File 'lib/roby/plan.rb', line 51

def repairs
  @repairs
end

#task_eventsObject (readonly)

The set of events that are defined by #known_tasks



36
37
38
# File 'lib/roby/plan.rb', line 36

def task_events
  @task_events
end

#task_indexObject (readonly)

The task index for this plan



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

def task_index
  @task_index
end

#transactionsObject (readonly)

The set of transactions which are built on top of this plan



63
64
65
# File 'lib/roby/plan.rb', line 63

def transactions
  @transactions
end

Class Method Details

.can_gc?(task) ⇒ Boolean

Returns:

  • (Boolean)


556
557
558
559
560
561
562
# File 'lib/roby/plan.rb', line 556

def self.can_gc?(task)
    if task.starting? then true # wait for the task to be started before deciding ...
    elsif task.running? && !task.finishing?
  task.event(:stop).controlable?
    else true
    end
end

Instance Method Details

#[](object) ⇒ Object

Returns object if object is a plan object from this plan, or if it has no plan yet (in which case it is added to the plan first). Otherwise, raises ArgumentError.

This method is provided for consistency with Transaction#[]



547
548
549
550
551
552
553
554
# File 'lib/roby/plan.rb', line 547

def [](object)
    if object.plan != self
  raise ArgumentError, "#{object} is not from #{plan}"
    elsif !object.plan
  discover(object)
    end
    object
end

#add_repair(failure_point, task) ⇒ Object

Install a plan repair for failure_point with task. If task is pending, it is started.

See also #repairs and #remove_repair



478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
# File 'lib/roby/plan.rb', line 478

def add_repair(failure_point, task)
    if !failure_point.kind_of?(Event)
  raise TypeError, "failure point #{failure_point} should be an event"
    elsif task.plan && task.plan != self
  raise ArgumentError, "wrong plan: #{task} is in #{task.plan}, not #{self}"
    elsif repairs.has_key?(failure_point)
  raise ArgumentError, "there is already a plan repair defined for #{failure_point}: #{repairs[failure_point]}"
    elsif !task.plan
  discover(task)
    end

    repairs[failure_point] = task
    if failure_point.generator.respond_to?(:task)
  task_index.repaired_tasks << failure_point.generator.task
    end

    if task.pending?
  Roby.once { task.start! }
    end
end

#added_transaction(trsc) ⇒ Object

Hook called when a new transaction has been built on top of this plan



322
# File 'lib/roby/plan.rb', line 322

def added_transaction(trsc); super if defined? super end

#auto(task) ⇒ Object

Make GC finalize task if it is not useful anymore



143
# File 'lib/roby/plan.rb', line 143

def auto(task); @keepalive.delete(task) end

#clearObject

Remove all tasks



172
173
174
175
176
177
178
179
# File 'lib/roby/plan.rb', line 172

def clear
    @known_tasks.each { |t| t.clear_relations }
    @known_tasks.clear
    @free_events.each { |e| e.clear_relations }
    @free_events.clear
    @missions.clear
    @keepalive.clear
end

#discard(task) ⇒ Object

Removes the task in tasks from the list of missions



156
157
158
159
160
161
162
163
# File 'lib/roby/plan.rb', line 156

def discard(task)
    @missions.delete(task)
    discover(task)
    task.mission = false if task.self_owned?

    discarded(task)
    self
end

#discarded(tasks) ⇒ Object

Hook called when tasks have been discarded from this plan



165
# File 'lib/roby/plan.rb', line 165

def discarded(tasks); super if defined? super end

#discover(objects) ⇒ Object

call-seq:

plan.discover([t1, t2, ...]) => plan

Updates Plan#known_tasks with either the child tree of the tasks in objects



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
# File 'lib/roby/plan.rb', line 233

def discover(objects)
    event_seeds, tasks = partition_event_task(objects)
    event_seeds = (event_seeds || ValueSet.new).to_value_set

    if tasks
  tasks = tasks.to_value_set
  new_tasks = useful_task_component(nil, tasks, tasks)
  unless new_tasks.empty?
      old_task_events = task_events.dup
      new_tasks = discover_task_set(new_tasks)
      event_seeds.merge(task_events - old_task_events)
  end
    end

    if !event_seeds.empty?
  events = event_seeds.dup

  # now, we include the set of free events that are linked to
  # +new_tasks+ in +events+
  EventStructure.each_root_relation do |rel|
      components = rel.generated_subgraphs(event_seeds, false)
      components.concat rel.reverse.generated_subgraphs(event_seeds, false)
      for c in components
    events.merge(c.to_value_set)
      end
  end

  discover_event_set(events - task_events - free_events)
    end

    self
end

#discover_event_set(events) ⇒ Object

Add events to the set of known events and call discovered_events for the new events

This is for internal use, use #discover instead



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/roby/plan.rb', line 270

def discover_event_set(events)
    events = events.difference(free_events)
    events.delete_if do |e|
  if !e.root_object?
      true
  else
      e.plan = self
      false
  end
    end

    unless events.empty?
  free_events.merge(events)
  discovered_events(events)
    end

    events
end

#discover_task_set(tasks) ⇒ Object

Add tasks to the set of known tasks and call discovered_tasks for the new tasks

This is for internal use, use #discover instead



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/roby/plan.rb', line 293

def discover_task_set(tasks)
    tasks = tasks.difference(known_tasks)
    for t in tasks
  t.plan = self
  task_events.merge t.bound_events.values.to_value_set
  task_index.add t
    end
    known_tasks.merge tasks
    discovered_tasks(tasks)

    for t in tasks
  t.instantiate_model_event_relations
    end
    tasks
end

#discovered(tasks) ⇒ Object

DEPRECATED. Use #discovered_tasks instead



310
# File 'lib/roby/plan.rb', line 310

def discovered(tasks); super if defined? super end

#discovered_events(events) ⇒ Object

Hook called when new events have been discovered in this plan



317
318
319
# File 'lib/roby/plan.rb', line 317

def discovered_events(events)
    super if defined? super
end

#discovered_tasks(tasks) ⇒ Object

Hook called when new tasks have been discovered in this plan



312
313
314
315
# File 'lib/roby/plan.rb', line 312

def discovered_tasks(tasks)
    discovered(tasks)
    super if defined? super
end

#droby_dump(dest) ⇒ Object

Returns an intermediate representation of self suitable to be sent to the dest peer.



295
296
297
# File 'lib/roby/distributed/proxy.rb', line 295

def droby_dump(dest)
    @__droby_marshalled__ ||= DRoby.new(Roby::Distributed.droby_dump(dest), remote_id)
end

#each_taskObject

Iterates on all tasks



472
# File 'lib/roby/plan.rb', line 472

def each_task; @known_tasks.each { |t| yield(t) } end

#editObject



145
146
147
148
149
150
151
# File 'lib/roby/plan.rb', line 145

def edit
    if block_given?
  Roby::Control.synchronize do
      yield
  end
    end
end

#empty?Boolean

Returns true if there is no task in this plan

Returns:

  • (Boolean)


470
# File 'lib/roby/plan.rb', line 470

def empty?; @known_tasks.empty? end

#executable?Boolean

Check that this is an executable plan. This is always true for plain Plan objects and false for transcations

Returns:

  • (Boolean)


226
# File 'lib/roby/plan.rb', line 226

def executable?; true end

#finalized(task) ⇒ Object

backward compatibility



741
742
743
# File 'lib/roby/plan.rb', line 741

def finalized(task) # :nodoc:
    super if defined? super
end

#finalized_event(event) ⇒ Object

Hook called when event has been removed from this plan



750
# File 'lib/roby/plan.rb', line 750

def finalized_event(event); super if defined? super end

#finalized_task(task) ⇒ Object

Hook called when task has been removed from this plan



745
746
747
748
# File 'lib/roby/plan.rb', line 745

def finalized_task(task)
    super if defined? super
    finalized(task)
end

#find_tasks(model = nil, args = nil) ⇒ Object

Returns a Query object on this plan



467
468
469
470
471
472
473
# File 'lib/roby/query.rb', line 467

def find_tasks(model = nil, args = nil)
    q = Query.new(self)
    if model || args
  q.which_fullfills(model, args)
    end
    q
end

#garbage(task) ⇒ Object

Hook called when task is marked as garbage. It will be garbage collected as soon as possible



724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
# File 'lib/roby/plan.rb', line 724

def garbage(task)
    # Remove all signals that go *to* the task
    #
    # While we want events which come from the task to be properly
    # forwarded, the signals that go to the task are to be ignored
    if task.self_owned?
  task.each_event do |ev|
      ev.parent_objects(EventStructure::Signal).each do |signalling_event|
    signalling_event.remove_signal ev
      end
  end
    end

    super if defined? super
end

#garbage_collect(force_on = nil) ⇒ Object

Kills and removes all unneeded tasks



565
566
567
568
569
570
571
572
573
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
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
# File 'lib/roby/plan.rb', line 565

def garbage_collect(force_on = nil)
    if force_on && !force_on.empty?
  force_gc.merge(force_on.to_value_set)
    end

    # The set of tasks for which we queued stop! at this cycle
    # #finishing? is false until the next event propagation cycle
    finishing = ValueSet.new
    did_something = true
    while did_something
  did_something = false

  tasks = unneeded_tasks | force_gc
  local_tasks  = self.local_tasks & tasks
  remote_tasks = tasks - local_tasks

  # Remote tasks are simply removed, regardless of other concerns
  for t in remote_tasks
      Plan.debug { "GC: removing the remote task #{t}" }
      remove_object(t)
  end

  break if local_tasks.empty?

  if local_tasks.all? { |t| t.pending? || t.finished? }
      local_tasks.each do |t|
    Plan.debug { "GC: #{t} is not running, removed" }
    garbage(t)
    remove_object(t)
      end
      break
  end

  # Mark all root local_tasks as garbage
  roots = nil
  2.times do |i|
      roots = local_tasks.find_all do |t|
    if t.root?
        garbage(t)
        true
    else
        Plan.debug { "GC: ignoring #{t}, it is not root" }
        false
    end
      end

      break if i == 1 || !roots.empty?

      # There is a cycle somewhere. Try to break it by removing
      # weak relations within elements of local_tasks
      Plan.debug "cycle found, removing weak relations"

      local_tasks.each do |t|
    next if t.root?
    t.each_graph do |rel|
        rel.remove(t) if rel.weak?
    end
      end
  end

  (roots.to_value_set - finishing - gc_quarantine).each do |local_task|
      if local_task.pending? 
    Plan.info "GC: removing pending task #{local_task}"
    remove_object(local_task)
    did_something = true
      elsif local_task.starting?
    # wait for task to be started before killing it
    Plan.debug { "GC: #{local_task} is starting" }
      elsif local_task.finished?
    Plan.debug { "GC: #{local_task} is not running, removed" }
    remove_object(local_task)
    did_something = true
      elsif !local_task.finishing?
    if local_task.event(:stop).controlable?
        Plan.debug { "GC: queueing #{local_task}/stop" }
        if !local_task.respond_to?(:stop!)
      Plan.fatal "something fishy: #{local_task}/stop is controlable but there is no #stop! method"
      gc_quarantine << local_task
        else
      finishing << local_task
      Roby::Control.once do
          Plan.debug { "GC: stopping #{local_task}" }
          local_task.stop!(nil)
      end
        end
    else
        Plan.warn "GC: ignored #{local_task}, it cannot be stopped"
        gc_quarantine << local_task
    end
      elsif local_task.finishing?
    Plan.debug { "GC: waiting for #{local_task} to finish" }
      else
    Plan.warn "GC: ignored #{local_task}"
      end
  end
    end

    unneeded_events.each do |event|
  remove_object(event)
    end
end

#handle_replace(from, to) {|from, to| ... } ⇒ Object

Yields:

  • (from, to)


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

def handle_replace(from, to)
    return if from == to

    # Check that +to+ is valid in all hierarchy relations where +from+ is a child
    if !to.fullfills?(*from.fullfilled_model)
  raise InvalidReplace.new(from, to, "to does not fullfills #{from.fullfilled_model}")
    end

    # Check that +to+ is in the same execution state than +from+
    if !to.compatible_state?(from)
  raise InvalidReplace.new(from, to, "state. #{from.running?}, #{to.running?}")
    end

    # Swap the subplans of +from+ and +to+
    yield(from, to)

    replaced(from, to)
    if mission?(from)
  discard(from)
  insert(to)
    elsif permanent?(from)
  auto(from)
  permanent(to)
    else
  discover(to)
    end
end

#include?(object) ⇒ Boolean

Checks if task is included in this plan

Returns:

  • (Boolean)


464
# File 'lib/roby/plan.rb', line 464

def include?(object); @known_tasks.include?(object) || @free_events.include?(object) end

#insert(task) ⇒ Object

Inserts a new mission in the plan. Its child tree is automatically inserted too. Returns the plan



124
125
126
127
128
129
130
131
132
# File 'lib/roby/plan.rb', line 124

def insert(task)
    return if @missions.include?(task)

    @missions << task
    discover(task)
    task.mission = true if task.self_owned?
    inserted(task)
    self
end

#inserted(tasks) ⇒ Object

Hook called when tasks have been inserted in this plan



134
# File 'lib/roby/plan.rb', line 134

def inserted(tasks); super if defined? super end

#inspectObject



89
90
91
# File 'lib/roby/plan.rb', line 89

def inspect
    "#<#{to_s}: missions=#{missions.to_s} tasks=#{known_tasks.to_s} events=#{free_events.to_s} transactions=#{transactions.to_s}>"
end

#local_tasksObject



381
382
383
# File 'lib/roby/plan.rb', line 381

def local_tasks
    task_index.by_owner[Roby::Distributed] || ValueSet.new
end

#locally_useful_tasksObject

Returns the set of useful tasks in this plan



355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'lib/roby/plan.rb', line 355

def locally_useful_tasks
    # Remove all missions that are finished
    for finished_mission in (@missions & task_index.by_state[:finished?])
  if !task_index.repaired_tasks.include?(finished_mission)
      discard(finished_mission)
  end
    end
    for finished_permanent in (@keepalive & task_index.by_state[:finished?])
  if !task_index.repaired_tasks.include?(finished_permanent)
      auto(finished_permanent)
  end
    end

    # Create the set of tasks which must be kept as-is
    seeds = @missions | @keepalive
    for trsc in transactions
  seeds.merge trsc.proxy_objects.keys.to_value_set
    end

    return ValueSet.new if seeds.empty?

    # Compute the set of LOCAL tasks which serve the seeds.  The set of
    # locally_useful_tasks is the union of the seeds and of this one 
    useful_task_component(local_tasks, seeds & local_tasks, seeds) | seeds
end

#mission?(task) ⇒ Boolean

Checks if task is a mission of this plan

Returns:

  • (Boolean)


466
# File 'lib/roby/plan.rb', line 466

def mission?(task); @missions.include?(task) end

#owns?(object) ⇒ Boolean

Returns:

  • (Boolean)


167
168
169
# File 'lib/roby/plan.rb', line 167

def owns?(object)
    (object.owners - owners).empty?
end

#partition_event_task(objects) ⇒ Object

call-seq:

plan.partition_event_task(objects) => events, tasks


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/roby/plan.rb', line 96

def partition_event_task(objects)
    if objects.respond_to?(:to_task) then return nil, [objects.to_task]
    elsif objects.respond_to?(:to_event) then return [objects.to_event], nil
    elsif !objects.respond_to?(:each)
  raise TypeError, "expecting a task, event, or a collection of tasks and events, got #{objects}"
    end

    evts, tasks = objects.partition do |o|
  if o.respond_to?(:to_event) then true
  elsif o.respond_to?(:to_task) then false
  else raise ArgumentError, "found #{o || 'nil'} which is neither a task nor an event"
  end
    end
    return evts, tasks
end

#permanent(task) ⇒ Object

Forbid the GC to take out task



137
138
139
140
# File 'lib/roby/plan.rb', line 137

def permanent(task)
    @keepalive << task
    discover(task)
end

#permanent?(task) ⇒ Boolean

Returns:

  • (Boolean)


153
# File 'lib/roby/plan.rb', line 153

def permanent?(task); @keepalive.include?(task) end

#query_each(result_set, &block) ⇒ Object

Called by TaskMatcher#each and Query#each to return the result of this query on self



487
488
489
490
491
# File 'lib/roby/query.rb', line 487

def query_each(result_set, &block)
    for task in result_set
  yield(task)
    end
end

#query_result_set(matcher) ⇒ Object

Called by TaskMatcher#result_set and Query#result_set to get the set of tasks matching matcher



477
478
479
480
481
482
483
# File 'lib/roby/query.rb', line 477

def query_result_set(matcher)
    result = ValueSet.new
    for task in matcher.filter(known_tasks, task_index)
  result << task if matcher === task
    end
    result
end

#query_roots(result_set, relation) ⇒ Object

Given the result set of query, returns the subset of tasks which have no parent in query



495
496
497
498
499
500
501
502
503
504
505
506
# File 'lib/roby/query.rb', line 495

def query_roots(result_set, relation)
    children = ValueSet.new
    found    = ValueSet.new
    for task in result_set
  next if children.include?(task)
  task_children = task.generated_subgraph(relation)
  found -= task_children
  children.merge(task_children)
  found << task
    end
    found
end

#real_planObject

If this plan is a toplevel plan, returns self. If it is a transaction, returns the underlying plan



114
115
116
117
118
119
120
# File 'lib/roby/plan.rb', line 114

def real_plan
    ret = self
    while ret.respond_to?(:plan)
  ret = ret.plan
    end
    ret
end

#remote_tasksObject



385
386
387
388
389
390
391
# File 'lib/roby/plan.rb', line 385

def remote_tasks
    if local_tasks = task_index.by_owner[Roby::Distributed]
  known_tasks - local_tasks
    else
  known_tasks
    end
end

#remove_object(object) ⇒ Object



667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
# File 'lib/roby/plan.rb', line 667

def remove_object(object)
    if !object.root_object?
  raise ArgumentError, "cannot remove #{object} which is a non-root object"
    elsif object.plan != self
  if known_tasks.include?(object) || free_events.include?(object)
      raise ArgumentError, "#{object} is included in #{self} but #plan == #{object.plan}"
  elsif !object.plan
      if object.removed_at
    raise ArgumentError, "#{object} has been removed at\n  #{object.removed_at.join("\n  ")}"
      else
    raise ArgumentError, "#{object} has not been included in this plan"
      end
  end
  raise ArgumentError, "#{object} is not in #{self}: #plan == #{object.plan}"
    end

    # Remove relations first. This is needed by transaction since
    # removing relations may need wrapping some new objects, and in
    # that case these new objects will be discovered as well
    object.clear_relations

    @free_events.delete(object)
    @missions.delete(object)
    @known_tasks.delete(object)
    @keepalive.delete(object)
    force_gc.delete(object)

    object.plan = nil
    object.removed_at = caller

    case object
    when EventGenerator
  finalized_event(object)

    when Task
  task_index.remove(object)

  for ev in object.bound_events.values
      task_events.delete(ev)
      finalized_event(ev)
  end
  finalized_task(object)

    else
  raise ArgumentError, "unknown object type #{object}"
    end

    self
end

#remove_repair(task) ⇒ Object

Removes task from the set of active plan repairs.

See also #repairs and #add_repair



502
503
504
505
506
507
508
509
510
511
# File 'lib/roby/plan.rb', line 502

def remove_repair(task)
    repairs.delete_if do |ev, repair|
  if repair == task
      if ev.generator.respond_to?(:task)
    task_index.repaired_tasks.delete(ev.generator.task)
      end
      true
  end
    end
end

#remove_task(t) ⇒ Object

Backward compatibility



718
719
720
# File 'lib/roby/plan.rb', line 718

def remove_task(t) # :nodoc:
    remove_object(t)
end

#remove_transaction(trsc) ⇒ Object

Removes the transaction trsc from the list of known transactions built on this plan



325
326
327
328
# File 'lib/roby/plan.rb', line 325

def remove_transaction(trsc)
    transactions.delete(trsc)
    removed_transaction(trsc)
end

#removed_transaction(trsc) ⇒ Object

Hook called when a new transaction has been built on top of this plan



330
# File 'lib/roby/plan.rb', line 330

def removed_transaction(trsc); super if defined? super end

#repairs_for(event) ⇒ Object

Return all repairs which apply on event



514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
# File 'lib/roby/plan.rb', line 514

def repairs_for(event)
    result = Hash.new

    if event.generator.respond_to?(:task)
  equivalent_generators = event.generator.generated_subgraph(EventStructure::Forwarding)

  history = event.generator.task.history
  id    = event.propagation_id
  index = history.index(event)
  while index < history.size
      ev = history[index]
      break if ev.propagation_id != id

      if equivalent_generators.include?(ev.generator) &&
    (task = repairs[ev])

    result[ev] = task
      end

      index += 1
  end
    elsif task = repairs[event]
  result[event] = task
    end

    result
end

#replace(from, to) ⇒ Object



215
216
217
218
219
# File 'lib/roby/plan.rb', line 215

def replace(from, to)
    handle_replace(from, to) do
  from.replace_subplan_by(to)
    end
end

#replace_task(from, to) ⇒ Object



209
210
211
212
213
# File 'lib/roby/plan.rb', line 209

def replace_task(from, to)
    handle_replace(from, to) do
  from.replace_by(to)
    end
end

#replaced(from, to) ⇒ Object

Hook called when to has replaced from in this plan



222
# File 'lib/roby/plan.rb', line 222

def replaced(from, to); super if defined? super end

#respawn(task) ⇒ Object

Replace task with a fresh copy of itself



753
754
755
756
757
758
759
# File 'lib/roby/plan.rb', line 753

def respawn(task)
    new_task = task.class.new(task.arguments.dup)

    replace_task(task, new_task)
    Control.once { new_task.start!(nil) }
    new_task
end

#sibling_on?(peer) ⇒ Boolean

If this object is the main plan, checks if we are subscribed to the whole remote plan

Returns:

  • (Boolean)


67
68
69
70
71
# File 'lib/roby/plan.rb', line 67

def sibling_on?(peer)
    if Roby.plan == self then peer.remote_plan
    else super
    end
end

#sizeObject

Count of tasks in this plan



468
# File 'lib/roby/plan.rb', line 468

def size; @known_tasks.size end

#unneeded_eventsObject

The set of events that can be removed from the plan



453
454
455
456
457
458
459
460
461
# File 'lib/roby/plan.rb', line 453

def unneeded_events
    useful_events = self.useful_events

    result = (free_events - useful_events)
    result.delete_if do |ev|
  transactions.any? { |trsc| trsc.wrap(ev, false) }
    end
    result
end

#unneeded_tasksObject

Returns the set of unused tasks



394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
# File 'lib/roby/plan.rb', line 394

def unneeded_tasks
    # Get the set of local tasks that are serving one of our own missions or
    # permanent tasks
    useful = self.locally_useful_tasks

    # Append to that the set of tasks that are useful for our peers and
    # include the set of local tasks that are serving tasks in
    # +remotely_useful+
    remotely_useful = Distributed.remotely_useful_objects(remote_tasks, true, nil)
    serving_remote = useful_task_component(local_tasks, useful & local_tasks, remotely_useful)

    useful.merge remotely_useful
    useful.merge serving_remote

    (known_tasks - useful)
end

#useful_event_component(useful_events) ⇒ Object



418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
# File 'lib/roby/plan.rb', line 418

def useful_event_component(useful_events)
    current_size = useful_events.size
    for rel in EventStructure.relations
  next unless rel.root_relation?

  for subgraph in rel.components(free_events, false)
      subgraph = subgraph.to_value_set
      if subgraph.intersects?(useful_events) || subgraph.intersects?(task_events)
    useful_events.merge(subgraph)
    if useful_events.include_all?(free_events)
        return free_events
    end
      end
  end

  if useful_events.include_all?(free_events)
      return free_events
  end
    end

    if current_size != useful_events.size
  useful_event_component(useful_events)
    else
  useful_events
    end
end

#useful_eventsObject

Computes the set of events that are useful in the plan Events are ‘useful’ when they are chained to a task.



447
448
449
450
# File 'lib/roby/plan.rb', line 447

def useful_events
    return ValueSet.new if free_events.empty?
    (free_events & useful_event_component(ValueSet.new))
end

#useful_task?(task) ⇒ Boolean

Computes the set of useful tasks and checks that task is in it. This is quite slow. It is here for debugging purposes. Do not use it in production code

Returns:

  • (Boolean)


414
415
416
# File 'lib/roby/plan.rb', line 414

def useful_task?(task)
    known_tasks.include?(task) && !unneeded_tasks.include?(task)
end

#useful_task_component(complete_set, useful_set, seeds) ⇒ Object

Merges the set of tasks that are useful for seeds into useful_set. Only the tasks that are in complete_set are included.



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/roby/plan.rb', line 334

def useful_task_component(complete_set, useful_set, seeds)
    old_useful_set = useful_set.dup
    for rel in TaskStructure.relations
  next unless rel.root_relation?
  for subgraph in rel.generated_subgraphs(seeds, false)
      useful_set.merge(subgraph)
  end
    end

    if complete_set
  useful_set &= complete_set
    end

    if useful_set.size == old_useful_set.size || (complete_set && useful_set.size == complete_set.size)
  useful_set
    else
  useful_task_component(complete_set, useful_set, (useful_set - old_useful_set))
    end
end