Class: Roby::Task
- Inherits:
-
PlanObject
- Object
- DistributedObject
- PlanObject
- Roby::Task
- Extended by:
- Logger::Hierarchy, DRoby::Identifiable, DRoby::V5::Models::TaskDumper, Models::Task, TaskStateHelper
- Includes:
- Logger::Hierarchy, 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.
-
Direct Known Subclasses
Actions::Task, Coordination::FaultHandlingTask, Interface::REST::Task, Roby::Tasks::ExternalProcess, Roby::Tasks::Group, Roby::Tasks::Null, Roby::Tasks::Simple, Roby::Tasks::TaskAggregator, Roby::Tasks::Thread, Roby::Tasks::Timeout, Roby::Tasks::Virtual, Roby::Test::EmptyTask, Roby::Test::Goto2D
Defined Under Namespace
Classes: InternalError
Constant Summary
Constants included from Models::Arguments
Models::Arguments::NO_DEFAULT_ARGUMENT
Deprecated Event API collapse
-
#data ⇒ Object
The internal data for this task.
-
#execute_handlers ⇒ Array<InstanceHandler>
readonly
private
The set of instance-level execute blocks.
-
#poll_handlers ⇒ Array<InstanceHandler>
readonly
private
The set of instance-level poll blocks.
Instance Attribute Summary collapse
-
#arguments ⇒ TaskArguments
readonly
The task arguments.
-
#bound_events ⇒ Object
readonly
List of EventGenerator objects bound to this task.
-
#failed_to_start_time ⇒ Object
readonly
The time at which the task failed to start.
-
#failure_event ⇒ Object
readonly
The event that caused this task to fail.
-
#failure_reason ⇒ Object
readonly
The reason for which this task failed.
-
#history ⇒ Array<Event>
readonly
The accumulated history of this task.
-
#quarantine_reason ⇒ Exception?
readonly
The reason why the task is in quarantine.
-
#state_machine ⇒ Object
readonly
Returns the value of attribute state_machine.
-
#terminal_event ⇒ Object
readonly
The most specialized event that caused this task to end.
Attributes included from GUI::RelationsCanvasTask
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
Attributes inherited from DistributedObject
Deprecated Event API collapse
-
#+(other) ⇒ Object
Creates a sequence where
selfwill be started first, andtaskis started ifselffinished successfully. -
#action_state_machine(&block) ⇒ Object
Create an action state machine and attach it to this task.
-
#add_child_object(child, type, info) ⇒ Object
private
Validates that both self and the child object are owned by the local instance.
-
#add_coordination_object(object) ⇒ Object
private
Declare that a coordination object is attached to this task.
- #as_plan ⇒ Object
-
#as_service ⇒ PlanService
Returns an object that will allow to track this task’s role in the plan regardless of replacements.
-
#can_merge?(target) ⇒ Boolean
Tests if a task could be merged within self.
-
#can_replace?(target) ⇒ Boolean
True if self can be used to replace target.
-
#commit_transaction ⇒ Object
private
This method is called during the commit process to apply changes stored in a proxy.
-
#compatible_state?(task) ⇒ Boolean
Checks if
taskis in the same execution state thanselfReturns true if they are either both running or both pending. -
#compute_replacement_candidates(object, filter, with_subplan) ⇒ Object
private
Computes the list of edge replacements that might be necessary to perform a replacement in a transaction-aware way.
- #compute_subplan_replacement_operation(object, filter) ⇒ Object private
- #compute_task_replacement_operation(object, filter) ⇒ Object
- #create_transaction_proxy(transaction) ⇒ Object private
-
#do_poll(plan) ⇒ Object
private
Internal method used to register the poll blocks in the engine execution cycle.
-
#each_coordination_object {|object| ... } ⇒ Object
Enumerate the coordination objects currently attached to this task.
-
#each_event(only_wrapped = true) {|generator| ... } ⇒ Object
(also: #each_plan_child)
Iterates on all the events defined for this task.
-
#each_exception_handler(&iterator) ⇒ Object
Lists all exception handlers attached to this task.
-
#emit(event_model, *context) ⇒ Object
deprecated
Deprecated.
use EventGenerator#emit instead (e.g. task.start_event.emit)
- #ensure_poll_handler_called ⇒ Object private
-
#event_model(model) ⇒ Model<TaskEvent>
Accesses an event model.
-
#execute(options = {}, &block) ⇒ void
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.
-
#forcefully_terminate ⇒ Object
“Simply” mark this task as terminated.
-
#forward_to(event_model, to, *to_task_events) ⇒ Object
deprecated
Deprecated.
use EventGenerator#forward_to instead (e.g.
-
#fullfills?(models, args = nil) ⇒ Boolean
Whether this task instance provides a set of models and arguments.
-
#handle_exception(e) ⇒ Object
private
Handles the given exception.
-
#has_argument?(key) ⇒ Boolean
True if this model requires an argument named key and that argument is set.
- #initialize_replacement(task) ⇒ Object private
-
#match ⇒ Queries::TaskMatcher
Return a task match object that matches self.
-
#null? ⇒ Boolean
True if this task is a null task.
-
#on(event_model, options = {}, &user_handler) ⇒ Object
deprecated
Deprecated.
use Roby::TaskEventGenerator#on on the event object, e.g. task.start_event.on { |event| … }
-
#poll(options = {}) {|task| ... } ⇒ Object
Adds a new poll block on this instance.
-
#poll_handler ⇒ Object
private
Method under which ‘Models::Task#poll` registers its given block.
-
#pretty_print(pp, with_owners = true) ⇒ Object
:nodoc:.
-
#remove_coordination_object(object) ⇒ Object
private
Declare that a coordination object is no longer attached to this task.
-
#remove_poll_handler(handler) ⇒ void
Remove a poll handler from this instance.
-
#replace_by(object, filter: Plan::ReplacementFilter::Null.new) ⇒ Object
Replaces self by object.
-
#replace_subplan_by(object, filter: Plan::ReplacementFilter::Null.new) ⇒ Object
Replaces self’s subplan by another subplan.
-
#respawn ⇒ Object
Create a new task of the same model and with the same arguments than this one.
-
#signals(event_model, to, *to_task_events) ⇒ Object
deprecated
Deprecated.
use EventGenerator#signal instead (e.g.
-
#simulate ⇒ Object
deprecated
Deprecated.
this has no equivalent. It really has never seen proper support
-
#terminal_events ⇒ Array<TaskEventGenerator>
Returns this task’s set of terminal events.
- #to_execution_exception ⇒ Object
-
#to_s ⇒ Object
:nodoc:.
-
#to_task ⇒ Object
Converts this object into a task object.
-
#transform_candidates_into_operations(edges) ⇒ Object
private
The compute_ methods work on a edge set that looks like this: [graph, [add_parent, add_child, remove_parent, remove_child]] while Plan#apply_replacement_operations works on two sets [[graph, add_parent, add_child, info], …] [[graph, remove_parent, remove_child], …] This transforms the first form into the second.
-
#updated_data ⇒ Object
This hook is called whenever the internal data of this task is updated.
-
#use_fault_response_table(table_model, arguments = {}) ⇒ Object
Declares that this fault response table should be made active when this task starts, and deactivated when it ends.
-
#when_finalized(options = {}, &block) ⇒ Object
Register a hook that is called when this task is finalized (removed from its plan).
-
#|(other) ⇒ Object
Creates a parallel aggregation between
selfandtask.
Class Method Summary collapse
- .create_script(*task, &block) ⇒ Object
- .goal ⇒ Object
-
.script(&block) ⇒ Object
Adds a script that is going to be executed for every instance of this task model.
- .state ⇒ Object
Instance Method Summary collapse
-
#abstract? ⇒ Object
:method:abstract?.
- #apply_terminal_flags(terminal_events, success_events, failure_events) ⇒ Object
-
#assign_argument(key, value) ⇒ Object
private
Sets one of this task’s arguments.
-
#assign_arguments(**arguments) ⇒ Object
private
Helper to assign multiple argument values at once.
-
#check_emission_validity(event) ⇒ Object
This method is called by TaskEventGenerator#fire just before the event handlers and commands are called.
-
#clear_events_external_relations(remove_strong: true) ⇒ Object
Clear relations events of this task have with events outside the task.
-
#clear_relations(remove_internal: false, remove_strong: true) ⇒ Object
Remove all relations in which
selfor its event are involved. - #create_fresh_copy ⇒ Object
-
#current_state ⇒ Symbol
Retrieve the current state of the task.
-
#current_state?(state) ⇒ Boolean
Test if that current state corresponds to the provided state (symbol).
-
#do_not_reuse ⇒ void
Call to force the value of #reusable? to false.
-
#end_time ⇒ Object
Returns when this task has finished.
-
#event(event_model) ⇒ TaskEventGenerator?
Returns the event generator by its name or model.
-
#executable=(flag) ⇒ Object
Set the executable flag.
-
#executable? ⇒ Boolean
True if this task is executable.
-
#failed_to_start!(reason, time = Time.now) ⇒ Object
Declares that this task has failed to start.
- #failed_to_start? ⇒ Boolean
- #filter_events_from_strongly_related_tasks(events) ⇒ Object
-
#find_event(name) ⇒ TaskEventGenerator?
Returns the event generator by its name.
-
#fired_event(event) ⇒ Object
Hook called by TaskEventGenerator#fired when one of this task’s events has been fired.
-
#freeze_delayed_arguments ⇒ Object
private
Evaluate delayed arguments, and replace in #arguments the ones that currently have a value.
-
#fully_instanciated? ⇒ Boolean
True if all arguments defined by Task.argument on the task model are either explicitely set or have a default value.
- #garbage! ⇒ Object
- #goal ⇒ Object
-
#has_event?(event_model) ⇒ Boolean
True if this task has an event of the required model.
-
#initialize(plan: TemplatePlan.new, **arguments) ⇒ Task
constructor
Create a new task object.
-
#initialize_copy(old) ⇒ Object
:nodoc:.
- #inspect ⇒ Object
-
#interruptible? ⇒ Boolean
Returns true if this task’s stop event is controlable.
- #invalidate_terminal_flag ⇒ Object
- #invalidated_terminal_flag? ⇒ Boolean
-
#last_event ⇒ TaskEvent?
The last event emitted by this task.
-
#lifetime ⇒ Object
Returns for how many seconds this task is running.
-
#list_unset_arguments ⇒ Object
Lists all arguments, that are set to be needed via the :argument syntax but are not set.
- #mark_failed_to_start(reason, time) ⇒ Object
-
#meaningful_arguments(task_model = self.model) ⇒ Object
The part of #arguments that is meaningful for this task model.
-
#name ⇒ String
The task name.
-
#partially_instanciated? ⇒ Boolean
True if at least one argument required by the task model is not set.
-
#plan=(new_plan) ⇒ Object
:nodoc:.
-
#promise(description: "#{self}.promise", executor: promise_executor, &block) ⇒ Promise
Create a promise that is serialized with all promises created for this object.
-
#quarantined!(reason: nil) ⇒ Object
Mark the task as quarantined.
-
#quarantined? ⇒ Boolean
Whether this task has been quarantined.
-
#related_events(result = Set.new) ⇒ Object
Returns the set of events directly related to this task.
-
#related_tasks(result = Set.new) ⇒ Object
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.
- #resolve_goals ⇒ Object
- #resolve_state_sources ⇒ Object
-
#reusable? ⇒ Boolean
True if this task can be reused by some other parts in the plan.
-
#running? ⇒ Boolean
True if this task is currently running (i.e. is has already started, and is not finished).
-
#script(options = {}, &block) ⇒ Object
Adds a task script that is going to be executed while this task instance runs.
-
#start_time ⇒ Object
Returns when this task has been started.
- #state ⇒ Object
- #transition! ⇒ Object
-
#update_task_status(event) ⇒ Object
Call to update the task status because of
event. -
#update_terminal_flag ⇒ Object
Updates the terminal flag for all events in the task.
Methods included from Models::Task
abstract, all_models, causal_link, clear_model, compute_terminal_events, define_command_method, define_event_methods, define_method_unless_present, 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
Methods included from TaskStateHelper
import_events_to_roby, namespace, namespace=, refine_running_state
Methods included from DRoby::Identifiable
Methods included from DRoby::V5::Models::TaskDumper
Methods included from DRoby::V5::ModelDumper
#droby_dump, #droby_marshallable?
Methods included from DRoby::V5::TaskDumper
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
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
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 276 277 278 279 280 281 282 283 |
# File 'lib/roby/task.rb', line 229 def initialize(plan: TemplatePlan.new, **arguments) @bound_events = {} 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 = [] @coordination_objects = [] @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 = {} 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
#arguments ⇒ TaskArguments (readonly)
The task arguments
106 107 108 |
# File 'lib/roby/task.rb', line 106 def arguments @arguments end |
#bound_events ⇒ Object (readonly)
List of EventGenerator objects bound to this task
848 849 850 |
# File 'lib/roby/task.rb', line 848 def bound_events @bound_events end |
#data ⇒ Object
The internal data for this task
1121 1122 1123 |
# File 'lib/roby/task.rb', line 1121 def data @data end |
#execute_handlers ⇒ Array<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
1147 1148 1149 |
# File 'lib/roby/task.rb', line 1147 def execute_handlers @execute_handlers end |
#failed_to_start_time ⇒ Object (readonly)
The time at which the task failed to start
804 805 806 |
# File 'lib/roby/task.rb', line 804 def failed_to_start_time @failed_to_start_time end |
#failure_event ⇒ Object (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
811 812 813 |
# File 'lib/roby/task.rb', line 811 def failure_event @failure_event end |
#failure_reason ⇒ Object (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.
801 802 803 |
# File 'lib/roby/task.rb', line 801 def failure_reason @failure_reason end |
#history ⇒ Array<Event> (readonly)
The accumulated history of this task
This is the list of events that this task ever emitted, sorted by emission time (oldest first)
114 115 116 |
# File 'lib/roby/task.rb', line 114 def history @history end |
#poll_handlers ⇒ Array<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
1154 1155 1156 |
# File 'lib/roby/task.rb', line 1154 def poll_handlers @poll_handlers end |
#quarantine_reason ⇒ Exception? (readonly)
The reason why the task is in quarantine
If the quarantine was caused by an exception, this will return the original exception
566 567 568 |
# File 'lib/roby/task.rb', line 566 def quarantine_reason @quarantine_reason end |
#state_machine ⇒ Object (readonly)
Returns the value of attribute state_machine.
303 304 305 |
# File 'lib/roby/coordination/task_state_machine.rb', line 303 def state_machine @state_machine end |
#terminal_event ⇒ Object (readonly)
The most specialized event that caused this task to end
790 791 792 |
# File 'lib/roby/task.rb', line 790 def terminal_event @terminal_event end |
Class Method Details
.create_script(*task, &block) ⇒ Object
264 265 266 267 268 269 270 271 |
# File 'lib/roby/coordination/task_script.rb', line 264 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 |
.goal ⇒ Object
23 24 25 26 27 28 29 30 31 |
# File 'lib/roby/state/task.rb', line 23 def self.goal unless @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
275 276 277 278 279 |
# File 'lib/roby/coordination/task_script.rb', line 275 def self.script(&block) s = create_script(&block) scripts << s s end |
.state ⇒ Object
5 6 7 8 9 10 11 12 13 |
# File 'lib/roby/state/task.rb', line 5 def self.state unless @state if superclass.respond_to?(:state) supermodel = superclass.state end @state = StateModel.new(supermodel) end @state end |
Instance Method Details
#+(other) ⇒ 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
1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 |
# File 'lib/roby/task.rb', line 1704 def +(other) # !!!! + is NOT commutative if other.null? self elsif self.null? other else Tasks::Sequence.new << self << other 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>
445 |
# File 'lib/roby/task.rb', line 445 attr_predicate :abstract?, true |
#action_state_machine(&block) ⇒ Object
Create an action state machine and attach it to this task
Unlike ‘Actions::Interface#action_state_machine`, states must be defined from explicit action objects
1784 1785 1786 1787 1788 1789 |
# File 'lib/roby/task.rb', line 1784 def action_state_machine(&block) model = Coordination::ActionStateMachine .new_submodel(action_interface: nil, root: self.model) model.parse(&block) model.new(self) end |
#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
1382 1383 1384 1385 1386 1387 1388 1389 1390 |
# File 'lib/roby/task.rb', line 1382 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
1767 1768 1769 |
# File 'lib/roby/task.rb', line 1767 def add_coordination_object(object) @coordination_objects.push(object) end |
#apply_terminal_flags(terminal_events, success_events, failure_events) ⇒ Object
719 720 721 722 723 724 725 726 727 728 729 730 731 732 |
# File 'lib/roby/task.rb', line 719 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_plan ⇒ Object
1635 1636 1637 |
# File 'lib/roby/task.rb', line 1635 def as_plan self end |
#as_service ⇒ PlanService
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.
1631 1632 1633 |
# File 'lib/roby/task.rb', line 1631 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
209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/roby/task.rb', line 209 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
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/roby/task.rb', line 183 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
1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 |
# File 'lib/roby/task.rb', line 1332 def can_merge?(target) if defined?(super) && !super return false elsif finished? || target.finished? return false elsif !model.can_merge?(target.model) return false end arguments.can_semantic_merge?(target.arguments) end |
#can_replace?(target) ⇒ Boolean
True if self can be used to replace target
1321 1322 1323 |
# File 'lib/roby/task.rb', line 1321 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
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 |
# File 'lib/roby/task.rb', line 758 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
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 |
# File 'lib/roby/task.rb', line 636 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? parents = graph.each_in_neighbour(event).find_all do |neighbour| !task_events.include?(neighbour) end children = graph.each_out_neighbour(event).find_all do |neighbour| !task_events.include?(neighbour) end unless remove_strong parents = (parents) children = (children) end parents.each { |from| graph.remove_edge(from, event) } children.each { |to| graph.remove_edge(event, to) } removed ||= !parents.empty? || !children.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
683 684 685 686 687 688 689 690 691 692 693 694 695 696 |
# File 'lib/roby/task.rb', line 683 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_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.
This method is called during the commit process to apply changes stored in a proxy
1396 1397 1398 1399 1400 1401 1402 1403 1404 |
# File 'lib/roby/task.rb', line 1396 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
1138 1139 1140 |
# File 'lib/roby/task.rb', line 1138 def compatible_state?(task) finished? || !(running? ^ task.running?) end |
#compute_replacement_candidates(object, filter, with_subplan) ⇒ 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.
Computes the list of edge replacements that might be necessary to perform a replacement in a transaction-aware way
At this stage, we make little difference between subplan and task replacement
1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 |
# File 'lib/roby/task.rb', line 1425 def compute_replacement_candidates(object, filter, with_subplan) edges, edges_candidates = [], [] subplan_tasks = Set[self, object] subplan_tasks.compare_by_identity parent_tasks = Set.new parent_tasks.compare_by_identity plan.each_task_relation_graph do |g| next if g.strong? || filter.excluded_graph?(g) rel = g.class next if filter.excluded_relation?(rel) each_in_neighbour_merged(rel, intrusive: true) do |parent| parent_tasks << parent unless filter.excluded_task?(parent) edges << [g, parent, self, parent, object] end end if with_subplan || 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 transaction_stack = plan.each_object_in_transaction_stack(self).to_a object_transaction_stack = plan.each_object_in_transaction_stack(object).to_a event_pairs = [] model.each_event do |_, event| event = transaction_stack .find { |_, o| o.find_event(event.symbol) } .last.event(event.symbol) object_event = object_transaction_stack .find { |_, o| o.find_event(event.symbol) } .last.event(event.symbol) event_pairs << [event, object_event] end plan.each_event_relation_graph do |g| next if g.strong? || filter.excluded_graph?(g) rel = g.class next if filter.excluded_relation?(rel) event_pairs.each do |event, object_event| event.each_in_neighbour_merged(rel, intrusive: false) do |_, parent| if parent.respond_to?(:task) && !transaction_stack.include?(parent.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) && !transaction_stack.include?(child.task) edges_candidates << [ plan[child.task], [g, event, child, object_event, child] ] end end end end [edges, edges_candidates, subplan_tasks, parent_tasks] end |
#compute_subplan_replacement_operation(object, filter) ⇒ 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.
1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 |
# File 'lib/roby/task.rb', line 1543 def compute_subplan_replacement_operation(object, filter) edges, edges_candidates, subplan_tasks, parent_tasks = compute_replacement_candidates(object, filter, false) edges_candidates.each do |reference_task, op| if filter.excluded_task?(reference_task) next elsif 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 transform_candidates_into_operations(edges) end |
#compute_task_replacement_operation(object, filter) ⇒ Object
1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 |
# File 'lib/roby/task.rb', line 1527 def compute_task_replacement_operation(object, filter) edges, edges_candidates, = compute_replacement_candidates(object, filter, true) edges_candidates.each do |reference_task, op| if filter.excluded_task?(reference_task) next elsif reference_task == object || reference_task == self next else edges << op end end transform_candidates_into_operations(edges) end |
#create_fresh_copy ⇒ Object
389 390 391 |
# File 'lib/roby/task.rb', line 389 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.
1744 1745 1746 |
# File 'lib/roby/task.rb', line 1744 def create_transaction_proxy(transaction) transaction.create_and_register_proxy_task(self) end |
#current_state ⇒ Symbol
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
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
# File 'lib/roby/task.rb', line 295 def current_state # Started and not finished # True, when task has never been started if pending? :pending elsif failed_to_start? :failed_to_start elsif starting? :starting # True, when terminal event is pending. Note that a finishing task # is running elsif finishing? :finishing elsif running? state_machine&.status_name || :running # Terminated with success or failure elsif success? :succeeded elsif failed? :failed elsif stop_event.emitted? :finished end end |
#current_state?(state) ⇒ Boolean
Test if that current state corresponds to the provided state (symbol)
324 325 326 |
# File 'lib/roby/task.rb', line 324 def current_state?(state) state == current_state.to_sym end |
#do_not_reuse ⇒ void
This method returns an undefined value.
Call to force the value of #reusable? to false
540 541 542 |
# File 'lib/roby/task.rb', line 540 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
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 |
# File 'lib/roby/task.rb', line 1224 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 poll_handler 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
1758 1759 1760 |
# File 'lib/roby/task.rb', line 1758 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
968 969 970 971 972 973 974 975 |
# File 'lib/roby/task.rb', line 968 def each_event(only_wrapped = true) return enum_for(__method__, only_wrapped) unless 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
1374 1375 1376 |
# File 'lib/roby/task.rb', line 1374 def each_exception_handler(&iterator) model.each_exception_handler(&iterator) end |
#emit(event_model, *context) ⇒ Object
use EventGenerator#emit instead (e.g. task.start_event.emit)
877 878 879 880 881 882 883 884 |
# File 'lib/roby/task.rb', line 877 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_time ⇒ Object
Returns when this task has finished
376 377 378 379 380 |
# File 'lib/roby/task.rb', line 376 def end_time if ev = stop_event.last ev.time end end |
#ensure_poll_handler_called ⇒ Object
1205 1206 1207 1208 1209 1210 1211 1212 |
# File 'lib/roby/task.rb', line 1205 def ensure_poll_handler_called return if transaction_proxy? || !running? @poll_handler_id ||= execution_engine.add_propagation_handler( description: "poll block for #{self}", type: :external_events, &method(:do_poll) ) end |
#event(event_model) ⇒ TaskEventGenerator?
Returns the event generator by its name or model
864 865 866 867 868 869 870 871 872 |
# File 'lib/roby/task.rb', line 864 def event(event_model) unless (event = find_event(event_model)) raise ArgumentError, "cannot find #{event_model} in the set of bound events in "\ "#{self}. Known events are #{bound_events}." end event 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.
990 991 992 |
# File 'lib/roby/task.rb', line 990 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.
466 467 468 469 470 471 472 473 474 475 476 477 478 479 |
# File 'lib/roby/task.rb', line 466 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.
451 452 453 454 455 456 457 |
# File 'lib/roby/task.rb', line 451 def executable? if @executable == true true elsif @executable.nil? !abstract? && !partially_instanciated? && super end end |
#execute(options = {}, &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
1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 |
# File 'lib/roby/task.rb', line 1161 def execute( = {}, &block) default_on_replace = abstract? ? :copy : :drop = InstanceHandler.( , on_replace: default_on_replace ) check_arity(block, 1) @execute_handlers << InstanceHandler.new(block, ([:on_replace] == :copy)) ensure_poll_handler_called end |
#failed_to_start!(reason, time = Time.now) ⇒ Object
Declares that this task has failed to start
#failure_reason will be set to FailedToStart with the given reason
627 628 629 630 631 632 633 |
# File 'lib/roby/task.rb', line 627 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
593 594 595 |
# File 'lib/roby/task.rb', line 593 def failed_to_start? @failed_to_start end |
#filter_events_from_strongly_related_tasks(events) ⇒ Object
664 665 666 667 668 669 670 671 672 673 674 675 676 |
# File 'lib/roby/task.rb', line 664 def (events) return events if events.empty? strong_graphs = plan.each_relation_graph.find_all(&:strong?) events.find_all do |ev| next(true) unless ev.respond_to?(:task) task = ev.task strong_graphs.none? do |g| g.has_edge?(self, task) || g.has_edge?(task, self) end end end |
#find_event(name) ⇒ TaskEventGenerator?
Returns the event generator by its name
854 855 856 857 |
# File 'lib/roby/task.rb', line 854 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.
784 785 786 787 |
# File 'lib/roby/task.rb', line 784 def fired_event(event) history << event update_task_status(event) end |
#forcefully_terminate ⇒ Object
“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
1348 1349 1350 |
# File 'lib/roby/task.rb', line 1348 def forcefully_terminate update_task_status(event(:stop).new([])) end |
#forward_to(event_model, to, *to_task_events) ⇒ Object
use EventGenerator#forward_to instead (e.g.
task.start_event.forward_to other_task.stop_event)
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 |
# File 'lib/roby/task.rb', line 929 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 to when Task to_task_events.map { |ev| to.event(ev) } when EventGenerator [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_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.
Evaluate delayed arguments, and replace in #arguments the ones that currently have a value
127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/roby/task.rb', line 127 def freeze_delayed_arguments unless arguments.static? result = {} 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
1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 |
# File 'lib/roby/task.rb', line 1296 def fullfills?(models, args = nil) if models.kind_of?(Roby::Task) args ||= models.meaningful_arguments models = models.model end unless model.fullfills?(models) return false end args&.each do |key, name| if self.arguments[key] != name return false 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.
500 501 502 503 504 505 506 |
# File 'lib/roby/task.rb', line 500 def fully_instanciated? if arguments.static? @fully_instanciated ||= list_unset_arguments.empty? else list_unset_arguments.empty? end end |
#garbage! ⇒ Object
550 551 552 553 |
# File 'lib/roby/task.rb', line 550 def garbage! bound_events.each_value(&:garbage!) super end |
#goal ⇒ Object
33 34 35 |
# File 'lib/roby/state/task.rb', line 33 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)
1363 1364 1365 1366 1367 1368 1369 1370 1371 |
# File 'lib/roby/task.rb', line 1363 def handle_exception(e) return unless plan tasks = find_all_matching_repair_tasks(e) return super if tasks.empty? tasks.first.start! if tasks.none?(&:running?) true end |
#has_argument?(key) ⇒ Boolean
True if this model requires an argument named key and that argument is set
1316 1317 1318 |
# File 'lib/roby/task.rb', line 1316 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.
516 517 518 |
# File 'lib/roby/task.rb', line 516 def has_event?(event_model) bound_events.has_key?(event_model) end |
#initialize_copy(old) ⇒ Object
:nodoc:
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 |
# File 'lib/roby/task.rb', line 393 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 = {} @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.
1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 |
# File 'lib/roby/task.rb', line 1600 def initialize_replacement(task) super execute_handlers.each do |handler| if handler.copy_on_replace? task.execute(handler., &handler.block) end end poll_handlers.each do |handler| if handler.copy_on_replace? task.poll(handler., &handler.block) end end end |
#inspect ⇒ Object
158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/roby/task.rb', line 158 def inspect state = if pending? then "pending" elsif failed_to_start? then "failed to start" elsif starting? then "starting" elsif finishing? then "finishing" elsif running? then "running" else "finished" end "#<#{self} executable=#{executable?} state=#{state} plan=#{plan}>" end |
#interruptible? ⇒ Boolean
Returns true if this task’s stop event is controlable
460 461 462 |
# File 'lib/roby/task.rb', line 460 def interruptible? stop_event.controlable? end |
#invalidate_terminal_flag ⇒ Object
702 703 704 |
# File 'lib/roby/task.rb', line 702 def invalidate_terminal_flag @terminal_flag_invalid = true end |
#invalidated_terminal_flag? ⇒ Boolean
698 699 700 |
# File 'lib/roby/task.rb', line 698 def invalidated_terminal_flag? !!@terminal_flag_invalid end |
#last_event ⇒ TaskEvent?
The last event emitted by this task
385 386 387 |
# File 'lib/roby/task.rb', line 385 def last_event history.last end |
#lifetime ⇒ Object
Returns for how many seconds this task is running. Returns nil if the task is not running.
360 361 362 363 364 365 366 |
# File 'lib/roby/task.rb', line 360 def lifetime if running? Time.now - start_time elsif finished? end_time - start_time end end |
#list_unset_arguments ⇒ Object
Lists all arguments, that are set to be needed via the :argument syntax but are not set.
This is needed for debugging purposes.
485 486 487 488 489 490 491 492 493 494 495 496 |
# File 'lib/roby/task.rb', line 485 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
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 |
# File 'lib/roby/task.rb', line 597 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 = if reason.kind_of?(LocalizedError) && reason.failed_task == self reason else FailedToStart.new(self, reason, time) end @pending = false @starting = false @failed = true plan.task_index.set_state(self, :failed?) end |
#match ⇒ Queries::TaskMatcher
Return a task match object that matches self
1751 1752 1753 |
# File 'lib/roby/task.rb', line 1751 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
119 120 121 |
# File 'lib/roby/task.rb', line 119 def meaningful_arguments(task_model = self.model) task_model.meaningful_arguments(arguments) end |
#name ⇒ String
The task name
144 145 146 147 148 149 150 |
# File 'lib/roby/task.rb', line 144 def name return @name if @name name = model.name || self.class.name @name = name unless frozen? name end |
#null? ⇒ Boolean
True if this task is a null task. See NullTask.
1044 1045 1046 |
# File 'lib/roby/task.rb', line 1044 def null? false end |
#on(event_model, options = {}, &user_handler) ⇒ Object
use Roby::TaskEventGenerator#on on the event object, e.g. task.start_event.on { |event| … }
888 889 890 891 892 893 894 895 |
# File 'lib/roby/task.rb', line 888 def on(event_model, = {}, &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(, &user_handler) self end |
#partially_instanciated? ⇒ Boolean
True if at least one argument required by the task model is not set. See Task.argument.
510 511 512 |
# File 'lib/roby/task.rb', line 510 def partially_instanciated? !fully_instanciated? end |
#plan=(new_plan) ⇒ Object
:nodoc:
413 414 415 416 417 418 419 420 421 422 423 |
# File 'lib/roby/task.rb', line 413 def plan=(new_plan) # :nodoc: null = self.plan&.null_task_relation_graphs super @relation_graphs = plan&.task_relation_graphs || null || @relation_graphs for ev in bound_events.each_value ev.plan = plan end end |
#poll(options = {}) {|task| ... } ⇒ Object
Adds a new poll block on this instance
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 |
# File 'lib/roby/task.rb', line 1180 def poll( = {}, &block) default_on_replace = abstract? ? :copy : :drop = InstanceHandler.( , on_replace: default_on_replace ) check_arity(block, 1) handler = InstanceHandler.new(block, ([:on_replace] == :copy)) @poll_handlers << handler ensure_poll_handler_called Roby.disposable { @poll_handlers.delete(handler) } end |
#poll_handler ⇒ 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.
Method under which ‘Models::Task#poll` registers its given block. Defined empty at this level to allow calling super() unconditionally
1218 |
# File 'lib/roby/task.rb', line 1218 def poll_handler; end |
#pretty_print(pp, with_owners = true) ⇒ Object
:nodoc:
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 |
# File 'lib/roby/task.rb', line 1013 def pretty_print(pp, with_owners = true) # :nodoc: pp.text "#{model.name}<id:#{droby_id.id}>" if with_owners pp.nest(2) do pp.breakable if owners.empty? pp.text "no owners" else pp.text "owners: " pp.nest(2) do pp.seplist(owners) { |r| pp.text r.to_s } end end end end pp.nest(2) do pp.breakable if arguments.empty? pp.text "no arguments" else pp.text "arguments:" 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
346 347 348 349 350 351 352 353 354 355 356 |
# File 'lib/roby/task.rb', line 346 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!(reason: nil) ⇒ Object
Mark the task as quarantined
Quarantined tasks are essentially tasks that are present in the plan, but cannot be used because they are known to misbehave and themselves can’t be killed. The prime example is a task the system tried to stop but for which the stop process failed.
Once set it cannot be unset. The engine will generate a QuarantinedTaskError error as long as there are tasks that depend on the task, to make sure that anything that depend on it either stops using it, or is killed itself.
583 584 585 586 587 588 589 590 591 |
# File 'lib/roby/task.rb', line 583 def quarantined!(reason: nil) return if quarantined? @quarantined = true @quarantine_reason = reason fatal "#{self} entered quarantine: #{reason}" plan.register_quarantined_task(self) end |
#quarantined? ⇒ Boolean
Whether this task has been quarantined
556 557 558 |
# File 'lib/roby/task.rb', line 556 def quarantined? @quarantined end |
#related_events(result = Set.new) ⇒ Object
Returns the set of events directly related to this task
747 748 749 750 751 752 753 754 |
# File 'lib/roby/task.rb', line 747 def (result = Set.new) each_event do |ev| ev.(result) end result.reject { |ev| ev.respond_to?(:task) && ev.task == self } .to_set end |
#related_tasks(result = Set.new) ⇒ Object
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
737 738 739 740 741 742 743 744 |
# File 'lib/roby/task.rb', line 737 def (result = Set.new) result = (nil, result) each_event do |ev| ev.(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
1776 1777 1778 |
# File 'lib/roby/task.rb', line 1776 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
1197 1198 1199 |
# File 'lib/roby/task.rb', line 1197 def remove_poll_handler(handler) handler.dispose end |
#replace_by(object, filter: Plan::ReplacementFilter::Null.new) ⇒ 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.
1569 1570 1571 1572 1573 1574 1575 1576 1577 |
# File 'lib/roby/task.rb', line 1569 def replace_by(object, filter: Plan::ReplacementFilter::Null.new) added, removed = compute_task_replacement_operation(object, filter) plan.apply_replacement_operations(added, removed) initialize_replacement(object) each_event do |event| event.initialize_replacement(nil) { object.event(event.symbol) } end end |
#replace_subplan_by(object, filter: Plan::ReplacementFilter::Null.new) ⇒ 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
1589 1590 1591 1592 1593 1594 1595 1596 1597 |
# File 'lib/roby/task.rb', line 1589 def replace_subplan_by(object, filter: Plan::ReplacementFilter::Null.new) added, removed = compute_subplan_replacement_operation(object, filter) plan.apply_replacement_operations(added, removed) initialize_replacement(object) each_event do |event| event.initialize_replacement(object.event(event.symbol)) end end |
#resolve_goals ⇒ Object
37 38 39 40 41 42 43 |
# File 'lib/roby/state/task.rb', line 37 def resolve_goals unless 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_sources ⇒ Object
19 20 21 |
# File 'lib/roby/state/task.rb', line 19 def resolve_state_sources model.state.resolve_data_sources(self, state) end |
#respawn ⇒ Object
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
1411 1412 1413 |
# File 'lib/roby/task.rb', line 1411 def respawn plan.respawn(self) end |
#reusable? ⇒ Boolean
True if this task can be reused by some other parts in the plan
545 546 547 548 |
# File 'lib/roby/task.rb', line 545 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)
527 528 529 |
# File 'lib/roby/task.rb', line 527 def running? started? && !finished? end |
#script(options = {}, &block) ⇒ Object
Adds a task script that is going to be executed while this task instance runs.
290 291 292 293 294 295 296 297 |
# File 'lib/roby/coordination/task_script.rb', line 290 def script( = {}, &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
use EventGenerator#signal instead (e.g.
task.start_event.signal other_task.stop_event)
899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 |
# File 'lib/roby/task.rb', line 899 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 [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 |
#simulate ⇒ Object
this has no equivalent. It really has never seen proper support
1617 1618 1619 1620 1621 |
# File 'lib/roby/task.rb', line 1617 def simulate simulation_task = self.model.simulation_model.new(arguments.to_hash) plan.force_replace(self, simulation_task) simulation_task end |
#start_time ⇒ Object
Returns when this task has been started
369 370 371 372 373 |
# File 'lib/roby/task.rb', line 369 def start_time if ev = start_event.last ev.time end end |
#state ⇒ Object
15 16 17 |
# File 'lib/roby/state/task.rb', line 15 def state @state ||= StateSpace.new(self.model.state) end |
#terminal_events ⇒ Array<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.
985 986 987 |
# File 'lib/roby/task.rb', line 985 def terminal_events bound_events.each_value.find_all(&:terminal?) end |
#to_execution_exception ⇒ Object
1739 1740 1741 |
# File 'lib/roby/task.rb', line 1739 def to_execution_exception ExecutionException.new(LocalizedError.new(self)) end |
#to_s ⇒ Object
:nodoc:
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 |
# File 'lib/roby/task.rb', line 994 def to_s # :nodoc: s = "#{name}<id:#{droby_id.id}>(#{arguments})".dup id = owners.map do |owner| next if plan && (owner == plan.local_owner) sibling = remote_siblings[owner] sibling_address = if sibling Object.address_from_id(sibling.ref).to_s(16) else "nil" end "#{sibling_address}@#{owner.remote_name}" end s << "[" << id.join(",") << "]" unless id.empty? s end |
#to_task ⇒ Object
Converts this object into a task object
1049 1050 1051 |
# File 'lib/roby/task.rb', line 1049 def to_task self end |
#transform_candidates_into_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.
The compute_ methods work on a edge set that looks like this:
[graph, [add_parent, add_child, remove_parent, remove_child]]
while Plan#apply_replacement_operations works on two sets
[[graph, add_parent, add_child, info], ...]
[[graph, remove_parent, remove_child], ...]
This transforms the first form into the second
1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 |
# File 'lib/roby/task.rb', line 1510 def transform_candidates_into_operations(edges) added, removed = [], [] edges.each do |g, removed_parent, removed_child, added_parent, added_child| added_parent = plan[added_parent] added_child = plan[added_child] removed_parent = plan[removed_parent] removed_child = plan[removed_child] info = g.edge_info(removed_parent, removed_child) added << [g, added_parent, added_child, info] unless g.copy_on_replace? removed << [g, removed_parent, removed_child] end end [added, removed] end |
#transition! ⇒ Object
299 300 301 |
# File 'lib/roby/coordination/task_script.rb', line 299 def transition! poll_transition_event.emit end |
#update_task_status(event) ⇒ Object
Call to update the task status because of event
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 |
# File 'lib/roby/task.rb', line 814 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 @terminal_event ||= event if event.terminal? 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 nil end |
#update_terminal_flag ⇒ Object
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.
709 710 711 712 713 714 715 716 717 |
# File 'lib/roby/task.rb', line 709 def update_terminal_flag # :nodoc: return unless 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 [terminal_events, success_events, failure_events] end |
#updated_data ⇒ Object
This hook is called whenever the internal data of this task is updated. See #data, #data= and the updated_data event
1133 |
# File 'lib/roby/task.rb', line 1133 def updated_data; end |
#use_fault_response_table(table_model, arguments = {}) ⇒ Object
Declares that this fault response table should be made active when this task starts, and deactivated when it ends
1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 |
# File 'lib/roby/task.rb', line 1276 def use_fault_response_table(table_model, arguments = {}) 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 = {}, &block) ⇒ Object
Register a hook that is called when this task is finalized (removed from its plan)
1643 1644 1645 1646 1647 1648 |
# File 'lib/roby/task.rb', line 1643 def when_finalized( = {}, &block) default = abstract? ? :copy : :drop , remaining = InstanceHandler. , on_replace: default super(.merge(remaining), &block) end |
#|(other) ⇒ 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
1729 1730 1731 1732 1733 1734 1735 1736 1737 |
# File 'lib/roby/task.rb', line 1729 def |(other) if self.null? other elsif other.null? self else Tasks::Parallel.new << self << other end end |