class Roby::TaskStructure::Dependency
Attributes
failing_tasks[R]
interesting_events[R]
Public Class Methods
merge_dependency_options(opt1, opt2)
click to toggle source
Merges the dependency descriptions (i.e. the relation payload), verifying that the two provided option hashes are compatible
@return [Hash] the merged options @raise [ModelViolation] if the two hashes are not compatible
# File lib/roby/task_structure/dependency.rb, line 98 def self.merge_dependency_options(opt1, opt2) if opt1[:remove_when_done] != opt2[:remove_when_done] raise Roby::ModelViolation, "incompatible dependency specification: trying to change the value of +remove_when_done+" end result = { remove_when_done: opt1[:remove_when_done], consider_in_pending: opt1[:consider_in_pending] } if opt1[:success] || opt2[:success] result[:success] = if !opt1[:success] then opt2[:success] elsif !opt2[:success] then opt1[:success] else opt1[:success].and(opt2[:success]) end end if opt1[:failure] || opt2[:failure] result[:failure] = if !opt1[:failure] then opt2[:failure] elsif !opt2[:failure] then opt1[:failure] else opt1[:failure].or(opt2[:failure]) end end # Check model compatibility models1, arguments1 = opt1[:model] models2, arguments2 = opt2[:model] task_model1 = models1.find { |m| m <= Roby::Task } task_model2 = models2.find { |m| m <= Roby::Task } result_model = [] if task_model1 && task_model2 if task_model1.fullfills?(task_model2) result_model << task_model1 elsif task_model2.fullfills?(task_model1) result_model << task_model2 else raise Roby::ModelViolation, "incompatible models #{task_model1} and #{task_model2}" end elsif task_model1 result_model << task_model1 elsif task_model2 result_model << task_model2 end models1.each do |m| next if m <= Roby::Task if !models2.any? { |other_m| other_m.fullfills?(m) } result_model << m end end models2.each do |m| next if m <= Roby::Task if !models1.any? { |other_m| other_m.fullfills?(m) } result_model << m end end result[:model] = [result_model] # Merge arguments result[:model][1] = arguments1.merge(arguments2) do |key, old_value, new_value| if old_value != new_value raise Roby::ModelViolation, "incompatible argument constraint #{old_value} and #{new_value} for #{key}" end old_value end # Finally, merge the roles (the easy part ;-)) result[:roles] = opt1[:roles] | opt2[:roles] result end
merge_fullfilled_model(model, required_models, required_arguments)
click to toggle source
# File lib/roby/task_structure/dependency.rb, line 56 def self.merge_fullfilled_model(model, required_models, required_arguments) model, tags, arguments = *model tags = tags.dup required_models = Array(required_models) for m in required_models if m.kind_of?(Roby::Models::TaskServiceModel) tags << m elsif m.has_ancestor?(model) model = m elsif !model.has_ancestor?(m) raise Roby::ModelViolation, "inconsistency in fullfilled models: #{model} and #{m} are incompatible" end end arguments = arguments.merge(required_arguments) do |name, old, new| if old != new raise Roby::ModelViolation, "inconsistency in fullfilled models: #{old} and #{new}" end old end return [model, tags, arguments] end
new(observer: nil)
click to toggle source
Calls superclass method
Roby::Relations::TaskRelationGraph::new
# File lib/roby/task_structure/dependency.rb, line 12 def initialize(observer: nil) super(observer: observer) @interesting_events = Array.new @failing_tasks = Set.new end
validate_options(options, defaults = Hash.new)
click to toggle source
# File lib/roby/task_structure/dependency.rb, line 82 def self.validate_options(options, defaults = Hash.new) defaults = Hash[model: [[Roby::Task], Hash.new], success: nil, failure: nil, remove_when_done: true, consider_in_pending: true, roles: Set.new, role: nil].merge(defaults) Kernel.validate_options options, defaults end
Public Instance Methods
check_structure(plan)
click to toggle source
Checks the structure of plan
w.r.t. the constraints of the hierarchy relations. It returns an array of ChildFailedError
for all failed hierarchy relations
# File lib/roby/task_structure/dependency.rb, line 186 def check_structure(plan) # The Set in #interesting_events is also referenced # *separately* in EventStructure.gather_events. We therefore have to # keep it (and can't use #partition). Yuk events = Array.new interesting_events.delete_if do |ev| if ev.plan == plan events << ev true else !ev.plan end end tasks = Set.new failing_tasks.delete_if do |task| if task.plan == plan tasks << task true else !task.plan end end return Array.new if events.empty? && tasks.empty? result = [] # Get the set of tasks for which a possible failure has been # registered The tasks that are failing the hierarchy requirements # are registered in Hierarchy.failing_tasks. events.each do |event| task = event.task tasks << task if event.symbol == :start # also add the children task.each_child do |child_task, _| tasks << child_task end end end for child in tasks # Check if the task has been removed from the plan next unless child.plan removed_parents = [] child.each_parent_task do |parent| next if parent.finished? next unless parent.self_owned? options = parent[child, Dependency] success = options[:success] failure = options[:failure] has_success = success && success.evaluate(child) if !has_success has_failure = failure && failure.evaluate(child) end error = nil if has_success if options[:remove_when_done] # Must not delete it here as we are iterating over the # parents removed_parents << parent end elsif has_failure explanation = failure.explain_true(child) error = Roby::ChildFailedError.new(parent, child, explanation, :failed_event) elsif success && success.static?(child) explanation = success.explain_static(child) error = Roby::ChildFailedError.new(parent, child, explanation, :unreachable_success) end if error if parent.running? result << error failing_tasks << child elsif options[:consider_in_pending] && plan.control.pending_dependency_failed(parent, child, error) result << error failing_tasks << child end end end for parent in removed_parents parent.remove_child child end end result end
merge_info(parent, child, opt1, opt2)
click to toggle source
Called by the relation management when two dependency relations need to be merged
@see Dependency.merge_dependency_options
# File lib/roby/task_structure/dependency.rb, line 175 def merge_info(parent, child, opt1, opt2) result = Dependency.merge_dependency_options(opt1, opt2) update_triggers_for(parent, child, result) result rescue Exception => e raise e, e.message + " while updating the dependency information for #{parent} -> #{child}", e.backtrace end
update_triggers_for(parent, child, info)
click to toggle source
@api private
Updates the dependency internal data to trigger errors / success when relevant events are emitted
# File lib/roby/task_structure/dependency.rb, line 22 def update_triggers_for(parent, child, info) events = Set.new if info[:success] for event_name in info[:success].required_events events << child.event(event_name) end end if info[:failure] for event_name in info[:failure].required_events events << child.event(event_name) end end if !events.empty? parent.start_event.on(on_replace: :drop) do |ev| ev.plan.task_relation_graph_for(self.class).interesting_events << ev.generator end events.each do |e| e.if_unreachable do |reason, ev| # The actualy graph of 'ev' might be different than self # ... re-resolve ev.plan.task_relation_graph_for(self.class).interesting_events << ev end e.on(on_replace: :drop) do |ev| ev.plan.task_relation_graph_for(self.class).interesting_events << ev.generator end end end # Initial triggers failing_tasks << child end