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#pollregisters 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_roby_task_state ⇒ Symbol
Return a symbol representing the current state of the task.
-
#current_roby_task_state?(state) ⇒ Boolean
Test if that current state corresponds to the provided state (symbol).
-
#current_state ⇒ Object
deprecated
Deprecated.
use #current_roby_task_state instead
-
#current_state?(state) ⇒ Boolean
deprecated
Deprecated.
use #current_roby_task_state? instead
-
#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
- #task_state_to_s ⇒ 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
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 284 285 286 287 |
# File 'lib/roby/task.rb', line 233 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
862 863 864 |
# File 'lib/roby/task.rb', line 862 def bound_events @bound_events end |
#data ⇒ Object
The internal data for this task
1131 1132 1133 |
# File 'lib/roby/task.rb', line 1131 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
1157 1158 1159 |
# File 'lib/roby/task.rb', line 1157 def execute_handlers @execute_handlers end |
#failed_to_start_time ⇒ Object (readonly)
The time at which the task failed to start
818 819 820 |
# File 'lib/roby/task.rb', line 818 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
825 826 827 |
# File 'lib/roby/task.rb', line 825 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.
815 816 817 |
# File 'lib/roby/task.rb', line 815 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
1164 1165 1166 |
# File 'lib/roby/task.rb', line 1164 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
580 581 582 |
# File 'lib/roby/task.rb', line 580 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
804 805 806 |
# File 'lib/roby/task.rb', line 804 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
1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 |
# File 'lib/roby/task.rb', line 1714 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>
459 |
# File 'lib/roby/task.rb', line 459 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
1794 1795 1796 1797 1798 1799 |
# File 'lib/roby/task.rb', line 1794 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
1392 1393 1394 1395 1396 1397 1398 1399 1400 |
# File 'lib/roby/task.rb', line 1392 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
1777 1778 1779 |
# File 'lib/roby/task.rb', line 1777 def add_coordination_object(object) @coordination_objects.push(object) end |
#apply_terminal_flags(terminal_events, success_events, failure_events) ⇒ Object
733 734 735 736 737 738 739 740 741 742 743 744 745 746 |
# File 'lib/roby/task.rb', line 733 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
1645 1646 1647 |
# File 'lib/roby/task.rb', line 1645 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.
1641 1642 1643 |
# File 'lib/roby/task.rb', line 1641 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
213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/roby/task.rb', line 213 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
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/roby/task.rb', line 187 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
1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 |
# File 'lib/roby/task.rb', line 1342 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
1331 1332 1333 |
# File 'lib/roby/task.rb', line 1331 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
772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 |
# File 'lib/roby/task.rb', line 772 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
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 |
# File 'lib/roby/task.rb', line 650 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
697 698 699 700 701 702 703 704 705 706 707 708 709 710 |
# File 'lib/roby/task.rb', line 697 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
1406 1407 1408 1409 1410 1411 1412 1413 1414 |
# File 'lib/roby/task.rb', line 1406 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
1148 1149 1150 |
# File 'lib/roby/task.rb', line 1148 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
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 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 |
# File 'lib/roby/task.rb', line 1435 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.
1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 |
# File 'lib/roby/task.rb', line 1553 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
1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 |
# File 'lib/roby/task.rb', line 1537 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
403 404 405 |
# File 'lib/roby/task.rb', line 403 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.
1754 1755 1756 |
# File 'lib/roby/task.rb', line 1754 def create_transaction_proxy(transaction) transaction.create_and_register_proxy_task(self) end |
#current_roby_task_state ⇒ Symbol
Return a symbol representing 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
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 |
# File 'lib/roby/task.rb', line 299 def current_roby_task_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_roby_task_state?(state) ⇒ Boolean
Test if that current state corresponds to the provided state (symbol)
328 329 330 |
# File 'lib/roby/task.rb', line 328 def current_roby_task_state?(state) state == current_roby_task_state.to_sym end |
#current_state ⇒ Object
use #current_roby_task_state instead
333 334 335 |
# File 'lib/roby/task.rb', line 333 def current_state current_roby_task_state end |
#current_state?(state) ⇒ Boolean
use #current_roby_task_state? instead
338 339 340 |
# File 'lib/roby/task.rb', line 338 def current_state?(state) current_roby_task_state?(state) end |
#do_not_reuse ⇒ void
This method returns an undefined value.
Call to force the value of #reusable? to false
554 555 556 |
# File 'lib/roby/task.rb', line 554 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
1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 |
# File 'lib/roby/task.rb', line 1234 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
1768 1769 1770 |
# File 'lib/roby/task.rb', line 1768 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
982 983 984 985 986 987 988 989 |
# File 'lib/roby/task.rb', line 982 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
1384 1385 1386 |
# File 'lib/roby/task.rb', line 1384 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)
891 892 893 894 895 896 897 898 |
# File 'lib/roby/task.rb', line 891 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
390 391 392 393 394 |
# File 'lib/roby/task.rb', line 390 def end_time if ev = stop_event.last ev.time end end |
#ensure_poll_handler_called ⇒ Object
1215 1216 1217 1218 1219 1220 1221 1222 |
# File 'lib/roby/task.rb', line 1215 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
878 879 880 881 882 883 884 885 886 |
# File 'lib/roby/task.rb', line 878 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.
1004 1005 1006 |
# File 'lib/roby/task.rb', line 1004 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.
480 481 482 483 484 485 486 487 488 489 490 491 492 493 |
# File 'lib/roby/task.rb', line 480 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.
465 466 467 468 469 470 471 |
# File 'lib/roby/task.rb', line 465 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
1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 |
# File 'lib/roby/task.rb', line 1171 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
641 642 643 644 645 646 647 |
# File 'lib/roby/task.rb', line 641 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
607 608 609 |
# File 'lib/roby/task.rb', line 607 def failed_to_start? @failed_to_start end |
#filter_events_from_strongly_related_tasks(events) ⇒ Object
678 679 680 681 682 683 684 685 686 687 688 689 690 |
# File 'lib/roby/task.rb', line 678 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
868 869 870 871 |
# File 'lib/roby/task.rb', line 868 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.
798 799 800 801 |
# File 'lib/roby/task.rb', line 798 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
1358 1359 1360 |
# File 'lib/roby/task.rb', line 1358 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)
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 |
# File 'lib/roby/task.rb', line 943 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
argsis included in the task arguments
1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 |
# File 'lib/roby/task.rb', line 1306 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.
514 515 516 517 518 519 520 |
# File 'lib/roby/task.rb', line 514 def fully_instanciated? if arguments.static? @fully_instanciated ||= list_unset_arguments.empty? else list_unset_arguments.empty? end end |
#garbage! ⇒ Object
564 565 566 567 |
# File 'lib/roby/task.rb', line 564 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)
1373 1374 1375 1376 1377 1378 1379 1380 1381 |
# File 'lib/roby/task.rb', line 1373 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
1326 1327 1328 |
# File 'lib/roby/task.rb', line 1326 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.
530 531 532 |
# File 'lib/roby/task.rb', line 530 def has_event?(event_model) bound_events.has_key?(event_model) end |
#initialize_copy(old) ⇒ Object
:nodoc:
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 |
# File 'lib/roby/task.rb', line 407 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.
1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 |
# File 'lib/roby/task.rb', line 1610 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
169 170 171 172 |
# File 'lib/roby/task.rb', line 169 def inspect state = task_state_to_s "#<#{self} executable=#{executable?} state=#{state} plan=#{plan}>" end |
#interruptible? ⇒ Boolean
Returns true if this task's stop event is controlable
474 475 476 |
# File 'lib/roby/task.rb', line 474 def interruptible? stop_event.controlable? end |
#invalidate_terminal_flag ⇒ Object
716 717 718 |
# File 'lib/roby/task.rb', line 716 def invalidate_terminal_flag @terminal_flag_invalid = true end |
#invalidated_terminal_flag? ⇒ Boolean
712 713 714 |
# File 'lib/roby/task.rb', line 712 def invalidated_terminal_flag? !!@terminal_flag_invalid end |
#last_event ⇒ TaskEvent?
The last event emitted by this task
399 400 401 |
# File 'lib/roby/task.rb', line 399 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.
374 375 376 377 378 379 380 |
# File 'lib/roby/task.rb', line 374 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.
499 500 501 502 503 504 505 506 507 508 509 510 |
# File 'lib/roby/task.rb', line 499 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
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 |
# File 'lib/roby/task.rb', line 611 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
1761 1762 1763 |
# File 'lib/roby/task.rb', line 1761 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.
1054 1055 1056 |
# File 'lib/roby/task.rb', line 1054 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| ... }
902 903 904 905 906 907 908 909 |
# File 'lib/roby/task.rb', line 902 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.
524 525 526 |
# File 'lib/roby/task.rb', line 524 def partially_instanciated? !fully_instanciated? end |
#plan=(new_plan) ⇒ Object
:nodoc:
427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/roby/task.rb', line 427 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
1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 |
# File 'lib/roby/task.rb', line 1190 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
1228 |
# File 'lib/roby/task.rb', line 1228 def poll_handler; end |
#pretty_print(pp, with_owners = true) ⇒ Object
:nodoc:
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 |
# File 'lib/roby/task.rb', line 1027 def pretty_print(pp, with_owners = true) # :nodoc: pp.text "#{model.name}<id:#{droby_id.id}> #{task_state_to_s}" if with_owners && !owners.empty? pp.nest(2) do pp.breakable pp.text "owners: " pp.nest(2) do pp.seplist(owners) { |r| pp.text r.to_s } end end end pp.nest(2) do pp.breakable 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
360 361 362 363 364 365 366 367 368 369 370 |
# File 'lib/roby/task.rb', line 360 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.
597 598 599 600 601 602 603 604 605 |
# File 'lib/roby/task.rb', line 597 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
570 571 572 |
# File 'lib/roby/task.rb', line 570 def quarantined? @quarantined end |
#related_events(result = Set.new) ⇒ Object
Returns the set of events directly related to this task
761 762 763 764 765 766 767 768 |
# File 'lib/roby/task.rb', line 761 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
751 752 753 754 755 756 757 758 |
# File 'lib/roby/task.rb', line 751 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
1786 1787 1788 |
# File 'lib/roby/task.rb', line 1786 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
1207 1208 1209 |
# File 'lib/roby/task.rb', line 1207 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.
1579 1580 1581 1582 1583 1584 1585 1586 1587 |
# File 'lib/roby/task.rb', line 1579 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
1599 1600 1601 1602 1603 1604 1605 1606 1607 |
# File 'lib/roby/task.rb', line 1599 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
1421 1422 1423 |
# File 'lib/roby/task.rb', line 1421 def respawn plan.respawn(self) end |
#reusable? ⇒ Boolean
True if this task can be reused by some other parts in the plan
559 560 561 562 |
# File 'lib/roby/task.rb', line 559 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)
541 542 543 |
# File 'lib/roby/task.rb', line 541 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)
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 |
# File 'lib/roby/task.rb', line 913 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
1627 1628 1629 1630 1631 |
# File 'lib/roby/task.rb', line 1627 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
383 384 385 386 387 |
# File 'lib/roby/task.rb', line 383 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 |
#task_state_to_s ⇒ Object
158 159 160 161 162 163 164 165 166 167 |
# File 'lib/roby/task.rb', line 158 def task_state_to_s 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 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.
999 1000 1001 |
# File 'lib/roby/task.rb', line 999 def terminal_events bound_events.each_value.find_all(&:terminal?) end |
#to_execution_exception ⇒ Object
1749 1750 1751 |
# File 'lib/roby/task.rb', line 1749 def to_execution_exception ExecutionException.new(LocalizedError.new(self)) end |
#to_s ⇒ Object
:nodoc:
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 |
# File 'lib/roby/task.rb', line 1008 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
1059 1060 1061 |
# File 'lib/roby/task.rb', line 1059 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
1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 |
# File 'lib/roby/task.rb', line 1520 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
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 |
# File 'lib/roby/task.rb', line 828 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.
723 724 725 726 727 728 729 730 731 |
# File 'lib/roby/task.rb', line 723 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
1143 |
# File 'lib/roby/task.rb', line 1143 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
1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 |
# File 'lib/roby/task.rb', line 1286 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)
1653 1654 1655 1656 1657 1658 |
# File 'lib/roby/task.rb', line 1653 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
1739 1740 1741 1742 1743 1744 1745 1746 1747 |
# File 'lib/roby/task.rb', line 1739 def |(other) if self.null? other elsif other.null? self else Tasks::Parallel.new << self << other end end |