Class: Roby::Task
- Inherits:
-
PlanObject
- Object
- DistributedObject
- PlanObject
- Roby::Task
- Extended by:
- DRoby::Identifiable, DRoby::V5::Models::TaskDumper, Models::Task, TaskStateHelper
- Includes:
- 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, 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 ⇒ Object
readonly
Returns a list of Event objects, for all events that have been fired by this task.
-
#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
-
#+(task) ⇒ Object
Creates a sequence where
selfwill be started first, andtaskis started ifselffinished successfully. -
#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.
- #apply_replacement_operations(edges) ⇒ Object private
- #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_object_replacement_operation(object) ⇒ Object private
- #compute_subplan_replacement_operation(object) ⇒ Object private
- #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 = Hash.new, &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. task.start_event.forward_to other_task.stop_event)
-
#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 = Hash.new, &user_handler) ⇒ Object
deprecated
Deprecated.
use Roby::TaskEventGenerator#on on the event object, e.g. task.start_event.on { |event| … }
-
#poll(options = Hash.new) {|task| ... } ⇒ Object
Adds a new poll block on this instance.
-
#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) ⇒ Object
Replaces self by object.
-
#replace_subplan_by(object) ⇒ 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. task.start_event.signal other_task.stop_event)
-
#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.
-
#updated_data ⇒ Object
This hook is called whenever the internal data of this task is updated.
-
#use_fault_response_table(table_model, arguments = Hash.new) ⇒ Object
Declares that this fault response table should be made active when this task starts, and deactivated when it ends.
-
#when_finalized(options = Hash.new, &block) ⇒ Object
Register a hook that is called when this task is finalized (removed from its plan).
-
#|(task) ⇒ 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
- #failed_to_start? ⇒ Boolean
-
#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! ⇒ Object
Mark the task as quarantined.
-
#quarantined? ⇒ Object
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 = Hash.new, &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, 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
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
222 223 224 225 226 227 228 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 |
# File 'lib/roby/task.rb', line 222 def initialize(plan: TemplatePlan.new, **arguments) @bound_events = Hash.new 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 = Array.new @coordination_objects = Array.new @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 = Hash.new 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
98 99 100 |
# File 'lib/roby/task.rb', line 98 def arguments @arguments end |
#bound_events ⇒ Object (readonly)
List of EventGenerator objects bound to this task
759 760 761 |
# File 'lib/roby/task.rb', line 759 def bound_events @bound_events end |
#data ⇒ Object
The internal data for this task
990 991 992 |
# File 'lib/roby/task.rb', line 990 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
1017 1018 1019 |
# File 'lib/roby/task.rb', line 1017 def execute_handlers @execute_handlers end |
#failed_to_start_time ⇒ Object (readonly)
The time at which the task failed to start
714 715 716 |
# File 'lib/roby/task.rb', line 714 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
721 722 723 |
# File 'lib/roby/task.rb', line 721 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.
711 712 713 |
# File 'lib/roby/task.rb', line 711 def failure_reason @failure_reason end |
#history ⇒ Object (readonly)
Returns a list of Event objects, for all events that have been fired by this task. The list is sorted by emission times.
106 107 108 |
# File 'lib/roby/task.rb', line 106 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
1024 1025 1026 |
# File 'lib/roby/task.rb', line 1024 def poll_handlers @poll_handlers end |
#state_machine ⇒ Object (readonly)
Returns the value of attribute state_machine.
302 303 304 |
# File 'lib/roby/coordination/task_state_machine.rb', line 302 def state_machine @state_machine end |
#terminal_event ⇒ Object (readonly)
The most specialized event that caused this task to end
700 701 702 |
# File 'lib/roby/task.rb', line 700 def terminal_event @terminal_event end |
Class Method Details
.create_script(*task, &block) ⇒ Object
251 252 253 254 255 256 257 258 |
# File 'lib/roby/coordination/task_script.rb', line 251 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
21 22 23 24 25 26 27 28 29 |
# File 'lib/roby/state/task.rb', line 21 def self.goal if !@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
262 263 264 265 266 |
# File 'lib/roby/coordination/task_script.rb', line 262 def self.script(&block) s = create_script(&block) scripts << s s end |
.state ⇒ Object
3 4 5 6 7 8 9 10 11 |
# File 'lib/roby/state/task.rb', line 3 def self.state if !@state if superclass.respond_to?(:state) supermodel = superclass.state end @state = StateModel.new(supermodel) end @state end |
Instance Method Details
#+(task) ⇒ 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
1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 |
# File 'lib/roby/task.rb', line 1554 def +(task) # !!!! + is NOT commutative if task.null? self elsif self.null? task else Tasks::Sequence.new << self << task 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>
438 |
# File 'lib/roby/task.rb', line 438 attr_predicate :abstract?, true |
#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
1247 1248 1249 1250 1251 1252 1253 |
# File 'lib/roby/task.rb', line 1247 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
1617 1618 1619 |
# File 'lib/roby/task.rb', line 1617 def add_coordination_object(object) @coordination_objects.push(object) end |
#apply_replacement_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.
1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 |
# File 'lib/roby/task.rb', line 1355 def apply_replacement_operations(edges) edges.each do |g, removed_parent, removed_child, added_parent, added_child| info = g.edge_info(removed_parent, removed_child) g.add_relation(plan[added_parent], plan[added_child], info) end edges.each do |g, removed_parent, removed_child, added_parent, added_child| if !g.copy_on_replace? g.remove_relation(plan[removed_parent], plan[removed_child]) end end end |
#apply_terminal_flags(terminal_events, success_events, failure_events) ⇒ Object
636 637 638 639 640 641 642 643 644 645 646 647 648 649 |
# File 'lib/roby/task.rb', line 636 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
1486 1487 1488 |
# File 'lib/roby/task.rb', line 1486 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.
1482 1483 1484 |
# File 'lib/roby/task.rb', line 1482 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
201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/roby/task.rb', line 201 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
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/roby/task.rb', line 175 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
1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 |
# File 'lib/roby/task.rb', line 1187 def can_merge?(target) if defined?(super) && !super return end if finished? || target.finished? return false end if !model.can_merge?(target.model) return false end target.arguments.each_assigned_argument do |key, val| if arguments.set?(key) && arguments[key] != val return false end end true end |
#can_replace?(target) ⇒ Boolean
True if self can be used to replace target
1176 1177 1178 |
# File 'lib/roby/task.rb', line 1176 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
679 680 681 682 683 684 685 686 687 688 689 690 |
# File 'lib/roby/task.rb', line 679 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
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 |
# File 'lib/roby/task.rb', line 574 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? to_remove = Array.new graph.each_in_neighbour(event) do |neighbour| if !task_events.include?(neighbour) to_remove << neighbour << event end end graph.each_out_neighbour(event) do |neighbour| if !task_events.include?(neighbour) to_remove << event << neighbour end end to_remove.each_slice(2) do |from, to| graph.remove_edge(from, to) end removed ||= !to_remove.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
607 608 609 610 611 612 613 614 615 616 617 618 619 |
# File 'lib/roby/task.rb', line 607 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
1259 1260 1261 1262 1263 1264 1265 1266 1267 |
# File 'lib/roby/task.rb', line 1259 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
1008 1009 1010 |
# File 'lib/roby/task.rb', line 1008 def compatible_state?(task) finished? || !(running? ^ task.running?) end |
#compute_object_replacement_operation(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.
1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 |
# File 'lib/roby/task.rb', line 1388 def compute_object_replacement_operation(object) edges = [] plan.each_task_relation_graph do |g| next if g.strong? g.each_in_neighbour(self) do |parent| if parent != object edges << [g, parent, self, parent, object] end end g.each_out_neighbour(self) do |child| if object != child edges << [g, self, child, object, child] end end end plan.each_event_relation_graph do |g| next if g.strong? each_event do |event| object_event = nil g.each_in_neighbour(event) do |parent| if !parent.respond_to?(:task) || (parent.task != self && parent.task != object) object_event ||= object.event(event.symbol) edges << [g, parent, event, parent, object_event] end end g.each_out_neighbour(event) do |child| if !child.respond_to?(:task) || (child.task != self && child.task != object) object_event ||= object.event(event.symbol) edges << [g, event, child, object_event, child] end end end end edges end |
#compute_subplan_replacement_operation(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.
1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 |
# File 'lib/roby/task.rb', line 1279 def compute_subplan_replacement_operation(object) edges, edges_candidates = [], [] subplan_tasks = Set[self, object] parent_tasks = Set.new plan.each_task_relation_graph do |g| next if g.strong? rel = g.class each_in_neighbour_merged(rel, intrusive: true) do |parent| parent_tasks << parent edges << [g, parent, self, parent, object] end object.each_in_neighbour_merged(rel, intrusive: true) do |parent| parent_tasks << parent end if 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 plan.each_event_relation_graph do |g| next if g.strong? rel = g.class model.each_event do |_, event| event = plan.each_object_in_transaction_stack(self). find { |_, o| o.find_event(event.symbol) }. last.event(event.symbol) object_event = plan.each_object_in_transaction_stack(object). find { |_, o| o.find_event(event.symbol) }. last.event(event.symbol) event.each_in_neighbour_merged(rel, intrusive: false) do |_, parent| if parent.respond_to?(: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) edges_candidates << [plan[child.task], [g, event, child, object_event, child]] end end end end edges_candidates.each do |reference_task, op| if 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 edges = edges.map do |g, removed_parent, removed_child, added_parent, added_child| [g, plan[removed_parent], plan[removed_child], plan[added_parent], plan[added_child]] end edges end |
#create_fresh_copy ⇒ Object
383 384 385 |
# File 'lib/roby/task.rb', line 383 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.
1594 1595 1596 |
# File 'lib/roby/task.rb', line 1594 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
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
# File 'lib/roby/task.rb', line 287 def current_state # Started and not finished if running? if respond_to?("state_machine") # state_machine.status # => String # state_machine.status_name # => Symbol return state_machine.status_name else return :running end end # True, when task has never been started if pending? return :pending elsif failed_to_start? return :failed_to_start elsif starting? return :starting # True, when terminal event is pending elsif finishing? return :finishing # Terminated with success or failure elsif success? return :succeeded elsif failed? return :failed end end |
#current_state?(state) ⇒ Boolean
Test if that current state corresponds to the provided state (symbol)
321 322 323 |
# File 'lib/roby/task.rb', line 321 def current_state?(state) return 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
522 523 524 |
# File 'lib/roby/task.rb', line 522 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
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 |
# File 'lib/roby/task.rb', line 1079 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 if respond_to?(:poll_handler) poll_handler end 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
1608 1609 1610 |
# File 'lib/roby/task.rb', line 1608 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
856 857 858 859 860 861 862 |
# File 'lib/roby/task.rb', line 856 def each_event(only_wrapped = true) return enum_for(__method__, only_wrapped) if !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
1239 1240 1241 |
# File 'lib/roby/task.rb', line 1239 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)
786 787 788 789 790 |
# File 'lib/roby/task.rb', line 786 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
370 371 372 373 374 |
# File 'lib/roby/task.rb', line 370 def end_time if ev = stop_event.last ev.time end end |
#ensure_poll_handler_called ⇒ Object
1069 1070 1071 1072 1073 |
# File 'lib/roby/task.rb', line 1069 def ensure_poll_handler_called if !transaction_proxy? && running? @poll_handler_id ||= execution_engine.add_propagation_handler(description: "poll block for #{self}", type: :external_events, &method(:do_poll)) end end |
#event(event_model) ⇒ TaskEventGenerator?
Returns the event generator by its name or model
775 776 777 778 779 780 781 |
# File 'lib/roby/task.rb', line 775 def event(event_model) if event = find_event(event_model) event else raise ArgumentError, "cannot find #{event_model} in the set of bound events in #{self}. Known events are #{bound_events}." end 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.
877 878 879 |
# File 'lib/roby/task.rb', line 877 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.
456 457 458 459 460 461 462 463 464 465 |
# File 'lib/roby/task.rb', line 456 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.
444 445 446 447 448 449 450 |
# File 'lib/roby/task.rb', line 444 def executable? if @executable == true true elsif @executable.nil? (!abstract? && !partially_instanciated? && super) end end |
#execute(options = Hash.new, &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
1031 1032 1033 1034 1035 1036 1037 1038 |
# File 'lib/roby/task.rb', line 1031 def execute( = Hash.new, &block) default_on_replace = if abstract? then :copy else :drop end = 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
565 566 567 568 569 570 571 |
# File 'lib/roby/task.rb', line 565 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
548 |
# File 'lib/roby/task.rb', line 548 def failed_to_start?; @failed_to_start end |
#find_event(name) ⇒ TaskEventGenerator?
Returns the event generator by its name
765 766 767 768 |
# File 'lib/roby/task.rb', line 765 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.
694 695 696 697 |
# File 'lib/roby/task.rb', line 694 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
1212 1213 1214 |
# File 'lib/roby/task.rb', line 1212 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)
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 |
# File 'lib/roby/task.rb', line 824 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 when Task to_task_events.map { |ev| to.event(ev) } when EventGenerator then [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
119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/roby/task.rb', line 119 def freeze_delayed_arguments if !arguments.static? result = Hash.new 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
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 |
# File 'lib/roby/task.rb', line 1149 def fullfills?(models, args = nil) if models.kind_of?(Roby::Task) args ||= models.meaningful_arguments models = models.model end if !model.fullfills?(models) return false end if args args.each do |key, name| if self.arguments[key] != name return false end 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.
486 487 488 489 490 491 492 |
# File 'lib/roby/task.rb', line 486 def fully_instanciated? if arguments.static? @fully_instanciated ||= list_unset_arguments.empty? else list_unset_arguments.empty? end end |
#garbage! ⇒ Object
531 532 533 534 |
# File 'lib/roby/task.rb', line 531 def garbage! bound_events.each_value(&:garbage!) super end |
#goal ⇒ Object
31 32 33 |
# File 'lib/roby/state/task.rb', line 31 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)
1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 |
# File 'lib/roby/task.rb', line 1227 def handle_exception(e) return if !plan tasks = find_all_matching_repair_tasks(e) return super if tasks.empty? if !tasks.any? { |t| t.running? } tasks.first.start! end true end |
#has_argument?(key) ⇒ Boolean
True if this model requires an argument named key and that argument is set
1171 1172 1173 |
# File 'lib/roby/task.rb', line 1171 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.
500 501 502 |
# File 'lib/roby/task.rb', line 500 def has_event?(event_model) bound_events.has_key?(event_model) end |
#initialize_copy(old) ⇒ Object
:nodoc:
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
# File 'lib/roby/task.rb', line 387 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 = Hash.new @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.
1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 |
# File 'lib/roby/task.rb', line 1451 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
151 152 153 154 155 156 157 158 159 160 |
# File 'lib/roby/task.rb', line 151 def inspect state = if pending? then 'pending' elsif failed_to_start? then 'failed to start' elsif starting? then 'starting' elsif running? then 'running' elsif finishing? then 'finishing' else 'finished' end "#<#{to_s} executable=#{executable?} state=#{state} plan=#{plan.to_s}>" end |
#interruptible? ⇒ Boolean
Returns true if this task’s stop event is controlable
453 |
# File 'lib/roby/task.rb', line 453 def interruptible?; stop_event.controlable? end |
#invalidate_terminal_flag ⇒ Object
622 |
# File 'lib/roby/task.rb', line 622 def invalidate_terminal_flag; @terminal_flag_invalid = true end |
#invalidated_terminal_flag? ⇒ Boolean
621 |
# File 'lib/roby/task.rb', line 621 def invalidated_terminal_flag?; !!@terminal_flag_invalid end |
#last_event ⇒ TaskEvent?
The last event emitted by this task
379 380 381 |
# File 'lib/roby/task.rb', line 379 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.
354 355 356 357 358 359 360 |
# File 'lib/roby/task.rb', line 354 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.
471 472 473 474 475 476 477 478 479 480 481 482 |
# File 'lib/roby/task.rb', line 471 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
550 551 552 553 554 555 556 557 558 559 560 561 562 563 |
# File 'lib/roby/task.rb', line 550 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 = reason @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
1601 1602 1603 |
# File 'lib/roby/task.rb', line 1601 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
111 112 113 |
# File 'lib/roby/task.rb', line 111 def meaningful_arguments(task_model = self.model) task_model.meaningful_arguments(arguments) end |
#name ⇒ String
The task name
136 137 138 139 140 141 142 143 |
# File 'lib/roby/task.rb', line 136 def name return @name if @name name = "#{model.name || self.class.name}:0x#{address.to_s(16)}" if !frozen? @name = name end name end |
#null? ⇒ Boolean
True if this task is a null task. See NullTask.
918 |
# File 'lib/roby/task.rb', line 918 def null?; false end |
#on(event_model, options = Hash.new, &user_handler) ⇒ Object
use Roby::TaskEventGenerator#on on the event object, e.g. task.start_event.on { |event| … }
795 796 797 798 799 |
# File 'lib/roby/task.rb', line 795 def on(event_model, = Hash.new, &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.
496 |
# File 'lib/roby/task.rb', line 496 def partially_instanciated?; !fully_instanciated? end |
#plan=(new_plan) ⇒ Object
:nodoc:
407 408 409 410 411 412 413 414 415 416 |
# File 'lib/roby/task.rb', line 407 def plan=(new_plan) # :nodoc: super @relation_graphs = if plan then plan.task_relation_graphs end for ev in bound_events.each_value ev.plan = plan end end |
#poll(options = Hash.new) {|task| ... } ⇒ Object
Adds a new poll block on this instance
1047 1048 1049 1050 1051 1052 1053 1054 1055 |
# File 'lib/roby/task.rb', line 1047 def poll( = Hash.new, &block) default_on_replace = if abstract? then :copy else :drop end = InstanceHandler.(, on_replace: default_on_replace) check_arity(block, 1) @poll_handlers << (handler = InstanceHandler.new(block, ([:on_replace] == :copy))) ensure_poll_handler_called handler end |
#pretty_print(pp, with_owners = true) ⇒ Object
:nodoc:
894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 |
# File 'lib/roby/task.rb', line 894 def pretty_print(pp, with_owners = true) # :nodoc: pp.text "#{model.name}:0x#{self.address.to_s(16)}" if with_owners 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 pp.text "arguments: " if !arguments.empty? 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
343 344 345 346 347 348 349 350 |
# File 'lib/roby/task.rb', line 343 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! ⇒ Object
Mark the task as quarantined
Once set it cannot be unset
544 545 546 |
# File 'lib/roby/task.rb', line 544 def quarantined! @quarantined = true end |
#quarantined? ⇒ Object
Whether this task has been quarantined
539 |
# File 'lib/roby/task.rb', line 539 attr_predicate :quarantined? |
#related_events(result = Set.new) ⇒ Object
Returns the set of events directly related to this task
668 669 670 671 672 673 674 675 |
# File 'lib/roby/task.rb', line 668 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
658 659 660 661 662 663 664 665 |
# File 'lib/roby/task.rb', line 658 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
1626 1627 1628 |
# File 'lib/roby/task.rb', line 1626 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
1061 1062 1063 |
# File 'lib/roby/task.rb', line 1061 def remove_poll_handler(handler) @poll_handlers.delete(handler) end |
#replace_by(object) ⇒ 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.
1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 |
# File 'lib/roby/task.rb', line 1433 def replace_by(object) event_mappings = Hash.new event_resolver = ->(e) { object.event(e.symbol) } each_event do |ev| event_mappings[ev] = [nil, event_resolver] end object.each_event do |ev| event_mappings[ev] = nil end plan.replace_subplan(Hash[self => object, object => nil], event_mappings) initialize_replacement(object) each_event do |event| event.initialize_replacement(nil) { object.event(event.symbol) } end end |
#replace_subplan_by(object) ⇒ 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
1377 1378 1379 1380 1381 1382 1383 1384 1385 |
# File 'lib/roby/task.rb', line 1377 def replace_subplan_by(object) edges = compute_subplan_replacement_operation(object) apply_replacement_operations(edges) initialize_replacement(object) each_event do |event| event.initialize_replacement(object.event(event.symbol)) end end |
#resolve_goals ⇒ Object
35 36 37 38 39 40 |
# File 'lib/roby/state/task.rb', line 35 def resolve_goals if !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
17 18 19 |
# File 'lib/roby/state/task.rb', line 17 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
1274 1275 1276 |
# File 'lib/roby/task.rb', line 1274 def respawn plan.respawn(self) end |
#reusable? ⇒ Boolean
True if this task can be reused by some other parts in the plan
527 528 529 |
# File 'lib/roby/task.rb', line 527 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)
511 |
# File 'lib/roby/task.rb', line 511 def running?; started? && !finished? end |
#script(options = Hash.new, &block) ⇒ Object
Adds a task script that is going to be executed while this task instance runs.
277 278 279 280 281 282 283 284 |
# File 'lib/roby/coordination/task_script.rb', line 277 def script( = Hash.new, &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)
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 |
# File 'lib/roby/task.rb', line 802 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 then [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
1468 1469 1470 1471 1472 |
# File 'lib/roby/task.rb', line 1468 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
363 364 365 366 367 |
# File 'lib/roby/task.rb', line 363 def start_time if ev = start_event.last ev.time end end |
#state ⇒ Object
13 14 15 |
# File 'lib/roby/state/task.rb', line 13 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.
872 873 874 |
# File 'lib/roby/task.rb', line 872 def terminal_events bound_events.each_value.find_all { |ev| ev.terminal? } end |
#to_execution_exception ⇒ Object
1589 1590 1591 |
# File 'lib/roby/task.rb', line 1589 def to_execution_exception ExecutionException.new(LocalizedError.new(self)) end |
#to_s ⇒ Object
:nodoc:
881 882 883 884 885 886 887 888 889 890 891 892 |
# File 'lib/roby/task.rb', line 881 def to_s # :nodoc: s = "#{name}<id:#{droby_id.id}>(#{arguments})" id = owners.map do |owner| next if plan && (owner == plan.local_owner) sibling = remote_siblings[owner] "#{sibling ? Object.address_from_id(sibling.ref).to_s(16) : 'nil'}@#{owner.remote_name}" end unless id.empty? s << "[" << id.join(",") << "]" end s end |
#to_task ⇒ Object
Converts this object into a task object
920 |
# File 'lib/roby/task.rb', line 920 def to_task; self end |
#transition! ⇒ Object
286 287 288 |
# File 'lib/roby/coordination/task_script.rb', line 286 def transition! poll_transition_event.emit end |
#update_task_status(event) ⇒ Object
Call to update the task status because of event
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 |
# File 'lib/roby/task.rb', line 724 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 if event.terminal? @terminal_event ||= event end 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 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.
627 628 629 630 631 632 633 634 |
# File 'lib/roby/task.rb', line 627 def update_terminal_flag # :nodoc: return if !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 return 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
1002 1003 |
# File 'lib/roby/task.rb', line 1002 def updated_data end |
#use_fault_response_table(table_model, arguments = Hash.new) ⇒ Object
Declares that this fault response table should be made active when this task starts, and deactivated when it ends
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 |
# File 'lib/roby/task.rb', line 1129 def use_fault_response_table(table_model, arguments = Hash.new) 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 = Hash.new, &block) ⇒ Object
Register a hook that is called when this task is finalized (removed from its plan)
1493 1494 1495 1496 1497 |
# File 'lib/roby/task.rb', line 1493 def when_finalized( = Hash.new, &block) default = if abstract? then :copy else :drop end , remaining = InstanceHandler. , on_replace: default super(.merge(remaining), &block) end |
#|(task) ⇒ 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
1579 1580 1581 1582 1583 1584 1585 1586 1587 |
# File 'lib/roby/task.rb', line 1579 def |(task) if self.null? task elsif task.null? self else Tasks::Parallel.new << self << task end end |