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
|
+
# A command on an {CommandLibrary}
|
|
4
|
+
class Command
|
|
5
|
+
# @return [Symbol] the command name
|
|
6
|
+
attr_reader :name
|
|
7
|
+
# @return [Array<String>] the command description. The first element
|
|
8
|
+
# of the array is used as a command summary
|
|
9
|
+
attr_reader :description
|
|
10
|
+
# @return [Hash<Symbol,CommandArgument>] the set of arguments for
|
|
11
|
+
# this command
|
|
12
|
+
attr_reader :arguments
|
|
13
|
+
|
|
14
|
+
def initialize(name, description, arguments = Hash.new)
|
|
15
|
+
@name, @description, @arguments = name, Array(description), Kernel.normalize_options(arguments)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def droby_dump(peer)
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
module Interface
|
|
3
|
+
# An argument of a {Command}
|
|
4
|
+
class CommandArgument
|
|
5
|
+
# @return [Symbol] the argument name
|
|
6
|
+
attr_reader :name
|
|
7
|
+
# @return [Array<String>] the argument description
|
|
8
|
+
attr_reader :description
|
|
9
|
+
|
|
10
|
+
def initialize(name, description)
|
|
11
|
+
@name, @description = name.to_sym, Array(description)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
module Interface
|
|
3
|
+
# Objects that hold a set of commands
|
|
4
|
+
class CommandLibrary
|
|
5
|
+
class << self
|
|
6
|
+
extend MetaRuby::Attributes
|
|
7
|
+
inherited_attribute(:command, :commands, map: true) { Hash.new }
|
|
8
|
+
inherited_attribute(:subcommand, :subcommands, map: true) { Hash.new }
|
|
9
|
+
|
|
10
|
+
# Declares a command for this interface
|
|
11
|
+
def command(name, *info)
|
|
12
|
+
arguments = if info.last.kind_of?(Hash) then info.pop
|
|
13
|
+
else Hash.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
arguments = arguments.map_key do |name, _|
|
|
17
|
+
name.to_sym
|
|
18
|
+
end
|
|
19
|
+
arguments = arguments.map_value do |name, description|
|
|
20
|
+
CommandArgument.new(name.to_sym, Array(description))
|
|
21
|
+
end
|
|
22
|
+
commands[name.to_sym] = Command.new(name.to_sym, info, arguments)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Adds another interface object a subcommand of this command
|
|
26
|
+
# interface
|
|
27
|
+
#
|
|
28
|
+
# @param [String] name the subcommand name. The commands will be
|
|
29
|
+
# available as name.command_name
|
|
30
|
+
# @param [Model<CommandInterface>] interface the command interface model
|
|
31
|
+
def subcommand(name, interface, *description)
|
|
32
|
+
subcommands[name] = [interface, description]
|
|
33
|
+
define_method name do
|
|
34
|
+
subcommands[name].first
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return [Roby::Application] the application
|
|
40
|
+
attr_reader :app
|
|
41
|
+
# @return [Roby::Plan] the {#app}'s plan
|
|
42
|
+
def plan; app.plan end
|
|
43
|
+
# @return [Roby::ExecutionEngine] the {#plan}'s engine
|
|
44
|
+
def execution_engine; plan.execution_engine end
|
|
45
|
+
# @return [Hash<String,CommandInterface>] the set of command subcommands
|
|
46
|
+
# attached to this command interface
|
|
47
|
+
attr_reader :subcommands
|
|
48
|
+
|
|
49
|
+
def initialize(app)
|
|
50
|
+
@app = app
|
|
51
|
+
@subcommands = Hash.new
|
|
52
|
+
|
|
53
|
+
self.class.each_subcommand do |name, (interface_model, description)|
|
|
54
|
+
subcommand(name, interface_model.new(app), description)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Declare a subcommand on this interface
|
|
59
|
+
#
|
|
60
|
+
# Unless with {CommandLibrary.subcommand}, the interface must
|
|
61
|
+
# already be instanciated
|
|
62
|
+
def subcommand(name, interface, description)
|
|
63
|
+
subcommands[name] = [interface, description]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Enumerate the subcommands available on this interface
|
|
67
|
+
#
|
|
68
|
+
# @yieldparam [String] name the subcommand name
|
|
69
|
+
def each_subcommand
|
|
70
|
+
return enum_for(__method__) if !block_given?
|
|
71
|
+
subcommands.each do |name, (interface, description)|
|
|
72
|
+
yield(name, interface, description)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
InterfaceCommands = Struct.new :name, :description, :commands
|
|
77
|
+
|
|
78
|
+
# The set of commands that exist on self and on its subcommands
|
|
79
|
+
#
|
|
80
|
+
# @return [Hash<String,InterfaceCommands>] the set of commands of
|
|
81
|
+
# self (with key '') and of its subcommands (where the key is not
|
|
82
|
+
# empty)
|
|
83
|
+
def commands
|
|
84
|
+
result = Hash['' => InterfaceCommands.new('', nil, self.class.commands)]
|
|
85
|
+
each_subcommand do |name, interface, description|
|
|
86
|
+
result[name] = InterfaceCommands.new(name, description, interface.commands)
|
|
87
|
+
end
|
|
88
|
+
result
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
module Interface
|
|
3
|
+
# A wrapper on top of raw IO that uses droby marshalling to communicate
|
|
4
|
+
class DRobyChannel
|
|
5
|
+
# @return [#read_nonblock,#write] the channel that allows us to communicate to clients
|
|
6
|
+
attr_reader :io
|
|
7
|
+
# @return [Boolean] true if the local process is the client or the
|
|
8
|
+
# server
|
|
9
|
+
attr_predicate :client?
|
|
10
|
+
# @return [DRoby::Marshal] an object used to marshal or unmarshal
|
|
11
|
+
# objects to/from the connection
|
|
12
|
+
attr_reader :marshaller
|
|
13
|
+
# The maximum byte count that the channel can hold on the write side
|
|
14
|
+
# until it bails out
|
|
15
|
+
attr_reader :max_write_buffer_size
|
|
16
|
+
|
|
17
|
+
def initialize(io, client, marshaller: DRoby::Marshal.new(auto_create_plans: true), max_write_buffer_size: 25*1024**2)
|
|
18
|
+
@io = io
|
|
19
|
+
@io.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
|
|
20
|
+
@client = client
|
|
21
|
+
|
|
22
|
+
@incoming =
|
|
23
|
+
if client?
|
|
24
|
+
WebSocket::Frame::Incoming::Client.new(type: :binary)
|
|
25
|
+
else
|
|
26
|
+
WebSocket::Frame::Incoming::Server.new(type: :binary)
|
|
27
|
+
end
|
|
28
|
+
@marshaller = marshaller
|
|
29
|
+
@max_write_buffer_size = max_write_buffer_size
|
|
30
|
+
@read_buffer = String.new
|
|
31
|
+
@write_buffer = String.new
|
|
32
|
+
@write_thread = nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def write_buffer_size
|
|
36
|
+
@write_buffer.size
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_io
|
|
40
|
+
io.to_io
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def close
|
|
44
|
+
io.close
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def closed?
|
|
48
|
+
io.closed?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def eof?
|
|
52
|
+
io.eof?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def flush
|
|
56
|
+
io.flush
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Wait until there is something to read on the channel
|
|
60
|
+
#
|
|
61
|
+
# @param [Numeric,nil] timeout a timeout after which the method
|
|
62
|
+
# will return. Use nil for no timeout
|
|
63
|
+
# @return [Boolean] falsy if the timeout was reached, true
|
|
64
|
+
# otherwise
|
|
65
|
+
def read_wait(timeout: nil)
|
|
66
|
+
!!IO.select([io], [], [], timeout)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Read one packet from {#io} and unmarshal it
|
|
70
|
+
#
|
|
71
|
+
# @return [Object,nil] returns the unmarshalled object, or nil if no
|
|
72
|
+
# full object can be found in the data received so far
|
|
73
|
+
def read_packet(timeout = 0)
|
|
74
|
+
@read_thread ||= Thread.current
|
|
75
|
+
if @read_thread != Thread.current
|
|
76
|
+
raise InternalError, "cross-thread access to droby channel: from #{@read_thread} to #{Thread.current}"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
deadline = Time.now + timeout if timeout
|
|
80
|
+
remaining_time = timeout
|
|
81
|
+
|
|
82
|
+
if packet = @incoming.next
|
|
83
|
+
return unmarshal_packet(packet)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
while true
|
|
87
|
+
if IO.select([io], [], [], remaining_time)
|
|
88
|
+
begin
|
|
89
|
+
if io.sysread(1024 ** 2, @read_buffer)
|
|
90
|
+
@incoming << @read_buffer
|
|
91
|
+
end
|
|
92
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if packet = @incoming.next
|
|
97
|
+
return unmarshal_packet(packet)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
if deadline
|
|
101
|
+
remaining_time = deadline - Time.now
|
|
102
|
+
return if remaining_time < 0
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
rescue SystemCallError, EOFError, IOError
|
|
107
|
+
raise ComError, "closed communication"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def unmarshal_packet(packet)
|
|
111
|
+
unmarshalled = begin Marshal.load(packet.to_s)
|
|
112
|
+
rescue TypeError => e
|
|
113
|
+
raise ProtocolError, "failed to unmarshal received packet: #{e.message}"
|
|
114
|
+
end
|
|
115
|
+
marshaller.local_object(unmarshalled)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Write one ruby object (usually an array) as a marshalled packet and
|
|
119
|
+
# send it to {#io}
|
|
120
|
+
#
|
|
121
|
+
# @param [Object] object the object to be sent
|
|
122
|
+
# @return [void]
|
|
123
|
+
def write_packet(object)
|
|
124
|
+
marshalled = Marshal.dump(marshaller.dump(object))
|
|
125
|
+
packet =
|
|
126
|
+
if client?
|
|
127
|
+
WebSocket::Frame::Outgoing::Client.new(data: marshalled, type: :binary)
|
|
128
|
+
else
|
|
129
|
+
WebSocket::Frame::Outgoing::Server.new(data: marshalled, type: :binary)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
push_write_data(packet.to_s)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def reset_thread_guard
|
|
136
|
+
@write_thread = nil
|
|
137
|
+
@read_thread = nil
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Push queued data
|
|
141
|
+
#
|
|
142
|
+
# The write I/O is buffered. This method pushes data stored within
|
|
143
|
+
# the internal buffer and/or appends new data to it.
|
|
144
|
+
#
|
|
145
|
+
# @return [Boolean] true if there is still data left in the buffe,
|
|
146
|
+
# false otherwise
|
|
147
|
+
def push_write_data(new_bytes = nil)
|
|
148
|
+
@write_thread ||= Thread.current
|
|
149
|
+
if @write_thread != Thread.current
|
|
150
|
+
raise InternalError, "cross-thread access to droby channel: from #{@write_thread} to #{Thread.current}"
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
@write_buffer.concat(new_bytes) if new_bytes
|
|
154
|
+
written_bytes = io.syswrite(@write_buffer)
|
|
155
|
+
|
|
156
|
+
@write_buffer = @write_buffer[written_bytes..-1]
|
|
157
|
+
!@write_buffer.empty?
|
|
158
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
|
159
|
+
if @write_buffer.size > max_write_buffer_size
|
|
160
|
+
raise ComError, "droby_channel reached an internal buffer size of #{@write_buffer.size}, which is bigger than the limit of #{max_write_buffer_size}, bailing out"
|
|
161
|
+
end
|
|
162
|
+
rescue SystemCallError, IOError, EOFError
|
|
163
|
+
raise ComError, "broken communication channel"
|
|
164
|
+
rescue RuntimeError => e
|
|
165
|
+
# Workaround what seems to be a Ruby bug ...
|
|
166
|
+
if e.message =~ /can.t modify frozen IOError/
|
|
167
|
+
raise ComError, "broken communication channel"
|
|
168
|
+
else raise
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
module Interface
|
|
3
|
+
# Exception thrown when something is wrong in the client/server protocol
|
|
4
|
+
class ProtocolError < RuntimeError
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# Exception thrown when connection cannot be created
|
|
8
|
+
class ConnectionError < RuntimeError
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Exception thrown when the IO channel should be considered as broken
|
|
12
|
+
class ComError < RuntimeError
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Exception thrown when attempting an operation in an unsupported state
|
|
16
|
+
# (such as e.g. calling an operation on an unattached {Async} object
|
|
17
|
+
# that requires the object to be attached
|
|
18
|
+
class InvalidState < RuntimeError
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
module Interface
|
|
3
|
+
# The job's planning task is ready to be executed
|
|
4
|
+
JOB_PLANNING_READY = :planning_ready
|
|
5
|
+
# The job's planning task is running
|
|
6
|
+
JOB_PLANNING = :planning
|
|
7
|
+
# The job's planning task has failed
|
|
8
|
+
JOB_PLANNING_FAILED = :planning_failed
|
|
9
|
+
# The job's main task is ready to be executed
|
|
10
|
+
JOB_READY = :ready
|
|
11
|
+
# The job is started
|
|
12
|
+
JOB_STARTED = :started
|
|
13
|
+
# The job has finished successfully
|
|
14
|
+
JOB_SUCCESS = :success
|
|
15
|
+
# The job has failed
|
|
16
|
+
JOB_FAILED = :failed
|
|
17
|
+
# The job has finished
|
|
18
|
+
JOB_FINISHED = :finished
|
|
19
|
+
# The job has been finalized (i.e. removed from plan)
|
|
20
|
+
JOB_FINALIZED = :finalized
|
|
21
|
+
|
|
22
|
+
# The job has been dropped, i.e. its mission status has been removed
|
|
23
|
+
JOB_DROPPED = :dropped
|
|
24
|
+
|
|
25
|
+
# Initial notification, when the interface starts monitoring a job
|
|
26
|
+
JOB_MONITORED = :monitored
|
|
27
|
+
# The job got replaced by a task that is not this job
|
|
28
|
+
JOB_LOST = :lost
|
|
29
|
+
# The job placeholder task got replaced, and the replacement is managed
|
|
30
|
+
# under the same job
|
|
31
|
+
JOB_REPLACED = :replaced
|
|
32
|
+
|
|
33
|
+
# Whether the given state indicates that the job's planning is finished
|
|
34
|
+
def self.planning_finished_state?(state)
|
|
35
|
+
![JOB_PLANNING_READY, JOB_PLANNING, JOB_FINALIZED].include?(state)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Tests if the given state (one of the JOB_ constants) is terminal, e.g.
|
|
39
|
+
# means that the job is finished
|
|
40
|
+
def self.terminal_state?(state)
|
|
41
|
+
[JOB_PLANNING_FAILED, JOB_FAILED, JOB_SUCCESS, JOB_FINISHED, JOB_FINALIZED].include?(state)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Tests if the given state (one of the JOB_ constants) means that the
|
|
45
|
+
# job finished successfully
|
|
46
|
+
def self.success_state?(state)
|
|
47
|
+
[JOB_SUCCESS].include?(state)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Tests if the given state (one of the JOB_ constants) means that the
|
|
51
|
+
# job finished with error
|
|
52
|
+
def self.error_state?(state)
|
|
53
|
+
[JOB_PLANNING_FAILED, JOB_FAILED].include?(state)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Tests if the given state (one of the JOB_ constants) means that the
|
|
57
|
+
# job is still running
|
|
58
|
+
def self.running_state?(state)
|
|
59
|
+
[JOB_STARTED].include?(state)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Tests if the given state (one of the JOB_ constants) means that the
|
|
63
|
+
# job has been finalized (removed from plan)
|
|
64
|
+
def self.finalized_state?(state)
|
|
65
|
+
[JOB_FINALIZED].include?(state)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# The server-side implementation of the command-based interface
|
|
69
|
+
#
|
|
70
|
+
# This exports all the services and/or APIs that are available through e.g.
|
|
71
|
+
# the Roby shell. It does not do any marshalling/demarshalling
|
|
72
|
+
#
|
|
73
|
+
# Most methods can be accessed outside of the Roby execution thread. Methods
|
|
74
|
+
# that cannot will be noted in their documentation
|
|
75
|
+
#
|
|
76
|
+
# == About job management
|
|
77
|
+
# One of the tasks of this class is to do job management. Jobs are the
|
|
78
|
+
# unit that is used to interact with a running Roby instance at a high
|
|
79
|
+
# level, as e.g. through a shell or a GUI. In Roby, jobs are represented
|
|
80
|
+
# by tasks that provide the {Job} task service and have a
|
|
81
|
+
# non-nil job ID. Up to two tasks can be associated with the job. The
|
|
82
|
+
# first is obviously the job task itself, i.e. the task that provides
|
|
83
|
+
# {Job}. Quite often, the job task will be a planning task
|
|
84
|
+
# (actually, one can see that {Actions::Task} provides
|
|
85
|
+
# {Job}). In this case, the planned task will be also
|
|
86
|
+
# associated with the job as its placeholder: while the job task
|
|
87
|
+
# represents the job's deployment status, the placeholder task will
|
|
88
|
+
# represent the job's execution status.
|
|
89
|
+
class Interface < CommandLibrary
|
|
90
|
+
# @return [#call] the blocks that listen to job notifications. They are
|
|
91
|
+
# added with {#on_job_notification} and removed with
|
|
92
|
+
# {#remove_job_listener}
|
|
93
|
+
attr_reader :job_listeners
|
|
94
|
+
|
|
95
|
+
# @return [#call] the blocks that listen to end-of-cycle
|
|
96
|
+
# notifications. They are added with {#on_cycle_end} and
|
|
97
|
+
# removed with {#remove_cycle_end}
|
|
98
|
+
attr_reader :cycle_end_listeners
|
|
99
|
+
|
|
100
|
+
# @api private
|
|
101
|
+
#
|
|
102
|
+
# @return [Set<Integer>] the set of tracked jobs
|
|
103
|
+
# @see tracked_job?
|
|
104
|
+
attr_reader :tracked_jobs
|
|
105
|
+
# @api private
|
|
106
|
+
#
|
|
107
|
+
# The set of pending job notifications for this cycle
|
|
108
|
+
attr_reader :job_notifications
|
|
109
|
+
|
|
110
|
+
# Creates an interface from an existing Roby application
|
|
111
|
+
#
|
|
112
|
+
# @param [Roby::Application] app the application
|
|
113
|
+
def initialize(app)
|
|
114
|
+
super(app)
|
|
115
|
+
app.plan.add_trigger Roby::Interface::Job do |task|
|
|
116
|
+
if task.job_id && (planned_task = task.planned_task)
|
|
117
|
+
monitor_job(task, planned_task, new_task: true)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
execution_engine.at_cycle_end do
|
|
121
|
+
push_pending_job_notifications
|
|
122
|
+
notify_cycle_end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
@tracked_jobs = Set.new
|
|
126
|
+
@job_notifications = Array.new
|
|
127
|
+
@job_listeners = Array.new
|
|
128
|
+
@job_monitoring_state = Hash.new
|
|
129
|
+
@cycle_end_listeners = Array.new
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
State = Struct.new :service, :monitored, :job_id, :job_name do
|
|
133
|
+
def monitored?
|
|
134
|
+
monitored
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Returns the port of the log server
|
|
139
|
+
#
|
|
140
|
+
# @return [Integer,nil] the port, or nil if there is no log server
|
|
141
|
+
def log_server_port
|
|
142
|
+
app.log_server_port
|
|
143
|
+
end
|
|
144
|
+
command :log_server_port, 'returns the port of the log server',
|
|
145
|
+
advanced: true
|
|
146
|
+
|
|
147
|
+
# The set of actions available on {#app}
|
|
148
|
+
#
|
|
149
|
+
# @return [Array<Roby::Actions::Models::Action>]
|
|
150
|
+
def actions
|
|
151
|
+
result = []
|
|
152
|
+
app.planners.each do |planner_model|
|
|
153
|
+
planner_model.each_registered_action do |_, act|
|
|
154
|
+
result << act
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
result
|
|
158
|
+
end
|
|
159
|
+
command :actions, 'lists a summary of the available actions'
|
|
160
|
+
|
|
161
|
+
# Starts a job
|
|
162
|
+
#
|
|
163
|
+
# @return [Integer] the job ID
|
|
164
|
+
def start_job(m, arguments = Hash.new)
|
|
165
|
+
execution_engine.execute do
|
|
166
|
+
task, planning_task = app.prepare_action(m, mission: true, job_id: Job.allocate_job_id, **arguments)
|
|
167
|
+
planning_task.job_id
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Kill a job
|
|
172
|
+
#
|
|
173
|
+
# It removes the job from the list of missions and kills the job's
|
|
174
|
+
# main task
|
|
175
|
+
#
|
|
176
|
+
# @param [Integer] job_id the ID of the job that should be
|
|
177
|
+
# terminated
|
|
178
|
+
# @return [Boolean] true if the job was found and terminated, and
|
|
179
|
+
# false otherwise
|
|
180
|
+
# @see drop_job
|
|
181
|
+
def kill_job(job_id)
|
|
182
|
+
if task = find_job_placeholder_by_id(job_id)
|
|
183
|
+
plan.unmark_mission_task(task)
|
|
184
|
+
task.stop! if task.running?
|
|
185
|
+
true
|
|
186
|
+
else false
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
command :kill_job, 'forcefully kills the given job',
|
|
190
|
+
job_id: 'the job ID. It is the return value of the xxx! command and can also be obtained by calling jobs'
|
|
191
|
+
|
|
192
|
+
# Drop a job
|
|
193
|
+
#
|
|
194
|
+
# It removes the job from the list of missions but does not
|
|
195
|
+
# explicitely kill it
|
|
196
|
+
#
|
|
197
|
+
# @param [Integer] job_id the ID of the job that should be
|
|
198
|
+
# terminated
|
|
199
|
+
# @return [Boolean] true if the job was found and terminated, and
|
|
200
|
+
# false otherwise
|
|
201
|
+
# @see kill_job
|
|
202
|
+
def drop_job(job_id)
|
|
203
|
+
return if !(task = find_job_by_id(job_id))
|
|
204
|
+
|
|
205
|
+
placeholder_task = task.planned_task
|
|
206
|
+
if !placeholder_task
|
|
207
|
+
plan.unmark_mission_task(task)
|
|
208
|
+
return true
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
placeholder_task.remove_planning_task(task)
|
|
212
|
+
if job_ids_of_task(placeholder_task).empty?
|
|
213
|
+
plan.unmark_mission_task(placeholder_task)
|
|
214
|
+
true
|
|
215
|
+
else false
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
command :drop_job, "remove this job from the list of jobs, this does not necessarily kill the job's main task",
|
|
219
|
+
job_id: 'the job ID. It is the return value of the xxx! command and can also be obtained by calling jobs'
|
|
220
|
+
|
|
221
|
+
# Enumerates the job listeners currently registered through
|
|
222
|
+
# {#on_job_notification}
|
|
223
|
+
#
|
|
224
|
+
# @yieldparam [#call] the job listener object
|
|
225
|
+
def each_job_listener(&block)
|
|
226
|
+
job_listeners.each(&block)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Dispatch the given job-related notification to all listeners
|
|
230
|
+
#
|
|
231
|
+
# Listeners are registered with {#on_job_notification}
|
|
232
|
+
def job_notify(kind, job_id, job_name, *args)
|
|
233
|
+
job_notifications << [kind, job_id, job_name, args]
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# @api private
|
|
237
|
+
#
|
|
238
|
+
# Called in at_cycle_end to push job notifications
|
|
239
|
+
def push_pending_job_notifications
|
|
240
|
+
final_tracked_jobs = tracked_jobs.dup
|
|
241
|
+
|
|
242
|
+
# Re-track jobs for which we have a recapture event
|
|
243
|
+
job_notifications.each do |event, job_id, *|
|
|
244
|
+
if event == JOB_MONITORED
|
|
245
|
+
tracked_jobs << job_id
|
|
246
|
+
final_tracked_jobs << job_id
|
|
247
|
+
elsif event == JOB_DROPPED || event == JOB_LOST || event == JOB_FINALIZED
|
|
248
|
+
final_tracked_jobs.delete(job_id)
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
job_notifications = self.job_notifications.find_all do |event, job_id, *|
|
|
253
|
+
if event == JOB_DROPPED
|
|
254
|
+
!final_tracked_jobs.include?(job_id)
|
|
255
|
+
else
|
|
256
|
+
tracked_jobs.include?(job_id)
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
self.job_notifications.clear
|
|
260
|
+
|
|
261
|
+
each_job_listener do |listener|
|
|
262
|
+
job_notifications.each do |kind, job_id, job_name, args|
|
|
263
|
+
listener.call(kind, job_id, job_name, *args)
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
@tracked_jobs = final_tracked_jobs
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# (see Application#on_ui_event)
|
|
271
|
+
def on_ui_event(&block)
|
|
272
|
+
app.on_ui_event(&block)
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# (see Application#remove_ui_event_listener)
|
|
276
|
+
def remove_ui_event_listener(block)
|
|
277
|
+
app.remove_ui_event_listener(block)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# (see Application#on_notification)
|
|
281
|
+
def on_notification(&block)
|
|
282
|
+
app.on_notification(&block)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# (see Application#remove_notification_listener)
|
|
286
|
+
def remove_notification_listener(listener)
|
|
287
|
+
app.remove_notification_listener(listener)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Registers a block to be called when a job changes state
|
|
291
|
+
#
|
|
292
|
+
# All callbacks will be called with at minimum
|
|
293
|
+
#
|
|
294
|
+
# @overload on_job_notification
|
|
295
|
+
# @yieldparam kind one of the JOB_* constants
|
|
296
|
+
# @yieldparam [Integer] job_id the job ID (unique)
|
|
297
|
+
# @yieldparam [String] job_name the job name (non-unique)
|
|
298
|
+
#
|
|
299
|
+
# Generic interface. Some of the notifications, detailed below,
|
|
300
|
+
# have additional parameters (after the job_name argument)
|
|
301
|
+
#
|
|
302
|
+
# @overload on_job_notification
|
|
303
|
+
# @yieldparam JOB_MONITORED
|
|
304
|
+
# @yieldparam [Integer] job_id the job ID (unique)
|
|
305
|
+
# @yieldparam [String] job_name the job name (non-unique)
|
|
306
|
+
# @yieldparam [Task] task the job's placeholder task
|
|
307
|
+
# @yieldparam [Task] job_task the job task
|
|
308
|
+
#
|
|
309
|
+
# Interface for JOB_MONITORED notifications, called when the job
|
|
310
|
+
# task is initially detected
|
|
311
|
+
#
|
|
312
|
+
# @overload on_job_notification
|
|
313
|
+
# @yieldparam JOB_REPLACED or JOB_LOST
|
|
314
|
+
# @yieldparam [Integer] job_id the job ID (unique)
|
|
315
|
+
# @yieldparam [String] job_name the job name (non-unique)
|
|
316
|
+
# @yieldparam [Task] task the new task this job is now tracking
|
|
317
|
+
#
|
|
318
|
+
# Interface for JOB_REPLACED and JOB_LOST notifications
|
|
319
|
+
#
|
|
320
|
+
# @return [Object] the listener ID that can be given to
|
|
321
|
+
# {#remove_job_listener}
|
|
322
|
+
def on_job_notification(&block)
|
|
323
|
+
job_listeners << block
|
|
324
|
+
block
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# Remove a job listener added with {#on_job_notification}
|
|
328
|
+
#
|
|
329
|
+
# @param [Object] listener the listener ID returned by
|
|
330
|
+
# {#on_job_notification}
|
|
331
|
+
def remove_job_listener(listener)
|
|
332
|
+
job_listeners.delete(listener)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Returns all the job IDs of this task
|
|
336
|
+
#
|
|
337
|
+
# @param [Roby::Task] task the job task itself, or its placeholder
|
|
338
|
+
# task
|
|
339
|
+
# @return [Array<Integer>] the task's job IDs. May be empty if
|
|
340
|
+
# the task is not a job task, or if its job ID is not set
|
|
341
|
+
def job_ids_of_task(task)
|
|
342
|
+
if task.fullfills?(Job)
|
|
343
|
+
[task.job_id]
|
|
344
|
+
else
|
|
345
|
+
task.each_planning_task.map do |planning_task|
|
|
346
|
+
if planning_task.fullfills?(Job)
|
|
347
|
+
planning_task.job_id
|
|
348
|
+
end
|
|
349
|
+
end.compact
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Returns the job ID of a task, where the task can either be a
|
|
354
|
+
# placeholder for the job or the job task itself
|
|
355
|
+
#
|
|
356
|
+
# @return [Integer,nil] the task's job ID or nil if (1) the task is
|
|
357
|
+
# not a job task or (2) its job ID is not set
|
|
358
|
+
def job_id_of_task(task)
|
|
359
|
+
job_ids_of_task(task).first
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Monitor the given task as a job
|
|
363
|
+
#
|
|
364
|
+
# It must be called within the Roby execution thread
|
|
365
|
+
def monitor_job(planning_task, task, new_task: false)
|
|
366
|
+
# NOTE: this method MUST queue job notifications
|
|
367
|
+
# UNCONDITIONALLY. Job tracking is done on a per-cycle basis (in
|
|
368
|
+
# at_cycle_end) by {#push_pending_job_notifications}
|
|
369
|
+
|
|
370
|
+
job_id = planning_task.job_id
|
|
371
|
+
job_name = planning_task.job_name
|
|
372
|
+
|
|
373
|
+
# This happens when a placeholder/planning pair is replaced by
|
|
374
|
+
# another, but the job ID is inherited. We do this when e.g.
|
|
375
|
+
# running an action that returns another planning pair
|
|
376
|
+
if (state = @job_monitoring_state[job_id])
|
|
377
|
+
track_planning_state(
|
|
378
|
+
state.job_id, state.job_name, state.service, planning_task)
|
|
379
|
+
return
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
service = PlanService.new(task)
|
|
383
|
+
@job_monitoring_state[job_id] =
|
|
384
|
+
State.new(service, false, job_id, job_name)
|
|
385
|
+
service.when_finalized do
|
|
386
|
+
@job_monitoring_state.delete(job_id)
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
service.on_plan_status_change(initial: true) do |status|
|
|
390
|
+
state = @job_monitoring_state[job_id]
|
|
391
|
+
if !state.monitored? && (status == :mission)
|
|
392
|
+
job_notify(JOB_MONITORED, job_id, job_name, service.task,
|
|
393
|
+
service.task.planning_task)
|
|
394
|
+
job_notify(job_state(service.task), job_id, job_name)
|
|
395
|
+
state.monitored = true
|
|
396
|
+
elsif state.monitored? && (status != :mission)
|
|
397
|
+
job_notify(JOB_DROPPED, job_id, job_name)
|
|
398
|
+
state.monitored = false
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
track_planning_state(job_id, job_name, service, planning_task)
|
|
403
|
+
|
|
404
|
+
service.on_replacement do |_current, new|
|
|
405
|
+
if plan.mission_task?(new) && job_ids_of_task(new).include?(job_id)
|
|
406
|
+
job_notify(JOB_REPLACED, job_id, job_name, new)
|
|
407
|
+
job_notify(job_state(new), job_id, job_name)
|
|
408
|
+
else
|
|
409
|
+
job_notify(JOB_LOST, job_id, job_name, new)
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
service.on(:start) do |ev|
|
|
413
|
+
job_notify(JOB_STARTED, job_id, job_name)
|
|
414
|
+
end
|
|
415
|
+
service.on(:success) do |ev|
|
|
416
|
+
job_notify(JOB_SUCCESS, job_id, job_name)
|
|
417
|
+
end
|
|
418
|
+
service.on(:failed) do |ev|
|
|
419
|
+
job_notify(JOB_FAILED, job_id, job_name)
|
|
420
|
+
end
|
|
421
|
+
service.when_finalized do
|
|
422
|
+
job_notify(JOB_FINALIZED, job_id, job_name)
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
private def track_planning_state(job_id, job_name, service, planning_task)
|
|
427
|
+
planning_task.start_event.on do |ev|
|
|
428
|
+
job_task = planning_task.planned_task
|
|
429
|
+
if job_task == service.task
|
|
430
|
+
job_notify(JOB_PLANNING, job_id, job_name)
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
planning_task.success_event.on do |ev|
|
|
434
|
+
job_task = planning_task.planned_task
|
|
435
|
+
if job_task == service.task
|
|
436
|
+
if job_task.pending? || job_task.starting?
|
|
437
|
+
job_notify(JOB_READY, job_id, job_name)
|
|
438
|
+
end
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
planning_task.stop_event.on do |ev|
|
|
442
|
+
job_task = planning_task.planned_task
|
|
443
|
+
if job_task == service.task && !ev.task.success?
|
|
444
|
+
job_notify(JOB_PLANNING_FAILED, job_id, job_name)
|
|
445
|
+
end
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
PlanService.new(planning_task).when_finalized do
|
|
449
|
+
job_task = planning_task.planned_task
|
|
450
|
+
if job_task == service.task
|
|
451
|
+
job_notify(JOB_FINALIZED, job_id, job_name)
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def job_state(task)
|
|
457
|
+
if !task.plan
|
|
458
|
+
return JOB_FINALIZED
|
|
459
|
+
elsif !plan.mission_task?(task)
|
|
460
|
+
return JOB_DROPPED
|
|
461
|
+
elsif task.success_event.emitted?
|
|
462
|
+
return JOB_SUCCESS
|
|
463
|
+
elsif task.failed_event.emitted?
|
|
464
|
+
return JOB_FAILED
|
|
465
|
+
elsif task.stop_event.emitted?
|
|
466
|
+
return JOB_FINISHED
|
|
467
|
+
elsif task.running?
|
|
468
|
+
return JOB_STARTED
|
|
469
|
+
elsif task.pending?
|
|
470
|
+
if planner = task.planning_task
|
|
471
|
+
if planner.success?
|
|
472
|
+
return JOB_READY
|
|
473
|
+
elsif planner.stop?
|
|
474
|
+
return JOB_PLANNING_FAILED
|
|
475
|
+
elsif planner.running?
|
|
476
|
+
return JOB_PLANNING
|
|
477
|
+
else
|
|
478
|
+
return JOB_PLANNING_READY
|
|
479
|
+
end
|
|
480
|
+
else return JOB_READY
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
# The jobs currently running on {#app}'s plan
|
|
486
|
+
#
|
|
487
|
+
# @return [Hash<Integer,(Symbol,Roby::Task,Roby::Task)>] the mapping
|
|
488
|
+
# from job ID to the job's state (as returned by {#job_state}), the
|
|
489
|
+
# placeholder job task and the job task itself
|
|
490
|
+
def jobs
|
|
491
|
+
result = Hash.new
|
|
492
|
+
execution_engine.execute do
|
|
493
|
+
planning_tasks = plan.find_tasks(Job).to_a
|
|
494
|
+
planning_tasks.each do |job_task|
|
|
495
|
+
job_id = job_task.job_id
|
|
496
|
+
next if !job_id
|
|
497
|
+
placeholder_job_task = job_task.planned_task || job_task
|
|
498
|
+
result[job_id] = [job_state(placeholder_job_task), placeholder_job_task, job_task]
|
|
499
|
+
end
|
|
500
|
+
end
|
|
501
|
+
result
|
|
502
|
+
end
|
|
503
|
+
command :jobs, 'returns the list of non-finished jobs'
|
|
504
|
+
|
|
505
|
+
def find_job_info_by_id(id)
|
|
506
|
+
execution_engine.execute do
|
|
507
|
+
if planning_task = plan.find_tasks(Job).with_arguments(job_id: id).to_a.first
|
|
508
|
+
task = planning_task.planned_task || planning_task
|
|
509
|
+
return job_state(task), task, planning_task
|
|
510
|
+
end
|
|
511
|
+
end
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
# Finds a job task by its ID
|
|
515
|
+
#
|
|
516
|
+
# @param [Integer] id
|
|
517
|
+
# @return [Roby::Task,nil]
|
|
518
|
+
def find_job_by_id(id)
|
|
519
|
+
execution_engine.execute do
|
|
520
|
+
return plan.find_tasks(Job).with_arguments(job_id: id).to_a.first
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
# Finds the task that represents the given job ID
|
|
525
|
+
#
|
|
526
|
+
# It can be different than the job task when e.g. the job task is a
|
|
527
|
+
# planning task
|
|
528
|
+
def find_job_placeholder_by_id(id)
|
|
529
|
+
if task = find_job_by_id(id)
|
|
530
|
+
return task.planned_task || task
|
|
531
|
+
end
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
# Reload all models from this Roby application
|
|
535
|
+
#
|
|
536
|
+
# Do NOT do this while the robot does critical things
|
|
537
|
+
def reload_models
|
|
538
|
+
execution_engine.execute do
|
|
539
|
+
app.reload_models
|
|
540
|
+
end
|
|
541
|
+
nil
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
# @deprecated use {#reload_actions} instead
|
|
545
|
+
def reload_planners
|
|
546
|
+
reload_actions
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
# Reload the actions defined under the actions/ subfolder
|
|
550
|
+
def reload_actions
|
|
551
|
+
execution_engine.execute do
|
|
552
|
+
app.reload_actions
|
|
553
|
+
end
|
|
554
|
+
actions
|
|
555
|
+
end
|
|
556
|
+
command :reload_actions, 'reloads the files in models/actions/'
|
|
557
|
+
|
|
558
|
+
# Notification about plan exceptions
|
|
559
|
+
#
|
|
560
|
+
# @yieldparam [Symbol] kind one of {ExecutionEngine::EXCEPTION_NONFATAL},
|
|
561
|
+
# {ExecutionEngine::EXCEPTION_FATAL} or {ExecutionEngine::EXCEPTION_HANDLED}
|
|
562
|
+
# @yieldparam [Roby::ExecutionException] error the exception
|
|
563
|
+
# @yieldparam [Array<Roby::Task>] tasks the tasks that are involved in this exception
|
|
564
|
+
# @yieldparam [Set<Integer>] job_ids the job ID of the involved jobs
|
|
565
|
+
#
|
|
566
|
+
# @see ExecutionEngine#on_exception
|
|
567
|
+
def on_exception(&block)
|
|
568
|
+
execution_engine.execute do
|
|
569
|
+
execution_engine.on_exception(on_error: :raise) do |kind, exception, tasks|
|
|
570
|
+
involved_job_ids = tasks.flat_map do |t|
|
|
571
|
+
job_ids_of_task(t) if t.plan
|
|
572
|
+
end.compact.to_set
|
|
573
|
+
block.call(kind, exception, tasks, involved_job_ids)
|
|
574
|
+
end
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
# @see ExecutionEngine#remove_exception_listener
|
|
579
|
+
def remove_exception_listener(listener)
|
|
580
|
+
execution_engine.execute do
|
|
581
|
+
execution_engine.remove_exception_listener(listener)
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
# Add a handler called at each end of cycle
|
|
586
|
+
#
|
|
587
|
+
# Interface-related objects that need to be notified must use this
|
|
588
|
+
# method instead of using {ExecutionEngine#at_cycle_end} on
|
|
589
|
+
# {#execution_engine}, because the listener is guaranteed to be ordered
|
|
590
|
+
# properly w.r.t. {#push_pending_job_notifications}
|
|
591
|
+
#
|
|
592
|
+
# @param [#call] block the listener
|
|
593
|
+
# @yieldparam [ExecutionEngine] the underlying execution execution_engine
|
|
594
|
+
# @return [Object] and ID that can be passed to {#remove_cycle_end}
|
|
595
|
+
def on_cycle_end(&block)
|
|
596
|
+
execution_engine.execute do
|
|
597
|
+
cycle_end_listeners << block
|
|
598
|
+
block
|
|
599
|
+
end
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
# @api private
|
|
603
|
+
#
|
|
604
|
+
# Notify the end-of-cycle to the listeners registered with
|
|
605
|
+
# {#on_cycle_end}
|
|
606
|
+
def notify_cycle_end
|
|
607
|
+
cycle_end_listeners.each do |listener|
|
|
608
|
+
listener.call
|
|
609
|
+
end
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
# Remove a handler that has been added to {#on_cycle_end}
|
|
613
|
+
def remove_cycle_end(listener)
|
|
614
|
+
cycle_end_listeners.delete(listener)
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
# Requests for the Roby application to quit
|
|
618
|
+
def quit
|
|
619
|
+
execution_engine.quit
|
|
620
|
+
end
|
|
621
|
+
command :quit, 'requests that the Roby application quits'
|
|
622
|
+
|
|
623
|
+
# Requests for the Roby application to quit
|
|
624
|
+
def restart
|
|
625
|
+
app.restart
|
|
626
|
+
end
|
|
627
|
+
command :restart, "restart this app's process"
|
|
628
|
+
|
|
629
|
+
# This is implemented on ShellClient directly
|
|
630
|
+
command 'describe', 'gives details about the given action',
|
|
631
|
+
action: 'the action itself'
|
|
632
|
+
|
|
633
|
+
# This is implemented on Server directly
|
|
634
|
+
command 'enable_notifications', 'enables the forwarding of notifications'
|
|
635
|
+
command 'disable_notifications', 'disables the forwarding of notifications'
|
|
636
|
+
|
|
637
|
+
# Enable or disable backtrace filtering
|
|
638
|
+
def enable_backtrace_filtering(enable: true)
|
|
639
|
+
app.filter_backtraces = enable
|
|
640
|
+
end
|
|
641
|
+
command :enable_backtrace_filtering, 'enable or disable backtrace filtering',
|
|
642
|
+
enable: 'true to enable, false to disable',
|
|
643
|
+
advanced: true
|
|
644
|
+
|
|
645
|
+
# Returns the app's log directory
|
|
646
|
+
def log_dir
|
|
647
|
+
app.log_dir
|
|
648
|
+
end
|
|
649
|
+
command :log_dir, "the app's log directory",
|
|
650
|
+
advanced: true
|
|
651
|
+
end
|
|
652
|
+
end
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
|