roby 0.8.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.deep-cover.rb +3 -0
- data/.gitattributes +1 -0
- data/.gitignore +24 -0
- data/.simplecov +10 -0
- data/.travis.yml +17 -0
- data/.yardopts +4 -0
- data/Gemfile +15 -0
- data/README.md +11 -0
- data/Rakefile +47 -177
- data/benchmark/{alloc_misc.rb → attic/alloc_misc.rb} +2 -2
- data/benchmark/{discovery_latency.rb → attic/discovery_latency.rb} +19 -19
- data/benchmark/{garbage_collection.rb → attic/garbage_collection.rb} +9 -9
- data/benchmark/{genom.rb → attic/genom.rb} +0 -0
- data/benchmark/attic/transactions.rb +62 -0
- data/benchmark/plan_basic_operations.rb +28 -0
- data/benchmark/relations/graph.rb +63 -0
- data/benchmark/ruby/identity.rb +18 -0
- data/benchmark/ruby/set_intersect_vs_hash_merge.rb +39 -0
- data/benchmark/ruby/yield_vs_block.rb +35 -0
- data/benchmark/run +5 -0
- data/benchmark/synthetic_plan_modifications_with_transactions.rb +79 -0
- data/benchmark/transactions.rb +99 -51
- data/bin/roby +38 -197
- data/bin/roby-display +14 -0
- data/bin/roby-log +3 -176
- data/doc/guide/{src → attic}/abstraction/achieve_with.page +1 -1
- data/doc/guide/{src → attic}/abstraction/forwarding.page +1 -1
- data/doc/guide/{src → attic}/abstraction/hierarchy.page +1 -1
- data/doc/guide/{src → attic}/abstraction/index.page +1 -1
- data/doc/guide/{src → attic}/abstraction/task_models.page +1 -1
- data/doc/guide/{overview.rdoc → attic/cycle/api_overview.rdoc} +6 -1
- data/doc/guide/{src → attic}/cycle/cycle-overview.png +0 -0
- data/doc/guide/{src → attic}/cycle/cycle-overview.svg +0 -0
- data/doc/guide/attic/cycle/error_handling.page +98 -0
- data/doc/guide/{src → attic}/cycle/error_instantaneous_repair.png +0 -0
- data/doc/guide/{src → attic}/cycle/error_instantaneous_repair.svg +0 -0
- data/doc/guide/{src/cycle/error_handling.page → attic/cycle/error_sources.page} +46 -89
- data/doc/guide/{src → attic}/cycle/garbage_collection.page +1 -1
- data/doc/guide/{src → attic}/cycle/index.page +1 -1
- data/doc/guide/{src → attic}/cycle/propagation.page +11 -1
- data/doc/guide/{src → attic}/cycle/propagation_diamond.png +0 -0
- data/doc/guide/{src → attic}/cycle/propagation_diamond.svg +0 -0
- data/doc/guide/attic/plans/building_plans.page +89 -0
- data/doc/guide/attic/plans/code.page +192 -0
- data/doc/guide/{src/basics → attic/plans}/events.page +3 -4
- data/doc/guide/attic/plans/index.page +7 -0
- data/doc/guide/{plan_modifications.rdoc → attic/plans/plan_modifications.rdoc} +5 -3
- data/doc/guide/{src/basics → attic/plans}/plan_objects.page +2 -1
- data/doc/guide/attic/plans/querying_plans.page +5 -0
- data/doc/guide/{src/basics → attic/plans}/tasks.page +20 -20
- data/doc/guide/config.yaml +7 -4
- data/doc/guide/ext/extended_menu.rb +29 -0
- data/doc/guide/ext/init.rb +6 -0
- data/doc/guide/ext/rdoc_links.rb +7 -6
- data/doc/guide/src/advanced_concepts/history.page +5 -0
- data/doc/guide/src/advanced_concepts/index.page +11 -0
- data/doc/guide/src/advanced_concepts/recognizing_patterns.page +83 -0
- data/doc/guide/src/advanced_concepts/scheduling.page +87 -0
- data/doc/guide/src/advanced_concepts/transactions.page +5 -0
- data/doc/guide/src/advanced_concepts/unreachability.page +42 -0
- data/doc/guide/src/base.template +96 -0
- data/doc/guide/src/basics_shell_header.txt +5 -7
- data/doc/guide/src/building/action_coordination.page +96 -0
- data/doc/guide/src/building/actions.page +124 -0
- data/doc/guide/src/building/file_layout.page +71 -0
- data/doc/guide/src/building/index.page +50 -0
- data/doc/guide/src/building/patterns.page +86 -0
- data/doc/guide/src/building/patterns_forwarding.png +0 -0
- data/doc/guide/src/building/patterns_forwarding.svg +277 -0
- data/doc/guide/src/building/runtime.page +95 -0
- data/doc/guide/src/building/task_models.page +94 -0
- data/doc/guide/src/building/tasks.page +284 -0
- data/doc/guide/src/concepts/error_handling.page +100 -0
- data/doc/guide/src/concepts/exception_propagation.png +0 -0
- data/doc/guide/src/concepts/exception_propagation.svg +445 -0
- data/doc/guide/src/concepts/execution.page +85 -0
- data/doc/guide/src/concepts/execution.png +0 -0
- data/doc/guide/src/concepts/execution.svg +573 -0
- data/doc/guide/src/concepts/execution_cycle.png +0 -0
- data/doc/guide/src/concepts/garbage_collection.page +57 -0
- data/doc/guide/src/concepts/index.page +27 -0
- data/doc/guide/src/concepts/plans.page +101 -0
- data/doc/guide/src/concepts/policy.page +31 -0
- data/doc/guide/src/concepts/reactor.page +61 -0
- data/doc/guide/src/concepts/simple_plan_example.png +0 -0
- data/doc/guide/src/concepts/simple_plan_example.svg +376 -0
- data/doc/guide/src/default.template +9 -74
- data/doc/guide/src/event_relations/forward.page +71 -0
- data/doc/guide/src/event_relations/index.page +12 -0
- data/doc/guide/src/event_relations/scheduling_constraints.page +43 -0
- data/doc/guide/src/event_relations/signal.page +55 -0
- data/doc/guide/src/event_relations/temporal_constraints.page +77 -0
- data/doc/guide/src/htmldoc.metainfo +21 -8
- data/doc/guide/src/index.page +8 -3
- data/doc/guide/src/{introduction/install.page → installation/index.page} +37 -25
- data/doc/guide/src/installation/publications.page +14 -0
- data/doc/guide/src/{introduction → installation}/videos.page +14 -7
- data/doc/guide/src/interacting/index.page +16 -0
- data/doc/guide/src/interacting/run.page +33 -0
- data/doc/guide/src/interacting/shell.page +95 -0
- data/doc/guide/src/plugins/creating_plugins.page +72 -0
- data/doc/guide/src/plugins/index.page +27 -5
- data/doc/guide/src/plugins/{fault_tolerance.page → standard_plugins/fault_tolerance.page} +2 -2
- data/doc/guide/src/plugins/standard_plugins/index.page +11 -0
- data/doc/guide/src/plugins/{subsystems.page → standard_plugins/subsystems.page} +2 -2
- data/doc/guide/src/style_screen.css +687 -0
- data/doc/guide/src/task_relations/dependency.page +107 -0
- data/doc/guide/src/task_relations/executed_by.page +77 -0
- data/doc/guide/src/task_relations/index.page +12 -0
- data/doc/guide/src/task_relations/new_relations.page +119 -0
- data/doc/guide/src/task_relations/planned_by.page +46 -0
- data/doc/guide/src/tutorial/app.page +117 -0
- data/doc/guide/src/{basics → tutorial}/code_examples.page +6 -5
- data/doc/guide/src/{basics → tutorial}/dry.page +15 -15
- data/doc/guide/src/{basics → tutorial}/errors.page +43 -68
- data/doc/guide/src/tutorial/events.page +195 -0
- data/doc/guide/src/{basics → tutorial}/hierarchy.page +53 -52
- data/doc/guide/src/tutorial/index.page +13 -0
- data/doc/guide/src/tutorial/log_replay/goForward_1.png +0 -0
- data/doc/guide/src/tutorial/log_replay/goForward_2.png +0 -0
- data/doc/guide/src/tutorial/log_replay/goForward_3.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/goForward_4.png +0 -0
- data/doc/guide/src/tutorial/log_replay/goForward_5.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/hierarchy_error_1.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/hierarchy_error_2.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/hierarchy_error_3.png +0 -0
- data/doc/guide/src/tutorial/log_replay/moveto_code_error.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/plan_repair_1.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/plan_repair_2.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/plan_repair_3.png +0 -0
- data/doc/guide/src/tutorial/log_replay/plan_repair_4.png +0 -0
- data/doc/guide/src/tutorial/log_replay/roby_log_main_window.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/roby_log_relation_window.png +0 -0
- data/doc/guide/src/{basics → tutorial}/log_replay/roby_replay_event_representation.png +0 -0
- data/doc/guide/src/tutorial/relations_display.page +153 -0
- data/doc/guide/src/{basics → tutorial}/roby_cycle_overview.png +0 -0
- data/doc/guide/src/tutorial/shell.page +121 -0
- data/doc/guide/src/{basics → tutorial}/summary.page +1 -1
- data/doc/guide/src/tutorial/tasks.page +374 -0
- data/lib/roby.rb +102 -47
- data/lib/roby/actions.rb +17 -0
- data/lib/roby/actions/action.rb +80 -0
- data/lib/roby/actions/interface.rb +45 -0
- data/lib/roby/actions/library.rb +23 -0
- data/lib/roby/actions/models/action.rb +224 -0
- data/lib/roby/actions/models/coordination_action.rb +58 -0
- data/lib/roby/actions/models/interface.rb +22 -0
- data/lib/roby/actions/models/interface_base.rb +294 -0
- data/lib/roby/actions/models/library.rb +12 -0
- data/lib/roby/actions/models/method_action.rb +90 -0
- data/lib/roby/actions/task.rb +114 -0
- data/lib/roby/and_generator.rb +125 -0
- data/lib/roby/app.rb +2795 -829
- data/lib/roby/app/autotest_console_reporter.rb +138 -0
- data/lib/roby/app/base.rb +21 -0
- data/lib/roby/app/cucumber.rb +2 -0
- data/lib/roby/app/cucumber/controller.rb +439 -0
- data/lib/roby/app/cucumber/helpers.rb +280 -0
- data/lib/roby/app/cucumber/world.rb +32 -0
- data/lib/roby/app/debug.rb +136 -0
- data/lib/roby/app/gen.rb +2 -0
- data/lib/roby/app/rake.rb +178 -38
- data/lib/roby/app/robot_config.rb +9 -0
- data/lib/roby/app/robot_names.rb +115 -0
- data/lib/roby/app/run.rb +3 -2
- data/lib/roby/app/scripts.rb +72 -0
- data/lib/roby/app/scripts/autotest.rb +173 -0
- data/lib/roby/app/scripts/display.rb +2 -0
- data/lib/roby/app/scripts/restart.rb +52 -0
- data/lib/roby/app/scripts/results.rb +17 -8
- data/lib/roby/app/scripts/run.rb +155 -24
- data/lib/roby/app/scripts/shell.rb +147 -62
- data/lib/roby/app/scripts/test.rb +107 -22
- data/lib/roby/app/test_reporter.rb +74 -0
- data/lib/roby/app/test_server.rb +159 -0
- data/lib/roby/app/vagrant.rb +47 -0
- data/lib/roby/backports.rb +16 -0
- data/lib/roby/cli/display.rb +190 -0
- data/lib/roby/cli/exceptions.rb +17 -0
- data/lib/roby/cli/gen/actions/class.rb +5 -0
- data/lib/roby/cli/gen/actions/test.rb +6 -0
- data/lib/roby/cli/gen/app/.yardopts +6 -0
- data/lib/roby/cli/gen/app/README.md +28 -0
- data/lib/roby/cli/gen/app/Rakefile +15 -0
- data/{app → lib/roby/cli/gen/app}/config/app.yml +29 -39
- data/lib/roby/cli/gen/app/models/.gitattributes +1 -0
- data/{app → lib/roby/cli/gen/app/scripts}/controllers/.gitattributes +0 -0
- data/{app/data/.gitattributes → lib/roby/cli/gen/app/test/.gitignore} +0 -0
- data/lib/roby/cli/gen/class/class.rb +6 -0
- data/lib/roby/cli/gen/class/test.rb +7 -0
- data/lib/roby/cli/gen/helpers.rb +203 -0
- data/lib/roby/cli/gen/module/module.rb +5 -0
- data/lib/roby/cli/gen/module/test.rb +6 -0
- data/lib/roby/cli/gen/roby_app/config/init.rb +17 -0
- data/lib/roby/cli/gen/roby_app/config/robots/robot.rb +40 -0
- data/lib/roby/cli/gen/task/class.rb +44 -0
- data/lib/roby/cli/gen/task/test.rb +6 -0
- data/lib/roby/cli/gen_main.rb +120 -0
- data/lib/roby/cli/log.rb +276 -0
- data/lib/roby/cli/log/flamegraph.html +499 -0
- data/lib/roby/cli/log/flamegraph_renderer.rb +88 -0
- data/lib/roby/cli/main.rb +153 -0
- data/lib/roby/coordination.rb +60 -0
- data/lib/roby/coordination/action_script.rb +25 -0
- data/lib/roby/coordination/action_state_machine.rb +125 -0
- data/lib/roby/coordination/actions.rb +106 -0
- data/lib/roby/coordination/base.rb +145 -0
- data/lib/roby/coordination/calculus.rb +40 -0
- data/lib/roby/coordination/child.rb +28 -0
- data/lib/roby/coordination/event.rb +29 -0
- data/lib/roby/coordination/fault_handler.rb +25 -0
- data/lib/roby/coordination/fault_handling_task.rb +13 -0
- data/lib/roby/coordination/fault_response_table.rb +110 -0
- data/lib/roby/coordination/models/action_script.rb +64 -0
- data/lib/roby/coordination/models/action_state_machine.rb +224 -0
- data/lib/roby/coordination/models/actions.rb +191 -0
- data/lib/roby/coordination/models/arguments.rb +55 -0
- data/lib/roby/coordination/models/base.rb +176 -0
- data/lib/roby/coordination/models/capture.rb +86 -0
- data/lib/roby/coordination/models/child.rb +35 -0
- data/lib/roby/coordination/models/event.rb +41 -0
- data/lib/roby/coordination/models/exceptions.rb +42 -0
- data/lib/roby/coordination/models/fault_handler.rb +219 -0
- data/lib/roby/coordination/models/fault_response_table.rb +77 -0
- data/lib/roby/coordination/models/root.rb +22 -0
- data/lib/roby/coordination/models/script.rb +283 -0
- data/lib/roby/coordination/models/task.rb +184 -0
- data/lib/roby/coordination/models/task_from_action.rb +50 -0
- data/lib/roby/coordination/models/task_from_as_plan.rb +33 -0
- data/lib/roby/coordination/models/task_from_instanciation_object.rb +31 -0
- data/lib/roby/coordination/models/task_from_variable.rb +27 -0
- data/lib/roby/coordination/models/task_with_dependencies.rb +48 -0
- data/lib/roby/coordination/models/variable.rb +32 -0
- data/lib/roby/coordination/script.rb +200 -0
- data/lib/roby/coordination/script_instruction.rb +12 -0
- data/lib/roby/coordination/task.rb +45 -0
- data/lib/roby/coordination/task_base.rb +69 -0
- data/lib/roby/coordination/task_script.rb +293 -0
- data/lib/roby/coordination/task_state_machine.rb +308 -0
- data/lib/roby/decision_control.rb +33 -21
- data/lib/roby/distributed_object.rb +76 -0
- data/lib/roby/droby.rb +17 -0
- data/lib/roby/droby/droby_id.rb +6 -0
- data/lib/roby/droby/enable.rb +153 -0
- data/lib/roby/droby/event_logger.rb +189 -0
- data/lib/roby/droby/event_logging.rb +57 -0
- data/lib/roby/droby/exceptions.rb +14 -0
- data/lib/roby/droby/identifiable.rb +22 -0
- data/lib/roby/droby/logfile.rb +141 -0
- data/lib/roby/droby/logfile/client.rb +176 -0
- data/lib/roby/droby/logfile/file_format.md +97 -0
- data/lib/roby/droby/logfile/index.rb +117 -0
- data/lib/roby/droby/logfile/reader.rb +139 -0
- data/lib/roby/droby/logfile/server.rb +199 -0
- data/lib/roby/droby/logfile/writer.rb +114 -0
- data/lib/roby/droby/marshal.rb +264 -0
- data/lib/roby/droby/marshallable.rb +12 -0
- data/lib/roby/droby/null_event_logger.rb +25 -0
- data/lib/roby/droby/object_manager.rb +205 -0
- data/lib/roby/droby/peer_id.rb +6 -0
- data/lib/roby/droby/plan_rebuilder.rb +373 -0
- data/lib/roby/droby/rebuilt_plan.rb +160 -0
- data/lib/roby/droby/remote_droby_id.rb +6 -0
- data/lib/roby/droby/timepoints.rb +205 -0
- data/lib/roby/droby/timepoints_ctf.metadata.erb +101 -0
- data/lib/roby/droby/timepoints_ctf.rb +125 -0
- data/lib/roby/droby/v5.rb +14 -0
- data/lib/roby/droby/v5/builtin.rb +120 -0
- data/lib/roby/droby/v5/droby_class.rb +45 -0
- data/lib/roby/droby/v5/droby_constant.rb +81 -0
- data/lib/roby/droby/v5/droby_dump.rb +1026 -0
- data/lib/roby/droby/v5/droby_id.rb +44 -0
- data/lib/roby/droby/v5/droby_model.rb +82 -0
- data/lib/roby/droby/v5/peer_id.rb +10 -0
- data/lib/roby/droby/v5/remote_droby_id.rb +42 -0
- data/lib/roby/event.rb +79 -957
- data/lib/roby/event_constraints.rb +835 -0
- data/lib/roby/event_generator.rb +1047 -0
- data/lib/roby/event_structure/causal_link.rb +6 -0
- data/lib/roby/event_structure/forwarding.rb +6 -0
- data/lib/roby/event_structure/precedence.rb +7 -0
- data/lib/roby/event_structure/signal.rb +8 -0
- data/lib/roby/event_structure/temporal_constraints.rb +640 -0
- data/lib/roby/exceptions.rb +446 -152
- data/lib/roby/executable_plan.rb +549 -0
- data/lib/roby/execution_engine.rb +1997 -950
- data/lib/roby/filter_generator.rb +26 -0
- data/lib/roby/gui/chronicle_view.rb +225 -0
- data/lib/roby/gui/chronicle_widget.rb +925 -0
- data/lib/roby/gui/dot_id.rb +11 -0
- data/lib/roby/gui/exception_view.rb +44 -0
- data/lib/roby/gui/log_display.rb +273 -0
- data/lib/roby/gui/model_views.rb +2 -0
- data/lib/roby/gui/model_views/action_interface.rb +53 -0
- data/lib/roby/gui/model_views/task.rb +47 -0
- data/lib/roby/gui/model_views/task.rhtml +41 -0
- data/lib/roby/gui/object_info_view.rb +89 -0
- data/lib/roby/gui/plan_dot_layout.rb +427 -0
- data/lib/roby/gui/plan_rebuilder_widget.rb +357 -0
- data/lib/roby/gui/qt4_toMSecsSinceEpoch.rb +8 -0
- data/lib/roby/gui/relations_view.rb +278 -0
- data/lib/roby/gui/relations_view/relations.ui +139 -0
- data/lib/roby/gui/relations_view/relations_canvas.rb +1088 -0
- data/lib/roby/gui/relations_view/relations_config.rb +292 -0
- data/lib/roby/gui/relations_view/relations_view.ui +53 -0
- data/lib/roby/gui/scheduler_view.css +24 -0
- data/lib/roby/gui/scheduler_view.rb +46 -0
- data/lib/roby/gui/scheduler_view.rhtml +53 -0
- data/lib/roby/gui/stepping.rb +93 -0
- data/lib/roby/gui/stepping.ui +181 -0
- data/lib/roby/gui/styles.rb +81 -0
- data/lib/roby/gui/task_display_configuration.rb +42 -0
- data/lib/roby/gui/task_state_at.rb +38 -0
- data/lib/roby/hooks.rb +26 -0
- data/lib/roby/interface.rb +136 -469
- data/lib/roby/interface/async.rb +20 -0
- data/lib/roby/interface/async/action_monitor.rb +188 -0
- data/lib/roby/interface/async/interface.rb +498 -0
- data/lib/roby/interface/async/job_monitor.rb +213 -0
- data/lib/roby/interface/async/log.rb +238 -0
- data/lib/roby/interface/async/new_job_listener.rb +79 -0
- data/lib/roby/interface/async/ui_connector.rb +183 -0
- data/lib/roby/interface/client.rb +553 -0
- data/lib/roby/interface/command.rb +24 -0
- data/lib/roby/interface/command_argument.rb +16 -0
- data/lib/roby/interface/command_library.rb +92 -0
- data/lib/roby/interface/droby_channel.rb +174 -0
- data/lib/roby/interface/exceptions.rb +22 -0
- data/lib/roby/interface/interface.rb +655 -0
- data/lib/roby/interface/job.rb +47 -0
- data/lib/roby/interface/rest.rb +10 -0
- data/lib/roby/interface/rest/api.rb +29 -0
- data/lib/roby/interface/rest/helpers.rb +24 -0
- data/lib/roby/interface/rest/server.rb +212 -0
- data/lib/roby/interface/server.rb +154 -0
- data/lib/roby/interface/shell_client.rb +468 -0
- data/lib/roby/interface/shell_subcommand.rb +24 -0
- data/lib/roby/interface/subcommand_client.rb +35 -0
- data/lib/roby/interface/tcp.rb +168 -0
- data/lib/roby/models/arguments.rb +112 -0
- data/lib/roby/models/plan_object.rb +83 -0
- data/lib/roby/models/task.rb +835 -0
- data/lib/roby/models/task_event.rb +62 -0
- data/lib/roby/models/task_service.rb +78 -0
- data/lib/roby/or_generator.rb +88 -0
- data/lib/roby/plan.rb +1751 -864
- data/lib/roby/plan_object.rb +611 -0
- data/lib/roby/plan_service.rb +200 -0
- data/lib/roby/promise.rb +332 -0
- data/lib/roby/queries.rb +23 -0
- data/lib/roby/queries/and_matcher.rb +32 -0
- data/lib/roby/queries/any.rb +27 -0
- data/lib/roby/queries/code_error_matcher.rb +58 -0
- data/lib/roby/queries/event_generator_matcher.rb +9 -0
- data/lib/roby/queries/execution_exception_matcher.rb +165 -0
- data/lib/roby/queries/index.rb +165 -0
- data/lib/roby/queries/localized_error_matcher.rb +149 -0
- data/lib/roby/queries/matcher_base.rb +107 -0
- data/lib/roby/queries/none.rb +27 -0
- data/lib/roby/queries/not_matcher.rb +30 -0
- data/lib/roby/queries/op_matcher.rb +8 -0
- data/lib/roby/queries/or_matcher.rb +30 -0
- data/lib/roby/queries/plan_object_matcher.rb +363 -0
- data/lib/roby/queries/query.rb +188 -0
- data/lib/roby/queries/task_event_generator_matcher.rb +86 -0
- data/lib/roby/queries/task_matcher.rb +344 -0
- data/lib/roby/relations.rb +42 -678
- data/lib/roby/relations/bidirectional_directed_adjacency_graph.rb +492 -0
- data/lib/roby/relations/directed_relation_support.rb +268 -0
- data/lib/roby/relations/event_relation_graph.rb +19 -0
- data/lib/roby/relations/fork_merge_visitor.rb +154 -0
- data/lib/roby/relations/graph.rb +533 -0
- data/lib/roby/relations/models/directed_relation_support.rb +11 -0
- data/lib/roby/relations/models/graph.rb +75 -0
- data/lib/roby/relations/models/task_relation_graph.rb +18 -0
- data/lib/roby/relations/space.rb +380 -0
- data/lib/roby/relations/task_relation_graph.rb +20 -0
- data/lib/roby/robot.rb +85 -38
- data/lib/roby/schedulers/basic.rb +155 -25
- data/lib/roby/schedulers/null.rb +20 -0
- data/lib/roby/schedulers/reporting.rb +31 -0
- data/lib/roby/schedulers/state.rb +129 -0
- data/lib/roby/schedulers/temporal.rb +91 -0
- data/lib/roby/singletons.rb +87 -0
- data/lib/roby/standalone.rb +4 -2
- data/lib/roby/standard_errors.rb +405 -82
- data/lib/roby/state.rb +6 -3
- data/lib/roby/state/conf_model.rb +5 -0
- data/lib/roby/state/events.rb +181 -95
- data/lib/roby/state/goal_model.rb +77 -0
- data/lib/roby/state/open_struct.rb +591 -0
- data/lib/roby/state/open_struct_model.rb +68 -0
- data/lib/roby/state/pos.rb +45 -45
- data/lib/roby/state/shapes.rb +11 -11
- data/lib/roby/state/state_model.rb +303 -0
- data/lib/roby/state/task.rb +43 -0
- data/lib/roby/support.rb +88 -148
- data/lib/roby/task.rb +1361 -1750
- data/lib/roby/task_arguments.rb +428 -0
- data/lib/roby/task_event.rb +127 -0
- data/lib/roby/task_event_generator.rb +337 -0
- data/lib/roby/task_service.rb +6 -0
- data/lib/roby/task_structure/conflicts.rb +104 -0
- data/lib/roby/task_structure/dependency.rb +932 -0
- data/lib/roby/task_structure/error_handling.rb +118 -0
- data/lib/roby/task_structure/executed_by.rb +234 -0
- data/lib/roby/task_structure/planned_by.rb +90 -0
- data/lib/roby/tasks/aggregator.rb +37 -0
- data/lib/roby/tasks/external_process.rb +275 -0
- data/lib/roby/tasks/group.rb +27 -0
- data/lib/roby/tasks/null.rb +19 -0
- data/lib/roby/tasks/parallel.rb +43 -0
- data/lib/roby/tasks/sequence.rb +88 -0
- data/lib/roby/tasks/simple.rb +21 -0
- data/lib/roby/{thread_task.rb → tasks/thread.rb} +50 -24
- data/lib/roby/tasks/timeout.rb +17 -0
- data/lib/roby/tasks/virtual.rb +55 -0
- data/lib/roby/template_plan.rb +7 -0
- data/lib/roby/test/aruba_minitest.rb +74 -0
- data/lib/roby/test/assertion.rb +16 -0
- data/lib/roby/test/assertions.rb +490 -0
- data/lib/roby/test/common.rb +368 -591
- data/lib/roby/test/dsl.rb +149 -0
- data/lib/roby/test/error.rb +18 -0
- data/lib/roby/test/event_reporter.rb +83 -0
- data/lib/roby/test/execution_expectations.rb +1134 -0
- data/lib/roby/test/expect_execution.rb +151 -0
- data/lib/roby/test/minitest_helpers.rb +166 -0
- data/lib/roby/test/roby_app_helpers.rb +200 -0
- data/lib/roby/test/run_planners.rb +155 -0
- data/lib/roby/test/self.rb +112 -0
- data/lib/roby/test/spec.rb +198 -0
- data/lib/roby/test/tasks/empty_task.rb +4 -4
- data/lib/roby/test/tasks/goto.rb +28 -27
- data/lib/roby/test/teardown_plans.rb +100 -0
- data/lib/roby/test/testcase.rb +239 -307
- data/lib/roby/test/tools.rb +159 -155
- data/lib/roby/test/validate_state_machine.rb +75 -0
- data/lib/roby/transaction.rb +1125 -0
- data/lib/roby/transaction/event_generator_proxy.rb +63 -0
- data/lib/roby/transaction/plan_object_proxy.rb +99 -0
- data/lib/roby/transaction/plan_service_proxy.rb +43 -0
- data/lib/roby/transaction/proxying.rb +120 -0
- data/lib/roby/transaction/task_event_generator_proxy.rb +19 -0
- data/lib/roby/transaction/task_proxy.rb +135 -0
- data/lib/roby/until_generator.rb +30 -0
- data/lib/roby/version.rb +5 -0
- data/lib/roby/yard.rb +169 -0
- data/lib/yard-roby.rb +1 -0
- data/manifest.xml +32 -6
- data/roby.gemspec +59 -0
- metadata +788 -587
- data/Manifest.txt +0 -321
- data/NOTES +0 -4
- data/README.txt +0 -166
- data/TODO.txt +0 -146
- data/app/README.txt +0 -24
- data/app/Rakefile +0 -8
- data/app/config/ROBOT.rb +0 -5
- data/app/config/init.rb +0 -33
- data/app/config/roby.yml +0 -3
- data/app/controllers/ROBOT.rb +0 -2
- data/app/planners/ROBOT/main.rb +0 -6
- data/app/planners/main.rb +0 -5
- data/app/scripts/distributed +0 -3
- data/app/scripts/generate/bookmarks +0 -3
- data/app/scripts/replay +0 -3
- data/app/scripts/results +0 -3
- data/app/scripts/run +0 -3
- data/app/scripts/server +0 -3
- data/app/scripts/shell +0 -3
- data/app/scripts/test +0 -3
- data/app/tasks/.gitattributes +0 -0
- data/app/tasks/ROBOT/.gitattributes +0 -0
- data/bin/roby-shell +0 -25
- data/doc/guide/src/basics/app.page +0 -139
- data/doc/guide/src/basics/index.page +0 -11
- data/doc/guide/src/basics/log_replay/goForward_1.png +0 -0
- data/doc/guide/src/basics/log_replay/goForward_2.png +0 -0
- data/doc/guide/src/basics/log_replay/goForward_3.png +0 -0
- data/doc/guide/src/basics/log_replay/goForward_5.png +0 -0
- data/doc/guide/src/basics/log_replay/plan_repair_4.png +0 -0
- data/doc/guide/src/basics/log_replay/roby_log_main_window.png +0 -0
- data/doc/guide/src/basics/relations_display.page +0 -203
- data/doc/guide/src/basics/shell.page +0 -102
- data/doc/guide/src/default.css +0 -319
- data/doc/guide/src/introduction/index.page +0 -29
- data/doc/guide/src/introduction/publications.page +0 -14
- data/doc/guide/src/relations/dependency.page +0 -89
- data/doc/guide/src/relations/index.page +0 -12
- data/ext/droby/dump.cc +0 -175
- data/ext/droby/extconf.rb +0 -3
- data/ext/graph/algorithm.cc +0 -746
- data/ext/graph/extconf.rb +0 -7
- data/ext/graph/graph.cc +0 -575
- data/ext/graph/graph.hh +0 -183
- data/ext/graph/iterator_sequence.hh +0 -102
- data/ext/graph/undirected_dfs.hh +0 -226
- data/ext/graph/undirected_graph.hh +0 -421
- data/lib/roby/app/scripts/generate/bookmarks.rb +0 -162
- data/lib/roby/app/scripts/replay.rb +0 -31
- data/lib/roby/app/scripts/server.rb +0 -18
- data/lib/roby/basic_object.rb +0 -151
- data/lib/roby/config.rb +0 -14
- data/lib/roby/distributed.rb +0 -36
- data/lib/roby/distributed/base.rb +0 -448
- data/lib/roby/distributed/communication.rb +0 -875
- data/lib/roby/distributed/connection_space.rb +0 -616
- data/lib/roby/distributed/distributed_object.rb +0 -206
- data/lib/roby/distributed/drb.rb +0 -62
- data/lib/roby/distributed/notifications.rb +0 -531
- data/lib/roby/distributed/peer.rb +0 -555
- data/lib/roby/distributed/protocol.rb +0 -529
- data/lib/roby/distributed/proxy.rb +0 -343
- data/lib/roby/distributed/subscription.rb +0 -311
- data/lib/roby/distributed/transaction.rb +0 -498
- data/lib/roby/external_process_task.rb +0 -225
- data/lib/roby/graph.rb +0 -160
- data/lib/roby/log.rb +0 -3
- data/lib/roby/log/chronicle.rb +0 -303
- data/lib/roby/log/console.rb +0 -74
- data/lib/roby/log/data_stream.rb +0 -275
- data/lib/roby/log/dot.rb +0 -279
- data/lib/roby/log/event_stream.rb +0 -161
- data/lib/roby/log/file.rb +0 -396
- data/lib/roby/log/gui/basic_display.ui +0 -83
- data/lib/roby/log/gui/basic_display_ui.rb +0 -89
- data/lib/roby/log/gui/chronicle.rb +0 -26
- data/lib/roby/log/gui/chronicle_view.rb +0 -40
- data/lib/roby/log/gui/chronicle_view.ui +0 -70
- data/lib/roby/log/gui/chronicle_view_ui.rb +0 -90
- data/lib/roby/log/gui/data_displays.rb +0 -171
- data/lib/roby/log/gui/data_displays.ui +0 -155
- data/lib/roby/log/gui/data_displays_ui.rb +0 -146
- data/lib/roby/log/gui/notifications.rb +0 -26
- data/lib/roby/log/gui/relations.rb +0 -269
- data/lib/roby/log/gui/relations.ui +0 -123
- data/lib/roby/log/gui/relations_ui.rb +0 -120
- data/lib/roby/log/gui/relations_view.rb +0 -185
- data/lib/roby/log/gui/relations_view.ui +0 -149
- data/lib/roby/log/gui/relations_view_ui.rb +0 -144
- data/lib/roby/log/gui/replay.rb +0 -366
- data/lib/roby/log/gui/replay_controls.rb +0 -206
- data/lib/roby/log/gui/replay_controls.ui +0 -282
- data/lib/roby/log/gui/replay_controls_ui.rb +0 -249
- data/lib/roby/log/gui/runtime.rb +0 -130
- data/lib/roby/log/hooks.rb +0 -186
- data/lib/roby/log/logger.rb +0 -203
- data/lib/roby/log/notifications.rb +0 -244
- data/lib/roby/log/plan_rebuilder.rb +0 -468
- data/lib/roby/log/relations.rb +0 -1084
- data/lib/roby/log/server.rb +0 -547
- data/lib/roby/log/sqlite.rb +0 -47
- data/lib/roby/log/timings.rb +0 -233
- data/lib/roby/plan-object.rb +0 -371
- data/lib/roby/planning.rb +0 -13
- data/lib/roby/planning/loops.rb +0 -309
- data/lib/roby/planning/model.rb +0 -1012
- data/lib/roby/planning/task.rb +0 -180
- data/lib/roby/query.rb +0 -655
- data/lib/roby/relations/conflicts.rb +0 -67
- data/lib/roby/relations/dependency.rb +0 -358
- data/lib/roby/relations/ensured.rb +0 -19
- data/lib/roby/relations/error_handling.rb +0 -22
- data/lib/roby/relations/events.rb +0 -7
- data/lib/roby/relations/executed_by.rb +0 -208
- data/lib/roby/relations/influence.rb +0 -10
- data/lib/roby/relations/planned_by.rb +0 -63
- data/lib/roby/state/information.rb +0 -55
- data/lib/roby/state/state.rb +0 -367
- data/lib/roby/task-operations.rb +0 -186
- data/lib/roby/task_index.rb +0 -80
- data/lib/roby/test/distributed.rb +0 -230
- data/lib/roby/test/tasks/simple_task.rb +0 -23
- data/lib/roby/transactions.rb +0 -507
- data/lib/roby/transactions/proxy.rb +0 -325
- data/plugins/fault_injection/History.txt +0 -4
- data/plugins/fault_injection/README.txt +0 -34
- data/plugins/fault_injection/Rakefile +0 -12
- data/plugins/fault_injection/TODO.txt +0 -0
- data/plugins/fault_injection/app.rb +0 -52
- data/plugins/fault_injection/fault_injection.rb +0 -89
- data/plugins/fault_injection/test/test_fault_injection.rb +0 -78
- data/plugins/subsystems/README.txt +0 -37
- data/plugins/subsystems/Rakefile +0 -13
- data/plugins/subsystems/app.rb +0 -182
- data/plugins/subsystems/test/app/README +0 -24
- data/plugins/subsystems/test/app/Rakefile +0 -8
- data/plugins/subsystems/test/app/config/app.yml +0 -71
- data/plugins/subsystems/test/app/config/init.rb +0 -12
- data/plugins/subsystems/test/app/config/roby.yml +0 -3
- data/plugins/subsystems/test/app/planners/main.rb +0 -20
- data/plugins/subsystems/test/app/scripts/distributed +0 -3
- data/plugins/subsystems/test/app/scripts/replay +0 -3
- data/plugins/subsystems/test/app/scripts/results +0 -3
- data/plugins/subsystems/test/app/scripts/run +0 -3
- data/plugins/subsystems/test/app/scripts/server +0 -3
- data/plugins/subsystems/test/app/scripts/shell +0 -3
- data/plugins/subsystems/test/app/scripts/test +0 -3
- data/plugins/subsystems/test/app/tasks/services.rb +0 -15
- data/plugins/subsystems/test/test_subsystems.rb +0 -78
- data/test/distributed/test_communication.rb +0 -195
- data/test/distributed/test_connection.rb +0 -284
- data/test/distributed/test_execution.rb +0 -378
- data/test/distributed/test_mixed_plan.rb +0 -341
- data/test/distributed/test_plan_notifications.rb +0 -238
- data/test/distributed/test_protocol.rb +0 -525
- data/test/distributed/test_query.rb +0 -106
- data/test/distributed/test_remote_plan.rb +0 -491
- data/test/distributed/test_transaction.rb +0 -466
- data/test/mockups/external_process +0 -28
- data/test/mockups/tasks.rb +0 -27
- data/test/planning/test_loops.rb +0 -432
- data/test/planning/test_model.rb +0 -427
- data/test/planning/test_task.rb +0 -126
- data/test/relations/test_conflicts.rb +0 -42
- data/test/relations/test_dependency.rb +0 -324
- data/test/relations/test_ensured.rb +0 -38
- data/test/relations/test_executed_by.rb +0 -224
- data/test/relations/test_planned_by.rb +0 -56
- data/test/suite_core.rb +0 -29
- data/test/suite_distributed.rb +0 -10
- data/test/suite_planning.rb +0 -4
- data/test/suite_relations.rb +0 -8
- data/test/tasks/test_external_process.rb +0 -126
- data/test/tasks/test_thread_task.rb +0 -70
- data/test/test_bgl.rb +0 -528
- data/test/test_event.rb +0 -969
- data/test/test_exceptions.rb +0 -591
- data/test/test_execution_engine.rb +0 -987
- data/test/test_gui.rb +0 -20
- data/test/test_interface.rb +0 -43
- data/test/test_log.rb +0 -125
- data/test/test_log_server.rb +0 -133
- data/test/test_plan.rb +0 -418
- data/test/test_query.rb +0 -424
- data/test/test_relations.rb +0 -260
- data/test/test_state.rb +0 -432
- data/test/test_support.rb +0 -16
- data/test/test_task.rb +0 -1181
- data/test/test_testcase.rb +0 -138
- data/test/test_transactions.rb +0 -610
- data/test/test_transactions_proxy.rb +0 -216
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
module Interface
|
|
3
|
+
# Representation of a subcommand on {Interface} on the shell side
|
|
4
|
+
class ShellSubcommand < SubcommandClient
|
|
5
|
+
def call(options, path, m, *args)
|
|
6
|
+
parent.call(options, [name] + path, m, *args)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def method_missing(m, *args)
|
|
10
|
+
parent.call(Hash.new, [name], m, *args)
|
|
11
|
+
rescue NoMethodError => e
|
|
12
|
+
if e.message =~ /undefined method .#{m}./
|
|
13
|
+
puts "invalid command name #{m}, call 'help #{path.join(".")}' for more information"
|
|
14
|
+
else raise
|
|
15
|
+
end
|
|
16
|
+
rescue ArgumentError => e
|
|
17
|
+
if e.message =~ /wrong number of arguments/ && e.backtrace.first =~ /#{m.to_s}/
|
|
18
|
+
puts e.message
|
|
19
|
+
else raise
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
module Interface
|
|
3
|
+
# Representation of a subcommand on {Interface} on the shell side
|
|
4
|
+
class SubcommandClient
|
|
5
|
+
# @return [ShellClient,ShellSubcommand] the parent shell /
|
|
6
|
+
# subcommand
|
|
7
|
+
attr_reader :parent
|
|
8
|
+
# @return [
|
|
9
|
+
# @return [String] the subcommand name
|
|
10
|
+
attr_reader :name
|
|
11
|
+
# @return [String] the subcommand description
|
|
12
|
+
attr_reader :description
|
|
13
|
+
# @return [String] the set of commands on this subcommand
|
|
14
|
+
attr_reader :commands
|
|
15
|
+
|
|
16
|
+
def initialize(parent, name, description, commands)
|
|
17
|
+
@parent, @name, @description, @commands =
|
|
18
|
+
parent, name, description, commands
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def call(path, m, *args)
|
|
22
|
+
parent.call([name] + path, m, *args)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def path
|
|
26
|
+
parent.path + [name]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def method_missing(m, *args)
|
|
30
|
+
parent.call([name], m, *args)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
require 'socket'
|
|
2
|
+
|
|
3
|
+
module Roby
|
|
4
|
+
module Interface
|
|
5
|
+
# An object that publishes a Roby interface using a TCP server
|
|
6
|
+
class TCPServer
|
|
7
|
+
# @return [Interface] the interface object we give access to
|
|
8
|
+
attr_reader :interface
|
|
9
|
+
# @return [::TCPServer] the TCP server we are accepting from
|
|
10
|
+
attr_reader :server
|
|
11
|
+
# @return [Array<Client>] set of currently active clients
|
|
12
|
+
attr_reader :clients
|
|
13
|
+
# Whether the server handler should warn about disconnections
|
|
14
|
+
attr_predicate :warn_about_disconnection?, true
|
|
15
|
+
# Whether a non-comm-related failure will cause the whole Roby app
|
|
16
|
+
# to quit
|
|
17
|
+
attr_predicate :abort_on_exception?, true
|
|
18
|
+
|
|
19
|
+
# @return [String] the address this interface is bound to
|
|
20
|
+
def ip_address
|
|
21
|
+
server.local_address.ip_address
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [Integer] the port on which this interface runs
|
|
25
|
+
def ip_port
|
|
26
|
+
server.local_address.ip_port
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Creates a new interface server on the given port
|
|
30
|
+
#
|
|
31
|
+
# @param [Integer] port
|
|
32
|
+
def initialize(app, host: nil, port: Roby::Interface::DEFAULT_PORT)
|
|
33
|
+
@app = app
|
|
34
|
+
@interface = Interface.new(app)
|
|
35
|
+
@server =
|
|
36
|
+
begin ::TCPServer.new(host, port)
|
|
37
|
+
rescue TypeError
|
|
38
|
+
raise Errno::EADDRINUSE, "#{port} already in use"
|
|
39
|
+
end
|
|
40
|
+
@clients = Array.new
|
|
41
|
+
@abort_on_exception = true
|
|
42
|
+
@accept_executor = Concurrent::CachedThreadPool.new
|
|
43
|
+
@accept_future = queue_accept_future
|
|
44
|
+
@propagation_handler_id = interface.execution_engine.
|
|
45
|
+
add_propagation_handler(
|
|
46
|
+
description: 'TCPServer#process_pending_requests',
|
|
47
|
+
on_error: :ignore) do
|
|
48
|
+
process_pending_requests
|
|
49
|
+
end
|
|
50
|
+
@warn_about_disconnection = false
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def queue_accept_future
|
|
54
|
+
Concurrent::Future.execute(executor: @accept_executor) do
|
|
55
|
+
socket = @server.accept
|
|
56
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
|
|
57
|
+
socket
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Returns the port this server is bound to
|
|
62
|
+
#
|
|
63
|
+
# @return [Integer]
|
|
64
|
+
def port
|
|
65
|
+
Roby.warn_deprecated "Interface::TCPServer#port is deprecated in favor "\
|
|
66
|
+
"of #ip_port to match ruby's Addrinfo API"
|
|
67
|
+
ip_port
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Creates a server object that will manage the replies on a
|
|
71
|
+
# particular TCP socket
|
|
72
|
+
#
|
|
73
|
+
# @return [Server]
|
|
74
|
+
def create_server(socket)
|
|
75
|
+
Server.new(DRobyChannel.new(socket, false), interface)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Process all incoming connection requests
|
|
79
|
+
#
|
|
80
|
+
# The new clients are added into the Roby event loop
|
|
81
|
+
def process_pending_requests
|
|
82
|
+
if @accept_future.rejected?
|
|
83
|
+
raise @accept_future.reason
|
|
84
|
+
elsif @accept_future.fulfilled?
|
|
85
|
+
clients << create_server(@accept_future.value)
|
|
86
|
+
@accept_future = queue_accept_future
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
exceptions = []
|
|
90
|
+
clients.delete_if do |client|
|
|
91
|
+
begin
|
|
92
|
+
client.poll
|
|
93
|
+
rescue Exception => e
|
|
94
|
+
client.close
|
|
95
|
+
|
|
96
|
+
if warn_about_disconnection?
|
|
97
|
+
Roby::Interface.warn "disconnecting from #{client.client_id}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
next(true) if e.kind_of?(ComError)
|
|
101
|
+
|
|
102
|
+
if abort_on_exception?
|
|
103
|
+
exceptions << e
|
|
104
|
+
else
|
|
105
|
+
Roby.log_exception_with_backtrace(e, Roby::Interface, :warn)
|
|
106
|
+
end
|
|
107
|
+
true
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
raise exceptions.first unless exceptions.empty?
|
|
112
|
+
rescue Exception => e
|
|
113
|
+
if abort_on_exception?
|
|
114
|
+
@app.execution_engine.
|
|
115
|
+
add_framework_error(e, "Interface::TCPServer")
|
|
116
|
+
else
|
|
117
|
+
Roby.log_exception_with_backtrace(e, Roby, :warn)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Closes this server
|
|
122
|
+
def close
|
|
123
|
+
clients.each do |c|
|
|
124
|
+
unless c.closed?
|
|
125
|
+
c.close
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
clients.clear
|
|
129
|
+
if server && !server.closed?
|
|
130
|
+
server.close
|
|
131
|
+
end
|
|
132
|
+
@accept_executor.shutdown
|
|
133
|
+
interface.execution_engine.
|
|
134
|
+
remove_propagation_handler(@propagation_handler_id)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Whether the given client is handled by this server
|
|
138
|
+
def has_client?(client)
|
|
139
|
+
@clients.include?(client)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Connect to a Roby controller interface at this host and port
|
|
144
|
+
#
|
|
145
|
+
# @return [Client] the client object that gives access
|
|
146
|
+
def self.connect_with_tcp_to(host, port = DEFAULT_PORT,
|
|
147
|
+
marshaller: DRoby::Marshal.new(auto_create_plans: true))
|
|
148
|
+
require 'socket'
|
|
149
|
+
socket = TCPSocket.new(host, port)
|
|
150
|
+
addr = socket.addr(true)
|
|
151
|
+
Client.new(DRobyChannel.new(socket, true,
|
|
152
|
+
marshaller: DRoby::Marshal.new(auto_create_plans: true)),
|
|
153
|
+
"#{addr[2]}:#{addr[1]}")
|
|
154
|
+
|
|
155
|
+
rescue Errno::ECONNREFUSED => e
|
|
156
|
+
raise ConnectionError, "failed to connect to #{host}:#{port}: #{e.message}",
|
|
157
|
+
e.backtrace
|
|
158
|
+
rescue SocketError => e
|
|
159
|
+
raise e, "cannot connect to host '#{host}' port '#{port}': #{e.message}",
|
|
160
|
+
e.backtrace
|
|
161
|
+
rescue ::Exception
|
|
162
|
+
if socket && !socket.closed?
|
|
163
|
+
socket.close
|
|
164
|
+
end
|
|
165
|
+
raise
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
module Models
|
|
3
|
+
# Support for argument handling in the relevant models (task services
|
|
4
|
+
# and tasks)
|
|
5
|
+
module Arguments
|
|
6
|
+
extend MetaRuby::Attributes
|
|
7
|
+
|
|
8
|
+
# Representation of one argument
|
|
9
|
+
Argument = Struct.new :name, :default, :doc do
|
|
10
|
+
# Tests whether this argument has a default
|
|
11
|
+
def has_default?
|
|
12
|
+
default != NO_DEFAULT_ARGUMENT
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Tests whether this argument has a delayed argument as default
|
|
16
|
+
def has_delayed_default?
|
|
17
|
+
has_default? && TaskArguments.delayed_argument?(default)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# The set of knwon argument names
|
|
22
|
+
#
|
|
23
|
+
# @return [Set<Symbol>]
|
|
24
|
+
inherited_attribute("argument", "__arguments", map: true) { Hash.new }
|
|
25
|
+
|
|
26
|
+
# @return [Array<String>] the list of arguments required by this task model
|
|
27
|
+
def arguments
|
|
28
|
+
each_argument.map { |name, _| name }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# The null object used in {#argument} to signify that there are no
|
|
32
|
+
# default arguments
|
|
33
|
+
#
|
|
34
|
+
# nil cannot be used as 'nil' is a valid default as well
|
|
35
|
+
NO_DEFAULT_ARGUMENT = Object.new
|
|
36
|
+
def NO_DEFAULT_ARGUMENT.evaluate_delayed_argument
|
|
37
|
+
raise NotImplementedError, "trying to evaluate Roby::Models::Task::NO_DEFAULT_ARGUMENT which is an internal null object"
|
|
38
|
+
end
|
|
39
|
+
NO_DEFAULT_ARGUMENT.freeze
|
|
40
|
+
|
|
41
|
+
# @overload argument(argument_name, options)
|
|
42
|
+
# @param [String] argument_name the name of the new argument
|
|
43
|
+
# @param [Hash] options
|
|
44
|
+
# @param default the default value for this argument. It
|
|
45
|
+
# can either be a plain value (e.g. a number) or one of the
|
|
46
|
+
# delayed arguments (see examples below)
|
|
47
|
+
# @param doc documentation string for the argument. If left
|
|
48
|
+
# to nil, the method will attempt to extract the argument's
|
|
49
|
+
# documentation block.
|
|
50
|
+
#
|
|
51
|
+
# @example getting an argument at runtime from another object
|
|
52
|
+
# argument :target_point, default: from(:planned_task).target_point
|
|
53
|
+
# @example getting an argument at runtime from the global configuration
|
|
54
|
+
# argument :target_point, default: from_conf.target_position
|
|
55
|
+
# @example defining 'nil' as a default value
|
|
56
|
+
# argument :main_direction, default: nil
|
|
57
|
+
def argument(name, default: NO_DEFAULT_ARGUMENT, doc: nil)
|
|
58
|
+
name = name.to_sym
|
|
59
|
+
if !TaskArguments.delayed_argument?(default)
|
|
60
|
+
default = DefaultArgument.new(default)
|
|
61
|
+
end
|
|
62
|
+
doc ||= MetaRuby::DSLs.parse_documentation_block /\.rb$/, 'argument'
|
|
63
|
+
__arguments[name] = Argument.new(name, default, doc)
|
|
64
|
+
|
|
65
|
+
if name =~ /^\w+$/ && !method_defined?(name)
|
|
66
|
+
define_method(name) { arguments[name] }
|
|
67
|
+
define_method("#{name}=") { |value| arguments[name] = value }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Access an argument's default value
|
|
72
|
+
#
|
|
73
|
+
# @param [String] argname the argument name
|
|
74
|
+
# @return [(Boolean,Object)] the first returned value determines
|
|
75
|
+
# whether there is a default defined for the requested argument and
|
|
76
|
+
# the second is that value. Note that the default value can be nil.
|
|
77
|
+
def default_argument(argname)
|
|
78
|
+
if (arg = find_argument(argname)) && arg.has_default?
|
|
79
|
+
return true, arg.default
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# The part of +arguments+ that is meaningful for this task model
|
|
84
|
+
def meaningful_arguments(arguments)
|
|
85
|
+
self_arguments = self.arguments
|
|
86
|
+
result = Hash.new
|
|
87
|
+
arguments.each_assigned_argument do |key, value|
|
|
88
|
+
if self_arguments.include?(key)
|
|
89
|
+
result[key] = value
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
result
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Checks if this model fullfills everything in +models+
|
|
96
|
+
def fullfills?(models)
|
|
97
|
+
if !models.respond_to?(:each)
|
|
98
|
+
models = [models]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
for tag in models
|
|
102
|
+
if !has_ancestor?(tag)
|
|
103
|
+
return false
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
true
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
module Models
|
|
3
|
+
module PlanObject
|
|
4
|
+
include MetaRuby::ModelAsClass
|
|
5
|
+
extend MetaRuby::Attributes
|
|
6
|
+
include Transaction::Proxying::Cache
|
|
7
|
+
|
|
8
|
+
# @return [Array<UnboundMethod>] set of finalization handlers
|
|
9
|
+
# defined at the model level
|
|
10
|
+
# @see PlanObject.when_finalized
|
|
11
|
+
inherited_attribute(:finalization_handler, :finalization_handlers) { Array.new }
|
|
12
|
+
|
|
13
|
+
# Adds a model-level finalization handler, i.e. a handler that will be
|
|
14
|
+
# called on every instance of the class
|
|
15
|
+
#
|
|
16
|
+
# The block is called in the context of the task that got finalized
|
|
17
|
+
# (i.e. in the block, self is this task)
|
|
18
|
+
#
|
|
19
|
+
# @return [void]
|
|
20
|
+
def when_finalized(&block)
|
|
21
|
+
method_name = "finalization_handler_#{block.object_id}"
|
|
22
|
+
define_method(method_name, &block)
|
|
23
|
+
finalization_handlers << instance_method(method_name)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# If true, the backtrace at which a plan object is finalized is
|
|
27
|
+
# stored in this object's {PlanObject#removed_at} attribute.
|
|
28
|
+
#
|
|
29
|
+
# It defaults to false
|
|
30
|
+
#
|
|
31
|
+
# @see PlanObject#finalized!
|
|
32
|
+
attr_predicate :debug_finalization_place?, true
|
|
33
|
+
|
|
34
|
+
# This class method sets up the enclosing class as a child object,
|
|
35
|
+
# with the root object being returned by the given attribute.
|
|
36
|
+
# Task event generators are for instance defined by
|
|
37
|
+
#
|
|
38
|
+
# class TaskEventGenerator < EventGenerator
|
|
39
|
+
# # The task this generator belongs to
|
|
40
|
+
# attr_reader :task
|
|
41
|
+
#
|
|
42
|
+
# child_plan_object :task
|
|
43
|
+
# end
|
|
44
|
+
def child_plan_object(attribute)
|
|
45
|
+
class_eval <<-EOD, __FILE__, __LINE__+1
|
|
46
|
+
def root_object; #{attribute} end
|
|
47
|
+
def root_object?; false end
|
|
48
|
+
def owners; #{attribute}.owners end
|
|
49
|
+
def distribute?; #{attribute}.distribute? end
|
|
50
|
+
def plan; #{attribute}.plan end
|
|
51
|
+
def executable?; #{attribute}.executable? end
|
|
52
|
+
|
|
53
|
+
def subscribed?; #{attribute}.subscribed? end
|
|
54
|
+
def updated?; #{attribute}.updated? end
|
|
55
|
+
def updated_by?(peer); #{attribute}.updated_by?(peer) end
|
|
56
|
+
def update_on?(peer); #{attribute}.update_on?(peer) end
|
|
57
|
+
def updated_peers; #{attribute}.updated_peers end
|
|
58
|
+
def remotely_useful?; #{attribute}.remotely_useful? end
|
|
59
|
+
|
|
60
|
+
def forget_peer(peer)
|
|
61
|
+
remove_sibling_for(peer)
|
|
62
|
+
end
|
|
63
|
+
def sibling_of(remote_object, peer)
|
|
64
|
+
if !distribute?
|
|
65
|
+
raise ArgumentError, "#{self} is local only"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
add_sibling_for(peer, remote_object)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private :plan=
|
|
72
|
+
private :executable=
|
|
73
|
+
EOD
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Create a {Queries::PlanObjectMatcher}
|
|
77
|
+
def match
|
|
78
|
+
Queries::PlanObjectMatcher.new.with_model(self)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
@@ -0,0 +1,835 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
module Models
|
|
3
|
+
module Task
|
|
4
|
+
include MetaRuby::ModelAsClass
|
|
5
|
+
include Models::Arguments
|
|
6
|
+
|
|
7
|
+
# Proxy class used as intermediate by Task.with_arguments
|
|
8
|
+
class AsPlanProxy
|
|
9
|
+
def initialize(model, arguments)
|
|
10
|
+
@model, @arguments = model, arguments
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def as_plan
|
|
14
|
+
@model.as_plan(@arguments)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Template < TemplatePlan
|
|
19
|
+
attr_reader :events_by_name
|
|
20
|
+
attr_accessor :success_events
|
|
21
|
+
attr_accessor :failure_events
|
|
22
|
+
attr_accessor :terminal_events
|
|
23
|
+
|
|
24
|
+
def initialize
|
|
25
|
+
super
|
|
26
|
+
@events_by_name = Hash.new
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class TemplateEventGenerator < EventGenerator
|
|
31
|
+
def initialize(controlable, event_model, plan: Template.new)
|
|
32
|
+
super(controlable, plan: plan)
|
|
33
|
+
@event_model = event_model
|
|
34
|
+
end
|
|
35
|
+
def model
|
|
36
|
+
@event_model
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def invalidate_template
|
|
41
|
+
@template = nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The plan that is used to instantiate this task model
|
|
45
|
+
def template
|
|
46
|
+
return @template if @template
|
|
47
|
+
|
|
48
|
+
template = Template.new
|
|
49
|
+
each_event do |event_name, event_model|
|
|
50
|
+
template.add(event = TemplateEventGenerator.new(event_model.controlable?, event_model, plan: template))
|
|
51
|
+
template.events_by_name[event_name] = event
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
instantiate_event_relations(template)
|
|
55
|
+
@template = template
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def instantiate_event_relations(template)
|
|
59
|
+
events = template.events_by_name
|
|
60
|
+
|
|
61
|
+
all_signals.each do |generator, signalled_events|
|
|
62
|
+
next if signalled_events.empty?
|
|
63
|
+
generator = events[generator]
|
|
64
|
+
|
|
65
|
+
for signalled in signalled_events
|
|
66
|
+
signalled = events[signalled]
|
|
67
|
+
generator.signals signalled
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
all_forwardings.each do |generator, signalled_events|
|
|
72
|
+
next if signalled_events.empty?
|
|
73
|
+
generator = events[generator]
|
|
74
|
+
|
|
75
|
+
for signalled in signalled_events
|
|
76
|
+
signalled = events[signalled]
|
|
77
|
+
generator.forward_to signalled
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
all_causal_links.each do |generator, signalled_events|
|
|
82
|
+
next if signalled_events.empty?
|
|
83
|
+
generator = events[generator]
|
|
84
|
+
|
|
85
|
+
for signalled in signalled_events
|
|
86
|
+
signalled = events[signalled]
|
|
87
|
+
generator.add_causal_link signalled
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Add a link from internal_event to stop if stop is controllable
|
|
92
|
+
if events[:stop].controlable?
|
|
93
|
+
events[:internal_error].signals events[:stop]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
terminal_events, success_events, failure_events =
|
|
97
|
+
compute_terminal_events(events)
|
|
98
|
+
|
|
99
|
+
template.terminal_events = terminal_events
|
|
100
|
+
template.success_events = success_events
|
|
101
|
+
template.failure_events = failure_events
|
|
102
|
+
start_event = events[:start]
|
|
103
|
+
|
|
104
|
+
# WARN: the start event CAN be terminal: it can be a signal from
|
|
105
|
+
# :start to a terminal event
|
|
106
|
+
#
|
|
107
|
+
# Create the precedence relations between 'normal' events and the terminal events
|
|
108
|
+
root_terminal_events = terminal_events.find_all do |ev|
|
|
109
|
+
(ev != start_event) && ev.root?(Roby::EventStructure::Precedence)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
events.each_value do |ev|
|
|
113
|
+
next if ev == start_event
|
|
114
|
+
if !terminal_events.include?(ev)
|
|
115
|
+
if ev.root?(Roby::EventStructure::Precedence)
|
|
116
|
+
start_event.add_precedence(ev)
|
|
117
|
+
end
|
|
118
|
+
if ev.leaf?(Roby::EventStructure::Precedence)
|
|
119
|
+
for terminal in root_terminal_events
|
|
120
|
+
ev.add_precedence(terminal)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def discover_terminal_events(events, terminal_set, set, root)
|
|
128
|
+
stack = [root]
|
|
129
|
+
while !stack.empty?
|
|
130
|
+
vertex = stack.shift
|
|
131
|
+
for relation in [EventStructure::Signal, EventStructure::Forwarding]
|
|
132
|
+
for parent in vertex.parent_objects(relation)
|
|
133
|
+
if !events.include?(parent)
|
|
134
|
+
next
|
|
135
|
+
elsif parent[vertex, relation]
|
|
136
|
+
next
|
|
137
|
+
elsif !terminal_set.include?(parent)
|
|
138
|
+
terminal_set << parent
|
|
139
|
+
set << parent if set
|
|
140
|
+
stack << parent
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def compute_terminal_events(events)
|
|
148
|
+
success_events, failure_events, terminal_events =
|
|
149
|
+
[events[:success]].to_set,
|
|
150
|
+
[events[:failed]].to_set,
|
|
151
|
+
[events[:stop], events[:success], events[:failed]].to_set
|
|
152
|
+
|
|
153
|
+
event_set = events.values.to_set
|
|
154
|
+
discover_terminal_events(event_set, terminal_events, success_events, events[:success])
|
|
155
|
+
discover_terminal_events(event_set, terminal_events, failure_events, events[:failed])
|
|
156
|
+
discover_terminal_events(event_set, terminal_events, nil, events[:stop])
|
|
157
|
+
|
|
158
|
+
events.each_value do |ev|
|
|
159
|
+
if ev.event_model.terminal?
|
|
160
|
+
if !success_events.include?(ev) && !failure_events.include?(ev)
|
|
161
|
+
terminal_events << ev
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
return terminal_events, success_events, failure_events
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# If this class model has an 'as_plan', this specifies what arguments
|
|
170
|
+
# should be passed to as_plan
|
|
171
|
+
def with_arguments(arguments = Hash.new)
|
|
172
|
+
if respond_to?(:as_plan)
|
|
173
|
+
AsPlanProxy.new(self, arguments)
|
|
174
|
+
else
|
|
175
|
+
raise NoMethodError, "#with_arguments is invalid on #self, as #self does not have an #as_plan method"
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Default implementation of the #as_plan method
|
|
180
|
+
#
|
|
181
|
+
# The #as_plan method is used to use task models as representation of
|
|
182
|
+
# abstract actions. For instance, if an #as_plan method is available on
|
|
183
|
+
# a particular MoveTo task model, one can do
|
|
184
|
+
#
|
|
185
|
+
# root.depends_on(MoveTo)
|
|
186
|
+
#
|
|
187
|
+
# This default implementation looks for planning methods declared in the
|
|
188
|
+
# main Roby application planners that return the required task type or
|
|
189
|
+
# one of its subclasses. If one is found, it is using it to generate the
|
|
190
|
+
# action. Otherwise, it falls back to returning a new instance of this
|
|
191
|
+
# task model, unless the model is abstract in which case it raises
|
|
192
|
+
# ArgumentError.
|
|
193
|
+
#
|
|
194
|
+
# It can be used with
|
|
195
|
+
#
|
|
196
|
+
# class TaskModel < Roby::Task
|
|
197
|
+
# end
|
|
198
|
+
#
|
|
199
|
+
# root = Roby::Task.new
|
|
200
|
+
# child = root.depends_on(TaskModel)
|
|
201
|
+
#
|
|
202
|
+
# If arguments need to be given, the #with_arguments method should be
|
|
203
|
+
# used:
|
|
204
|
+
#
|
|
205
|
+
# root = Roby::Task.new
|
|
206
|
+
# child = root.depends_on(TaskModel.with_arguments(id: 200))
|
|
207
|
+
#
|
|
208
|
+
def as_plan(arguments = Hash.new)
|
|
209
|
+
Roby.app.prepare_action(self, **arguments).first
|
|
210
|
+
rescue Application::ActionResolutionError
|
|
211
|
+
if abstract?
|
|
212
|
+
raise Application::ActionResolutionError, "#{self} is abstract and no planning method exists that returns it"
|
|
213
|
+
else
|
|
214
|
+
new(arguments)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# @deprecated
|
|
219
|
+
#
|
|
220
|
+
# Use #each_submodel instead
|
|
221
|
+
def all_models
|
|
222
|
+
submodels
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Clears all definitions saved in this model. This is to be used by the
|
|
226
|
+
# reloading code
|
|
227
|
+
def clear_model
|
|
228
|
+
class_eval do
|
|
229
|
+
# Remove event models
|
|
230
|
+
events.each_key do |ev_symbol|
|
|
231
|
+
remove_const ev_symbol.to_s.camelcase(:upper)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
[@events, @signal_sets, @forwarding_sets, @causal_link_sets,
|
|
235
|
+
@arguments, @handler_sets, @precondition_sets].each do |set|
|
|
236
|
+
set.clear if set
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
super
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Declares an attribute set which follows the task models inheritance
|
|
243
|
+
# hierarchy. Define the corresponding enumeration methods as well.
|
|
244
|
+
#
|
|
245
|
+
# For instance,
|
|
246
|
+
# model_attribute_list 'signal'
|
|
247
|
+
#
|
|
248
|
+
# defines the model-level signals, which can be accessed through
|
|
249
|
+
# .each_signal(model)
|
|
250
|
+
# .signals(model)
|
|
251
|
+
# #each_signal(model)
|
|
252
|
+
#
|
|
253
|
+
def self.model_attribute_list(name) # :nodoc:
|
|
254
|
+
class_eval <<-EOD, __FILE__, __LINE__+1
|
|
255
|
+
inherited_attribute("#{name}_set", "#{name}_sets", map: true) { Hash.new { |h, k| h[k] = Set.new } }
|
|
256
|
+
def each_#{name}(model)
|
|
257
|
+
for obj in #{name}s(model)
|
|
258
|
+
yield(obj)
|
|
259
|
+
end
|
|
260
|
+
self
|
|
261
|
+
end
|
|
262
|
+
def #{name}s(model)
|
|
263
|
+
result = Set.new
|
|
264
|
+
each_#{name}_set(model, false) do |set|
|
|
265
|
+
result.merge set
|
|
266
|
+
end
|
|
267
|
+
result
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def all_#{name}s
|
|
271
|
+
if @all_#{name}s
|
|
272
|
+
@all_#{name}s
|
|
273
|
+
else
|
|
274
|
+
result = Hash.new
|
|
275
|
+
each_#{name}_set do |from, targets|
|
|
276
|
+
result[from] ||= Set.new
|
|
277
|
+
result[from].merge(targets)
|
|
278
|
+
end
|
|
279
|
+
@all_#{name}s = result
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
EOD
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def self.model_relation(name)
|
|
286
|
+
model_attribute_list(name)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
# @!group Event Relations
|
|
291
|
+
|
|
292
|
+
# The set of signals that are registered on this task model
|
|
293
|
+
model_relation 'signal'
|
|
294
|
+
|
|
295
|
+
# The set of forwardings that are registered on this task model
|
|
296
|
+
model_relation('forwarding')
|
|
297
|
+
|
|
298
|
+
# The set of causal links that are registered on this task model
|
|
299
|
+
model_relation('causal_link')
|
|
300
|
+
|
|
301
|
+
# The set of event handlers that are registered on this task model
|
|
302
|
+
model_attribute_list('handler')
|
|
303
|
+
|
|
304
|
+
# The set of precondition handlers that are registered on this task model
|
|
305
|
+
model_attribute_list('precondition')
|
|
306
|
+
|
|
307
|
+
# Establish model-level signals between events of that task. These
|
|
308
|
+
# signals will be established on all the instances of this task model
|
|
309
|
+
# (and its subclasses).
|
|
310
|
+
#
|
|
311
|
+
# Signals cause the target event(s) command to be called when the
|
|
312
|
+
# source event is emitted.
|
|
313
|
+
#
|
|
314
|
+
# @param [Hash<Symbol,Array<Symbol>>,Hash<Symbol,Symbol>] mappings the source-to-target mappings
|
|
315
|
+
# @raise [ArgumentError] if the target event is not controlable,
|
|
316
|
+
# i.e. not have a command
|
|
317
|
+
#
|
|
318
|
+
# @example when establishing multiple relations from the same source use name-to-arrays
|
|
319
|
+
# signal start: [:one, :two]
|
|
320
|
+
def signal(mappings)
|
|
321
|
+
mappings.each do |from, to|
|
|
322
|
+
from = event_model(from)
|
|
323
|
+
targets = Array[*to].map { |ev| event_model(ev) }
|
|
324
|
+
|
|
325
|
+
if from.terminal?
|
|
326
|
+
non_terminal = targets.find_all { |ev| !ev.terminal? }
|
|
327
|
+
if !non_terminal.empty?
|
|
328
|
+
raise ArgumentError, "trying to establish a signal from the terminal event #{from} to the non-terminal events #{non_terminal}"
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
non_controlable = targets.find_all { |ev| !ev.controlable? }
|
|
332
|
+
if !non_controlable.empty?
|
|
333
|
+
raise ArgumentError, "trying to signal #{non_controlable.join(" ")} which is/are not controlable"
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
signal_sets[from.symbol].merge targets.map { |ev| ev.symbol }
|
|
337
|
+
end
|
|
338
|
+
update_terminal_flag
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Establish model-level causal links between events of that task. These
|
|
342
|
+
# signals will be established on all the instances of this task
|
|
343
|
+
# model (and its subclasses).
|
|
344
|
+
#
|
|
345
|
+
# Causal links are used during event propagation to order the
|
|
346
|
+
# propagation properly. Establish a causal link when e.g. an event
|
|
347
|
+
# handler might call or emit on another of this task's event
|
|
348
|
+
#
|
|
349
|
+
# @param [Hash<Symbol,Array<Symbol>>,Hash<Symbol,Symbol>] mappings the source-to-target mappings
|
|
350
|
+
#
|
|
351
|
+
# @example when establishing multiple relations from the same source use name-to-arrays
|
|
352
|
+
# signal start: [:one, :two]
|
|
353
|
+
def causal_link(mappings)
|
|
354
|
+
mappings.each do |from, to|
|
|
355
|
+
from = event_model(from).symbol
|
|
356
|
+
causal_link_sets[from].merge Array[*to].map { |ev| event_model(ev).symbol }
|
|
357
|
+
end
|
|
358
|
+
update_terminal_flag
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# Establish model-level forwarding between events of that task.
|
|
362
|
+
# These relations will be established on all the instances of this
|
|
363
|
+
# task model (and its subclasses).
|
|
364
|
+
#
|
|
365
|
+
# Forwarding is used to cause the target event to be emitted when
|
|
366
|
+
# the source event is.
|
|
367
|
+
#
|
|
368
|
+
# @param [Hash<Symbol,Array<Symbol>>,Hash<Symbol,Symbol>] mappings the source-to-target mappings
|
|
369
|
+
# @example
|
|
370
|
+
# # A task that is stopped as soon as it is started
|
|
371
|
+
# class MyTask < Roby::Task
|
|
372
|
+
# forward start: :stop
|
|
373
|
+
# end
|
|
374
|
+
#
|
|
375
|
+
# @see Task#forward
|
|
376
|
+
# @see EventGenerator#forward.
|
|
377
|
+
# @see Roby::EventStructure::Forward the forwarding relation.
|
|
378
|
+
def forward(mappings)
|
|
379
|
+
mappings.each do |from, to|
|
|
380
|
+
from = event_model(from).symbol
|
|
381
|
+
targets = Array[*to].map { |ev| event_model(ev).symbol }
|
|
382
|
+
|
|
383
|
+
if event_model(from).terminal?
|
|
384
|
+
non_terminal = targets.find_all { |name| !event_model(name).terminal? }
|
|
385
|
+
if !non_terminal.empty?
|
|
386
|
+
raise ArgumentError, "trying to establish a forwarding relation from the terminal event #{from} to the non-terminal event(s) #{targets}"
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
forwarding_sets[from].merge targets
|
|
391
|
+
end
|
|
392
|
+
update_terminal_flag
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# @!endgroup
|
|
396
|
+
|
|
397
|
+
# Helper method to define delayed arguments from related objects
|
|
398
|
+
#
|
|
399
|
+
# @example propagate an argument from a parent task
|
|
400
|
+
# argument :target, default: from(:parent).target
|
|
401
|
+
def from(object)
|
|
402
|
+
if object.kind_of?(Symbol)
|
|
403
|
+
Roby.from(nil).send(object)
|
|
404
|
+
else
|
|
405
|
+
Roby.from(object)
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# Helper method to define delayed arguments from the State object
|
|
410
|
+
#
|
|
411
|
+
# @example get an argument from the State object
|
|
412
|
+
# argument :initial_pose, default: from_state.pose
|
|
413
|
+
def from_state(state_object = State)
|
|
414
|
+
Roby.from_state(state_object)
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
# Declare that tasks of this model can finish by simply emitting
|
|
418
|
+
# stop, i.e. with no specific action.
|
|
419
|
+
#
|
|
420
|
+
# @example
|
|
421
|
+
# class MyTask < Roby::Task
|
|
422
|
+
# terminates
|
|
423
|
+
# end
|
|
424
|
+
#
|
|
425
|
+
def terminates
|
|
426
|
+
event :failed, command: true, terminal: true
|
|
427
|
+
interruptible
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
# Declare that tasks of this model can be interrupted by calling the
|
|
431
|
+
# command of {Roby::Task#failed_event}
|
|
432
|
+
#
|
|
433
|
+
# @raise [ArgumentError] if {Roby::Task#failed_event} is not controlable.
|
|
434
|
+
def interruptible
|
|
435
|
+
if !has_event?(:failed) || !event_model(:failed).controlable?
|
|
436
|
+
raise ArgumentError, "failed is not controlable"
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
event(:stop) do |context|
|
|
440
|
+
if starting?
|
|
441
|
+
start_event.signals stop_event
|
|
442
|
+
return
|
|
443
|
+
end
|
|
444
|
+
failed!(context)
|
|
445
|
+
end
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# True if this task is an abstract task
|
|
449
|
+
#
|
|
450
|
+
# @see abstract
|
|
451
|
+
attr_predicate :abstract?, true
|
|
452
|
+
|
|
453
|
+
# Declare that this task model defines abstract tasks. Abstract
|
|
454
|
+
# tasks can be used to represent an action, without specifically
|
|
455
|
+
# representing how this action should be done.
|
|
456
|
+
#
|
|
457
|
+
# Instances of abstract task models are not executable, i.e. they
|
|
458
|
+
# cannot be started.
|
|
459
|
+
#
|
|
460
|
+
# @see abstract? executable?
|
|
461
|
+
def abstract
|
|
462
|
+
@abstract = true
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
# @api private
|
|
466
|
+
#
|
|
467
|
+
# Update the terminal flag for the event models that are defined in
|
|
468
|
+
# this task model. The event is terminal if model-level signals
|
|
469
|
+
# ({#signal}) or forwards ({#forward}) lead to the
|
|
470
|
+
# emission of {#stop_event}
|
|
471
|
+
def update_terminal_flag # :nodoc:
|
|
472
|
+
events = each_event.map { |name, _| name }
|
|
473
|
+
terminal_events = [:stop]
|
|
474
|
+
events.delete(:stop)
|
|
475
|
+
|
|
476
|
+
loop do
|
|
477
|
+
old_size = terminal_events.size
|
|
478
|
+
events.delete_if do |ev|
|
|
479
|
+
if signals(ev).any? { |sig_ev| terminal_events.include?(sig_ev) } ||
|
|
480
|
+
forwardings(ev).any? { |sig_ev| terminal_events.include?(sig_ev) }
|
|
481
|
+
terminal_events << ev
|
|
482
|
+
true
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
break if old_size == terminal_events.size
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
terminal_events.each do |sym|
|
|
489
|
+
if ev = self.events[sym]
|
|
490
|
+
ev.terminal = true
|
|
491
|
+
else
|
|
492
|
+
ev = superclass.event_model(sym)
|
|
493
|
+
unless ev.terminal?
|
|
494
|
+
event sym, model: ev, terminal: true,
|
|
495
|
+
command: (ev.method(:call) rescue nil)
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
# Defines a new event on this task.
|
|
502
|
+
#
|
|
503
|
+
# @param [Symbol] event_name the event name
|
|
504
|
+
# @param [Hash] options an option hash
|
|
505
|
+
# @option options [Boolean] :controllable if true, the event is
|
|
506
|
+
# controllable and will use the default command of emitting directly
|
|
507
|
+
# in the command
|
|
508
|
+
# @option options [Boolean] :terminal if true, the event is marked as
|
|
509
|
+
# terminal, i.e. it will terminate the task upon emission. Giving this
|
|
510
|
+
# flag is required to redeclare an existing terminal event in a
|
|
511
|
+
# subclass. Otherwise, it is determined automatically by checking
|
|
512
|
+
# whether the event is forwarded to :stop
|
|
513
|
+
# @option options [Class] :model the base class used to create the
|
|
514
|
+
# model for this event. This class is going to be used to generate the
|
|
515
|
+
# event. Defaults to TaskEvent.
|
|
516
|
+
#
|
|
517
|
+
# When a task event (for instance +start+) is emitted, a Roby::TaskEvent
|
|
518
|
+
# object is created to describe the information related to this
|
|
519
|
+
# emission (time, sources, context information, ...). Task.event
|
|
520
|
+
# defines a specific event model MyTask::MyEvent for each task event
|
|
521
|
+
# with name :my_event. This specific model is by default a subclass of
|
|
522
|
+
# Roby::TaskEvent, but it is possible to override that by using the +model+
|
|
523
|
+
# option.
|
|
524
|
+
def event(event_name, options = Hash.new, &block)
|
|
525
|
+
event_name = event_name.to_sym
|
|
526
|
+
|
|
527
|
+
options = validate_options options,
|
|
528
|
+
controlable: nil, command: nil, terminal: nil,
|
|
529
|
+
model: find_event_model(event_name) || Roby::TaskEvent
|
|
530
|
+
|
|
531
|
+
if options.has_key?(:controlable)
|
|
532
|
+
options[:command] = options[:controlable]
|
|
533
|
+
elsif !options.has_key?(:command) && block
|
|
534
|
+
options[:command] = define_command_method(event_name, block)
|
|
535
|
+
end
|
|
536
|
+
validate_event_definition_request(event_name, options)
|
|
537
|
+
|
|
538
|
+
# Define the event class
|
|
539
|
+
new_event = options[:model].new_submodel task_model: self,
|
|
540
|
+
terminal: options[:terminal],
|
|
541
|
+
symbol: event_name, command: options[:command]
|
|
542
|
+
new_event.permanent_model = self.permanent_model?
|
|
543
|
+
|
|
544
|
+
setup_terminal_handler = false
|
|
545
|
+
old_model = find_event_model(event_name)
|
|
546
|
+
if new_event.symbol != :stop && options[:terminal] && (!old_model || !old_model.terminal?)
|
|
547
|
+
setup_terminal_handler = true
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
events[new_event.symbol] = new_event
|
|
551
|
+
if setup_terminal_handler
|
|
552
|
+
forward(new_event => :stop)
|
|
553
|
+
end
|
|
554
|
+
const_set(event_name.to_s.camelcase(:upper), new_event)
|
|
555
|
+
|
|
556
|
+
define_event_methods(event_name)
|
|
557
|
+
new_event
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
# @api private
|
|
561
|
+
#
|
|
562
|
+
# Define the method that will be used as command for the given event
|
|
563
|
+
#
|
|
564
|
+
# @param [Symbol] event_name the event name
|
|
565
|
+
def define_command_method(event_name, block)
|
|
566
|
+
check_arity(block, 1, strict: true)
|
|
567
|
+
define_method("event_command_#{event_name}", &block)
|
|
568
|
+
method = instance_method("event_command_#{event_name}")
|
|
569
|
+
lambda do |dst_task, *event_context|
|
|
570
|
+
method.bind(dst_task).call(*event_context)
|
|
571
|
+
end
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
# @api private
|
|
575
|
+
#
|
|
576
|
+
# Define support methods for a task event
|
|
577
|
+
#
|
|
578
|
+
# @param [Symbol] event_name the event name
|
|
579
|
+
def define_event_methods(event_name)
|
|
580
|
+
event_name = event_name.to_sym
|
|
581
|
+
if !method_defined?("#{event_name}_event")
|
|
582
|
+
define_method("#{event_name}_event") do
|
|
583
|
+
@bound_events[event_name] || event(event_name)
|
|
584
|
+
end
|
|
585
|
+
end
|
|
586
|
+
if !method_defined?("#{event_name}?")
|
|
587
|
+
define_method("#{event_name}?") do
|
|
588
|
+
(@bound_events[event_name] || event(event_name)).emitted?
|
|
589
|
+
end
|
|
590
|
+
end
|
|
591
|
+
if !method_defined?("#{event_name}!")
|
|
592
|
+
define_method("#{event_name}!") do |*context|
|
|
593
|
+
(@bound_events[event_name] || event(event_name)).call(*context)
|
|
594
|
+
end
|
|
595
|
+
end
|
|
596
|
+
if !respond_to?("#{event_name}_event")
|
|
597
|
+
singleton_class.class_eval do
|
|
598
|
+
define_method("#{event_name}_event") do
|
|
599
|
+
find_event_model(event_name)
|
|
600
|
+
end
|
|
601
|
+
end
|
|
602
|
+
end
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
# @api private
|
|
606
|
+
#
|
|
607
|
+
# Validate the parameters passed to {#event}
|
|
608
|
+
#
|
|
609
|
+
# @raise [ArgumentError] if there are inconsistencies / errors in
|
|
610
|
+
# the arguments
|
|
611
|
+
def validate_event_definition_request(event_name, options) #:nodoc:
|
|
612
|
+
if options[:command] && options[:command] != true && !options[:command].respond_to?(:call)
|
|
613
|
+
raise ArgumentError, "Allowed values for :command option: true, false, nil and an object responding to #call. Got #{options[:command]}"
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
if event_name.to_sym == :stop
|
|
617
|
+
if options.has_key?(:terminal) && !options[:terminal]
|
|
618
|
+
raise ArgumentError, "the 'stop' event cannot be non-terminal"
|
|
619
|
+
end
|
|
620
|
+
options[:terminal] = true
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
# Check for inheritance rules
|
|
624
|
+
if events.include?(event_name)
|
|
625
|
+
raise ArgumentError, "event #{event_name} already defined"
|
|
626
|
+
elsif old_event = find_event_model(event_name)
|
|
627
|
+
if old_event.terminal? && !options[:terminal]
|
|
628
|
+
raise ArgumentError, "trying to override #{old_event.symbol} in #{self} which is terminal into a non-terminal event"
|
|
629
|
+
elsif old_event.controlable? && !options[:command]
|
|
630
|
+
raise ArgumentError, "trying to override #{old_event.symbol} in #{self} which is controlable into a non-controlable event"
|
|
631
|
+
end
|
|
632
|
+
end
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
# The events defined by the task model
|
|
636
|
+
#
|
|
637
|
+
# @return [Hash<Symbol,TaskEvent>]
|
|
638
|
+
inherited_attribute(:event, :events, map: true) { Hash.new }
|
|
639
|
+
|
|
640
|
+
def enum_events # :nodoc
|
|
641
|
+
Roby.warn_deprecated "#enum_events is deprecated, use #each_event without a block instead"
|
|
642
|
+
each_event
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
# Get the list of terminal events for this task model
|
|
646
|
+
def terminal_events
|
|
647
|
+
each_event.find_all { |_, e| e.terminal? }.
|
|
648
|
+
map { |_, e| e }
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
# Find the event class for +event+, or nil if +event+ is not an event name for this model
|
|
652
|
+
def find_event_model(name)
|
|
653
|
+
find_event(name.to_sym)
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
# Accesses an event model
|
|
657
|
+
#
|
|
658
|
+
# This method gives access to this task's event models. If given a
|
|
659
|
+
# name, it returns the corresponding event model. If given an event
|
|
660
|
+
# model, it verifies that the model is part of the events of self
|
|
661
|
+
# and returns it.
|
|
662
|
+
#
|
|
663
|
+
# @return [Model<TaskEvent>] a subclass of Roby::TaskEvent
|
|
664
|
+
# @raise [ArgumentError] if the provided event name or model does not
|
|
665
|
+
# exist on self
|
|
666
|
+
def event_model(model_def) #:nodoc:
|
|
667
|
+
if model_def.respond_to?(:to_sym)
|
|
668
|
+
ev_model = find_event_model(model_def.to_sym)
|
|
669
|
+
unless ev_model
|
|
670
|
+
all_events = each_event.map { |name, _| name }
|
|
671
|
+
raise ArgumentError, "#{model_def} is not an event of #{name}: #{all_events}" unless ev_model
|
|
672
|
+
end
|
|
673
|
+
elsif model_def.respond_to?(:has_ancestor?) && model_def.has_ancestor?(Roby::TaskEvent)
|
|
674
|
+
# Check that model_def is an event class for us
|
|
675
|
+
ev_model = find_event_model(model_def.symbol)
|
|
676
|
+
if !ev_model
|
|
677
|
+
raise ArgumentError, "no #{model_def.symbol} event in #{name}"
|
|
678
|
+
elsif ev_model != model_def
|
|
679
|
+
raise ArgumentError, "the event model #{model_def} is not a model for #{name} (found #{ev_model} with the same name)"
|
|
680
|
+
end
|
|
681
|
+
else
|
|
682
|
+
raise ArgumentError, "wanted either a symbol or an event class, got #{model_def}"
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
ev_model
|
|
686
|
+
end
|
|
687
|
+
|
|
688
|
+
# Checks if _name_ is a name for an event of this task
|
|
689
|
+
alias :has_event? :find_event_model
|
|
690
|
+
|
|
691
|
+
private :validate_event_definition_request
|
|
692
|
+
|
|
693
|
+
# Adds an event handler for the given event model. The block is
|
|
694
|
+
# going to be called whenever some events are emitted.
|
|
695
|
+
#
|
|
696
|
+
# Unlike a block given to {EventGenerator#on}, the block is
|
|
697
|
+
# evaluated in the context of the task instance.
|
|
698
|
+
#
|
|
699
|
+
# @param [Array<Symbol>] event_names the name of the events on which
|
|
700
|
+
# to install the handler
|
|
701
|
+
# @yieldparam [Object] context the arguments passed to {Roby::Task#emit}
|
|
702
|
+
# when the event was emitted
|
|
703
|
+
def on(*event_names, &user_handler)
|
|
704
|
+
if !user_handler
|
|
705
|
+
raise ArgumentError, "#on called without a block"
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
check_arity(user_handler, 1, strict: true)
|
|
709
|
+
event_names.each do |from|
|
|
710
|
+
from = event_model(from).symbol
|
|
711
|
+
if user_handler
|
|
712
|
+
method_name = "event_handler_#{from}_#{Object.address_from_id(user_handler.object_id).to_s(16)}"
|
|
713
|
+
define_method(method_name, &user_handler)
|
|
714
|
+
|
|
715
|
+
handler = lambda { |event| event.task.send(method_name, event) }
|
|
716
|
+
handler_sets[from] << EventGenerator::EventHandler.new(handler, false, false)
|
|
717
|
+
end
|
|
718
|
+
end
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
def precondition(event, reason, &block)
|
|
723
|
+
event = event_model(event)
|
|
724
|
+
precondition_sets[event.symbol] << [reason, block]
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
# Returns the lists of tags this model fullfills.
|
|
728
|
+
def provided_services
|
|
729
|
+
ancestors.find_all { |m| m.kind_of?(Models::TaskServiceModel) }
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
# Declares that the given block should be called at each execution
|
|
733
|
+
# cycle, when the task is running. Use it that way:
|
|
734
|
+
#
|
|
735
|
+
# class MyTask < Roby::Task
|
|
736
|
+
# poll do
|
|
737
|
+
# ... do something ...
|
|
738
|
+
# end
|
|
739
|
+
# end
|
|
740
|
+
#
|
|
741
|
+
# If the given polling block raises an exception, the task will be
|
|
742
|
+
# terminated by emitting its +failed+ event.
|
|
743
|
+
def poll(&block)
|
|
744
|
+
if !block_given?
|
|
745
|
+
raise ArgumentError, "no block given"
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
define_method(:poll_handler, &block)
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
# Defines an exception handler.
|
|
752
|
+
#
|
|
753
|
+
# When propagating exceptions, {ExecutionException} goes up in the
|
|
754
|
+
# task hierarchy and calls matching handlers on the tasks it finds,
|
|
755
|
+
# and on their planning task. The first matching handler is called,
|
|
756
|
+
# and the exception propagation assumes that it handled the
|
|
757
|
+
# exception (i.e. won't look for new handlers) unless it calls
|
|
758
|
+
# {Roby::Task#pass_exception}
|
|
759
|
+
#
|
|
760
|
+
# @param [#to_execution_exception_matcher] matcher object for
|
|
761
|
+
# exceptions. Subclasses of {LocalizedError} have it (matching the
|
|
762
|
+
# exception class) as well as {Task} (matches exception origin).
|
|
763
|
+
# See {Roby::Queries} for more advanced exception matchers.
|
|
764
|
+
#
|
|
765
|
+
# @yieldparam [ExecutionException] exception the exception that is
|
|
766
|
+
# being handled
|
|
767
|
+
#
|
|
768
|
+
# @example install a handler for a TaskModelViolation exception
|
|
769
|
+
# on_exception(TaskModelViolation, ...) do |task, exception_object|
|
|
770
|
+
# if cannot_handle
|
|
771
|
+
# task.pass_exception # send to the next handler
|
|
772
|
+
# end
|
|
773
|
+
# do_handle
|
|
774
|
+
# end
|
|
775
|
+
def on_exception(matcher, &handler)
|
|
776
|
+
check_arity(handler, 1, strict: true)
|
|
777
|
+
matcher = matcher.to_execution_exception_matcher
|
|
778
|
+
id = (@@exception_handler_id += 1)
|
|
779
|
+
define_method("exception_handler_#{id}", &handler)
|
|
780
|
+
exception_handlers.unshift [matcher, instance_method("exception_handler_#{id}")]
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
@@exception_handler_id = 0
|
|
784
|
+
|
|
785
|
+
def query(*args)
|
|
786
|
+
q = Queries::Query.new
|
|
787
|
+
if args.empty? && self != Task
|
|
788
|
+
q.which_fullfills(self)
|
|
789
|
+
else
|
|
790
|
+
q.which_fullfills(*args)
|
|
791
|
+
end
|
|
792
|
+
q
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
# Returns a TaskMatcher object that matches this task model
|
|
796
|
+
def match(*args)
|
|
797
|
+
matcher = Queries::TaskMatcher.new
|
|
798
|
+
if args.empty? && self != Task
|
|
799
|
+
matcher.which_fullfills(self)
|
|
800
|
+
else
|
|
801
|
+
matcher.which_fullfills(*args)
|
|
802
|
+
end
|
|
803
|
+
matcher
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
# @return [Queries::ExecutionExceptionMatcher] an exception match
|
|
807
|
+
# object that matches exceptions originating from this task
|
|
808
|
+
def to_execution_exception_matcher
|
|
809
|
+
Queries::ExecutionExceptionMatcher.new.with_origin(self)
|
|
810
|
+
end
|
|
811
|
+
|
|
812
|
+
def fullfills?(models)
|
|
813
|
+
if models.respond_to?(:each)
|
|
814
|
+
models = models.to_a
|
|
815
|
+
else models = [models]
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
models.each do |m|
|
|
819
|
+
m.each_fullfilled_model do |test_m|
|
|
820
|
+
return false if !has_ancestor?(test_m)
|
|
821
|
+
end
|
|
822
|
+
end
|
|
823
|
+
return true
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
def can_merge?(target_model)
|
|
827
|
+
fullfills?(target_model)
|
|
828
|
+
end
|
|
829
|
+
|
|
830
|
+
def to_coordination_task(task_model)
|
|
831
|
+
Roby::Coordination::Models::TaskFromAsPlan.new(self, self)
|
|
832
|
+
end
|
|
833
|
+
end
|
|
834
|
+
end
|
|
835
|
+
end
|