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
data/lib/roby/test/tools.rb
CHANGED
|
@@ -1,160 +1,164 @@
|
|
|
1
1
|
module Roby
|
|
2
2
|
module Test
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
3
|
+
class << self
|
|
4
|
+
def sampling(engine, duration, period, *fields)
|
|
5
|
+
Test.info "starting sampling #{fields.join(", ")} every #{period}s for #{duration}s"
|
|
6
|
+
|
|
7
|
+
samples = Array.new
|
|
8
|
+
fields.map! { |n| n.to_sym }
|
|
9
|
+
if fields.include?(:dt)
|
|
10
|
+
raise ArgumentError, "dt is reserved by #sampling"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
if compute_time = !fields.include?(:t)
|
|
14
|
+
fields << :t
|
|
15
|
+
end
|
|
16
|
+
fields << :dt
|
|
17
|
+
|
|
18
|
+
sample_type = Struct.new(*fields)
|
|
19
|
+
|
|
20
|
+
start = Time.now
|
|
21
|
+
Roby.condition_variable(true) do |cv, mt|
|
|
22
|
+
first_sample = nil
|
|
23
|
+
mt.synchronize do
|
|
24
|
+
timeout = false
|
|
25
|
+
id = engine.every(period) do
|
|
26
|
+
result = yield
|
|
27
|
+
if result
|
|
28
|
+
if compute_time
|
|
29
|
+
result << engine.cycle_start
|
|
30
|
+
end
|
|
31
|
+
new_sample = sample_type.new(*result)
|
|
32
|
+
|
|
33
|
+
unless samples.empty?
|
|
34
|
+
new_sample.dt = new_sample.t- samples.last.t
|
|
35
|
+
end
|
|
36
|
+
samples << new_sample
|
|
37
|
+
|
|
38
|
+
if samples.last.t - samples.first.t > duration
|
|
39
|
+
mt.synchronize do
|
|
40
|
+
timeout = true
|
|
41
|
+
cv.broadcast
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
while !timeout
|
|
48
|
+
cv.wait(mt)
|
|
49
|
+
end
|
|
50
|
+
engine.remove_periodic_handler(id)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
samples
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Stat = Struct.new :total, :count, :mean, :stddev, :min, :max
|
|
58
|
+
|
|
59
|
+
# Computes mean and standard deviation about the samples in
|
|
60
|
+
# +samples+ +spec+ describes what to compute:
|
|
61
|
+
# * if nothing is specified, we compute the statistics on
|
|
62
|
+
# v(i - 1) - v(i)
|
|
63
|
+
# * if spec['fieldname'] is 'rate', we compute the statistics on
|
|
64
|
+
# (v(i - 1) - v(i)) / (t(i - 1) / t(i))
|
|
65
|
+
# * if spec['fieldname'] is 'absolute', we compute the
|
|
66
|
+
# statistics on
|
|
67
|
+
# v(i)
|
|
68
|
+
# * if spec['fieldname'] is 'absolute_rate', we compute the
|
|
69
|
+
# statistics on
|
|
70
|
+
# v(i) / (t(i - 1) / t(i))
|
|
71
|
+
#
|
|
72
|
+
# The returned value is a struct with the same fields than the
|
|
73
|
+
# samples. Each element is a Stats object
|
|
74
|
+
def stats(samples, spec)
|
|
75
|
+
return if samples.empty?
|
|
76
|
+
type = samples.first.class
|
|
77
|
+
spec = spec.inject(Hash.new) do |h, (k, v)|
|
|
78
|
+
spec[k.to_sym] = v.to_sym
|
|
79
|
+
spec
|
|
80
|
+
end
|
|
81
|
+
spec[:t] = :exclude
|
|
82
|
+
spec[:dt] = :absolute
|
|
83
|
+
|
|
84
|
+
# Initialize the result value
|
|
85
|
+
fields = type.members.
|
|
86
|
+
find_all { |n| spec[n.to_sym] != :exclude }.
|
|
87
|
+
map { |n| n.to_sym }
|
|
88
|
+
result = Struct.new(*fields).new
|
|
89
|
+
fields.each do |name|
|
|
90
|
+
result[name] = Stat.new(0, 0, 0, 0, nil, nil)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Compute the deltas if the mode is not absolute
|
|
94
|
+
last_sample = nil
|
|
95
|
+
samples = samples.map do |original_sample|
|
|
96
|
+
sample = original_sample.dup
|
|
97
|
+
fields.each do |name|
|
|
98
|
+
next unless value = sample[name]
|
|
99
|
+
unless spec[name] == :absolute || spec[name] == :absolute_rate
|
|
100
|
+
if last_sample && last_sample[name]
|
|
101
|
+
sample[name] -= last_sample[name]
|
|
102
|
+
else
|
|
103
|
+
sample[name] = nil
|
|
104
|
+
next
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
last_sample = original_sample
|
|
109
|
+
sample
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Compute the rates if needed
|
|
113
|
+
samples = samples.map do |sample|
|
|
114
|
+
fields.each do |name|
|
|
115
|
+
next unless value = sample[name]
|
|
116
|
+
if spec[name] == :rate || spec[name] == :absolute_rate
|
|
117
|
+
if sample.dt
|
|
118
|
+
sample[name] = value / sample.dt
|
|
119
|
+
else
|
|
120
|
+
sample[name] = nil
|
|
121
|
+
next
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
sample
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
samples.each do |sample|
|
|
129
|
+
fields.each do |name|
|
|
130
|
+
next unless value = sample[name]
|
|
131
|
+
if !result[name].max || value > result[name].max
|
|
132
|
+
result[name].max = value
|
|
133
|
+
end
|
|
134
|
+
if !result[name].min || value < result[name].min
|
|
135
|
+
result[name].min = value
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
result[name].total += value
|
|
139
|
+
result[name].count += 1
|
|
140
|
+
end
|
|
141
|
+
last_sample = sample
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
result.each do |r|
|
|
145
|
+
r.mean = Float(r.total) / r.count
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
samples.each do |sample|
|
|
149
|
+
fields.each do |name|
|
|
150
|
+
next unless value = sample[name]
|
|
151
|
+
result[name].stddev += (value - result[name].mean) ** 2
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
result.each do |r|
|
|
156
|
+
r.stddev = Math.sqrt(r.stddev / r.count)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
result
|
|
160
|
+
end
|
|
161
|
+
end
|
|
158
162
|
end
|
|
159
163
|
end
|
|
160
164
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
module Test
|
|
3
|
+
# Implementation of the #validate_state_machine context
|
|
4
|
+
class ValidateStateMachine
|
|
5
|
+
def initialize(test, task_or_action)
|
|
6
|
+
@test = test
|
|
7
|
+
@toplevel_task = @test.roby_run_planner(task_or_action)
|
|
8
|
+
|
|
9
|
+
@state_machines = @toplevel_task.each_coordination_object.
|
|
10
|
+
find_all { |obj| obj.kind_of?(Coordination::ActionStateMachine) }
|
|
11
|
+
if @state_machines.empty?
|
|
12
|
+
raise ArgumentError, "#{task_or_action} has no state machines"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def assert_transitions_to_state(state_name, timeout: 5, start: true)
|
|
17
|
+
if state_name.respond_to?(:to_str) && !state_name.end_with?('_state')
|
|
18
|
+
state_name = "#{state_name}_state"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
done = false
|
|
22
|
+
@state_machines.each do |m|
|
|
23
|
+
m.on_transition do |_, new_state|
|
|
24
|
+
if state_name === new_state.name
|
|
25
|
+
done = true
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
yield if block_given?
|
|
30
|
+
@test.process_events_until(timeout: timeout, garbage_collect_pass: false) do
|
|
31
|
+
done
|
|
32
|
+
end
|
|
33
|
+
@test.roby_run_planner(@toplevel_task)
|
|
34
|
+
state_task = @toplevel_task.current_task_child
|
|
35
|
+
if start
|
|
36
|
+
expect_execution.to { emit state_task.start_event }
|
|
37
|
+
end
|
|
38
|
+
state_task
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def evaluate(&block)
|
|
42
|
+
instance_eval(&block)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def find_through_method_missing(m, args)
|
|
46
|
+
MetaRuby::DSLs.find_through_method_missing(
|
|
47
|
+
@toplevel_task, m, args,
|
|
48
|
+
'_event' => :find_event,
|
|
49
|
+
'_child' => :find_child_from_role) || super
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def has_through_method_missing?(m)
|
|
53
|
+
MetaRuby::DSLs.has_through_method_missing?(
|
|
54
|
+
@toplevel_task, m,
|
|
55
|
+
'_event' => :has_event?,
|
|
56
|
+
'_child' => :has_role?) || super
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
include MetaRuby::DSLs::FindThroughMethodMissing
|
|
60
|
+
|
|
61
|
+
def respond_to_missing?(m, include_private)
|
|
62
|
+
@test.respond_to?(m) || super
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def method_missing(m, *args, &block)
|
|
66
|
+
if @test.respond_to?(m)
|
|
67
|
+
@test.public_send(m, *args, &block)
|
|
68
|
+
else
|
|
69
|
+
super
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
@@ -0,0 +1,1125 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
# Exception raised when someone tries do commit an invalid transaction
|
|
3
|
+
class InvalidTransaction < RuntimeError; end
|
|
4
|
+
|
|
5
|
+
# A transaction is a special kind of plan. It allows to build plans in a separate
|
|
6
|
+
# sandbox, and then to apply the modifications to the real plan (using #commit_transaction), or
|
|
7
|
+
# to discard all modifications (using #discard)
|
|
8
|
+
class Transaction < Plan
|
|
9
|
+
# If true, an engine could execute tasks included in this plan. This is
|
|
10
|
+
# alwxays false for transactions
|
|
11
|
+
#
|
|
12
|
+
# @return [Boolean]
|
|
13
|
+
def executable?; false end
|
|
14
|
+
|
|
15
|
+
# If this is true, no new proxies can be created on the transaction.
|
|
16
|
+
# This is used during the commit process to verify that no new
|
|
17
|
+
# modifications are applied to the transaction
|
|
18
|
+
attr_predicate :frozen?
|
|
19
|
+
|
|
20
|
+
# True if this transaction has been committed
|
|
21
|
+
#
|
|
22
|
+
# @see #finalized?
|
|
23
|
+
attr_predicate :committed?
|
|
24
|
+
|
|
25
|
+
# True if this transaction has either been discarded or committed
|
|
26
|
+
#
|
|
27
|
+
# @return [Boolean]
|
|
28
|
+
# @see #committed?
|
|
29
|
+
def finalized?; !plan end
|
|
30
|
+
|
|
31
|
+
# (see Plan#root_plan?)
|
|
32
|
+
def root_plan?
|
|
33
|
+
false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def extend_proxy_object(proxy, object, klass = object.class)
|
|
37
|
+
proxy.extend Roby::Transaction::Proxying.proxying_module_for(klass)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def setup_and_register_proxy_task(proxy, task)
|
|
41
|
+
raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
|
|
42
|
+
|
|
43
|
+
proxy_tasks[task] = proxy
|
|
44
|
+
extend_proxy_object(proxy, task)
|
|
45
|
+
proxy.plan = self
|
|
46
|
+
proxy.setup_proxy(task, self)
|
|
47
|
+
register_task(proxy)
|
|
48
|
+
if plan.mission_task?(task)
|
|
49
|
+
add_mission_task(proxy)
|
|
50
|
+
elsif plan.permanent_task?(task)
|
|
51
|
+
add_permanent_task(proxy)
|
|
52
|
+
end
|
|
53
|
+
if services = plan.find_all_plan_services(task)
|
|
54
|
+
services.each do |original_srv|
|
|
55
|
+
create_and_register_proxy_plan_service(original_srv)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
proxy
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def setup_and_register_proxy_event(proxy, event)
|
|
62
|
+
raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
|
|
63
|
+
|
|
64
|
+
proxy_events[event] = proxy
|
|
65
|
+
extend_proxy_object(proxy, event)
|
|
66
|
+
proxy.plan = self
|
|
67
|
+
proxy.setup_proxy(event, self)
|
|
68
|
+
register_event(proxy)
|
|
69
|
+
if plan.permanent_event?(event)
|
|
70
|
+
add_permanent_event(proxy)
|
|
71
|
+
end
|
|
72
|
+
proxy
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def setup_and_register_proxy_plan_service(proxy, plan_service)
|
|
76
|
+
raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
|
|
77
|
+
|
|
78
|
+
extend_proxy_object(proxy, plan_service)
|
|
79
|
+
proxy.setup_proxy(plan_service, self)
|
|
80
|
+
proxy.task = wrap_task(plan_service.to_task)
|
|
81
|
+
add_plan_service(proxy)
|
|
82
|
+
proxy
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def create_and_register_proxy_task(object)
|
|
86
|
+
raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
|
|
87
|
+
proxy = object.dup
|
|
88
|
+
setup_and_register_proxy_task(proxy, object)
|
|
89
|
+
copy_object_relations(object, proxy, proxy_tasks)
|
|
90
|
+
proxy
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def create_and_register_proxy_event(object)
|
|
94
|
+
raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
|
|
95
|
+
proxy = object.dup
|
|
96
|
+
setup_and_register_proxy_event(proxy, object)
|
|
97
|
+
copy_object_relations(object, proxy, proxy_events)
|
|
98
|
+
proxy
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def create_and_register_proxy_plan_service(object)
|
|
102
|
+
raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
|
|
103
|
+
# Ensure the underlying task is wrapped
|
|
104
|
+
proxy = object.dup
|
|
105
|
+
setup_and_register_proxy_plan_service(proxy, object)
|
|
106
|
+
proxy
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def find_local_object_for_plan_object(object, proxy_map)
|
|
110
|
+
if object.plan == self
|
|
111
|
+
return object
|
|
112
|
+
elsif proxy = proxy_map[object]
|
|
113
|
+
return proxy
|
|
114
|
+
elsif !object.plan
|
|
115
|
+
raise ArgumentError, "#{object} has been removed from plan"
|
|
116
|
+
elsif object.plan.template?
|
|
117
|
+
add(object)
|
|
118
|
+
return object
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def find_local_object_for_task(object)
|
|
123
|
+
find_local_object_for_plan_object(object, proxy_tasks)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def find_local_object_for_event(object)
|
|
127
|
+
find_local_object_for_plan_object(object, proxy_events)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def find_local_object_for_plan_service(object)
|
|
131
|
+
if local_task = find_local_object_for_task(object.task)
|
|
132
|
+
find_plan_service(local_task)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def wrap_plan_object(object, proxy_map)
|
|
137
|
+
if object.plan != self.plan
|
|
138
|
+
raise ArgumentError, "#{object} is in #{object.plan}, this transaction #{self} applies on #{self.plan}"
|
|
139
|
+
else
|
|
140
|
+
return object.create_transaction_proxy(self)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def wrap_task(task, create: true)
|
|
145
|
+
if local_task = find_local_object_for_task(task)
|
|
146
|
+
local_task
|
|
147
|
+
elsif create
|
|
148
|
+
wrap_plan_object(task, proxy_tasks)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def wrap_event(event, create: true)
|
|
153
|
+
if local_event = find_local_object_for_event(event)
|
|
154
|
+
local_event
|
|
155
|
+
elsif create
|
|
156
|
+
wrap_plan_object(event, proxy_events)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def wrap_plan_service(plan_service, create: true)
|
|
161
|
+
if local_plan_service = find_local_object_for_plan_service(plan_service)
|
|
162
|
+
local_plan_service
|
|
163
|
+
elsif create
|
|
164
|
+
plan_service.create_transaction_proxy(self)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Get the transaction proxy for +object+
|
|
169
|
+
def wrap(object, create: true)
|
|
170
|
+
if object.kind_of?(PlanService)
|
|
171
|
+
wrap_plan_service(object, create: create)
|
|
172
|
+
elsif object.respond_to?(:to_task)
|
|
173
|
+
wrap_task(object, create: create)
|
|
174
|
+
elsif object.respond_to?(:to_event)
|
|
175
|
+
wrap_event(object, create: create)
|
|
176
|
+
elsif object.respond_to?(:to_ary)
|
|
177
|
+
object.map { |o| wrap(o, create: create) }
|
|
178
|
+
elsif object.respond_to?(:each)
|
|
179
|
+
raise ArgumentError, "don't know how to wrap containers of class #{object.class}"
|
|
180
|
+
else
|
|
181
|
+
raise TypeError, "don't know how to wrap #{object || 'nil'} of type #{object.class.ancestors}"
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def [](object, create: true)
|
|
186
|
+
wrap(object, create: create)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def propose; end
|
|
191
|
+
def edit
|
|
192
|
+
yield if block_given?
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# @api private
|
|
196
|
+
#
|
|
197
|
+
# Copies relations when importing a new subplan from the main plan
|
|
198
|
+
#
|
|
199
|
+
# @param [Hash<Relations::Graph,Relations::Graph>] relation graph
|
|
200
|
+
# mapping from the plan graphs to the transaction graphs
|
|
201
|
+
# @param [Hash<PlanObject,PlanObject>] mappings
|
|
202
|
+
# mapping from the plan objects to the transaction objects
|
|
203
|
+
def import_subplan_relations(graphs, mappings, proxy_map)
|
|
204
|
+
graphs.each do |plan_g, self_g|
|
|
205
|
+
plan_g.copy_subgraph_to(self_g, mappings)
|
|
206
|
+
mappings.each do |plan_v, self_v|
|
|
207
|
+
# The method assumes that the plan objects are new. We are
|
|
208
|
+
# therefore done if there is the same number of relations in
|
|
209
|
+
# both plan and transactions
|
|
210
|
+
#
|
|
211
|
+
# It is NOT true in the general case as one can add extra
|
|
212
|
+
# relations in the transaction
|
|
213
|
+
if plan_g.in_degree(plan_v) != self_g.in_degree(self_v)
|
|
214
|
+
plan_g.each_in_neighbour(plan_v) do |plan_parent|
|
|
215
|
+
next if mappings.has_key?(plan_parent)
|
|
216
|
+
if self_parent = proxy_map[plan_parent]
|
|
217
|
+
self_g.add_edge(self_parent, self_v, plan_g.edge_info(plan_parent, plan_v))
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
if plan_g.out_degree(plan_v) != self_g.out_degree(self_v)
|
|
223
|
+
plan_g.each_out_neighbour(plan_v) do |plan_child|
|
|
224
|
+
next if mappings.has_key?(plan_child)
|
|
225
|
+
if self_child = proxy_map[plan_child]
|
|
226
|
+
self_g.add_edge(self_v, self_child, plan_g.edge_info(plan_v, plan_child))
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# This method copies on +proxy+ all relations of +object+ for which
|
|
235
|
+
# both ends of the relation are already in the transaction.
|
|
236
|
+
def copy_object_relations(object, proxy, proxy_map)
|
|
237
|
+
# Create edges between the neighbours that are really in the transaction
|
|
238
|
+
object.each_relation do |rel|
|
|
239
|
+
plan_graph = object.relation_graph_for(rel)
|
|
240
|
+
trsc_graph = proxy.relation_graph_for(rel)
|
|
241
|
+
|
|
242
|
+
plan_graph.each_in_neighbour(object) do |parent|
|
|
243
|
+
if parent_proxy = proxy_map[parent]
|
|
244
|
+
trsc_graph.add_edge(parent_proxy, proxy, plan_graph.edge_info(parent, object))
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
plan_graph.each_out_neighbour(object) do |child|
|
|
248
|
+
if child_proxy = proxy_map[child]
|
|
249
|
+
trsc_graph.add_edge(proxy, child_proxy, plan_graph.edge_info(object, child))
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Tests whether a plan object has a proxy in self
|
|
256
|
+
#
|
|
257
|
+
# Unlike {#wrap}, the provided object must be a plan object from the
|
|
258
|
+
# transaction's underlying plan
|
|
259
|
+
#
|
|
260
|
+
# @param [Roby::Task] object the object to test for
|
|
261
|
+
def has_proxy_for_task?(object)
|
|
262
|
+
if object.plan != self.plan
|
|
263
|
+
raise ArgumentError, "#{object} is not in #{self}.plan (#{plan})"
|
|
264
|
+
end
|
|
265
|
+
proxy_tasks.has_key?(object)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Tests whether an event has a proxy in self
|
|
269
|
+
#
|
|
270
|
+
# Unlike {#wrap}, the provided object must be a plan object from the
|
|
271
|
+
# transaction's underlying plan
|
|
272
|
+
#
|
|
273
|
+
# @param [Roby::EventGenerator] object the object to test for
|
|
274
|
+
def has_proxy_for_event?(object)
|
|
275
|
+
if object.plan != self.plan
|
|
276
|
+
raise ArgumentError, "#{object} is not in #{self}.plan (#{plan})"
|
|
277
|
+
end
|
|
278
|
+
proxy_events.has_key?(object)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def restore_relation(proxy, relation)
|
|
282
|
+
object = proxy.__getobj__
|
|
283
|
+
|
|
284
|
+
proxy_children = proxy.child_objects(relation)
|
|
285
|
+
object.child_objects(relation).each do |object_child|
|
|
286
|
+
next unless proxy_child = wrap(object_child, create: false)
|
|
287
|
+
if proxy_children.include?(proxy_child)
|
|
288
|
+
relation.remove_edge(proxy, proxy_child)
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
proxy_parents = proxy.parent_objects(relation)
|
|
293
|
+
object.parent_objects(relation).each do |object_parent|
|
|
294
|
+
next unless proxy_parent = wrap(object_parent, create: false)
|
|
295
|
+
if proxy_parents.include?(proxy_parent)
|
|
296
|
+
relation.remove_edge(parent, proxy_parent)
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
added_objects.delete(proxy)
|
|
301
|
+
proxy.discovered_relations.delete(relation)
|
|
302
|
+
proxy.do_discover(relation, false)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Removes an object from this transaction
|
|
306
|
+
#
|
|
307
|
+
# This does *not* remove the object from the underlying plan. Removing
|
|
308
|
+
# objects directly is (at best) dangerous, and should be handled by
|
|
309
|
+
# garbage collection.
|
|
310
|
+
def remove_plan_object(object, proxy_map)
|
|
311
|
+
raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
|
|
312
|
+
|
|
313
|
+
object = may_unwrap(object)
|
|
314
|
+
proxy = proxy_map.delete(object)
|
|
315
|
+
actual_plan = (proxy || object).plan
|
|
316
|
+
|
|
317
|
+
if actual_plan != self
|
|
318
|
+
raise InternalError, "inconsistency: #{proxy || object} plan is #{actual_plan}, was expected to be #{self}"
|
|
319
|
+
end
|
|
320
|
+
return object, proxy
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def remove_task(task, timestamp = Time.now)
|
|
324
|
+
unwrapped, proxy = remove_plan_object(task, proxy_tasks)
|
|
325
|
+
if proxy
|
|
326
|
+
unmarked_mission_tasks.delete(unwrapped)
|
|
327
|
+
unmarked_permanent_tasks.delete(unwrapped)
|
|
328
|
+
proxy.each_plan_child do |task_event_proxy|
|
|
329
|
+
remove_plan_object(task_event_proxy, proxy_events)
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
super(proxy || task, timestamp)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def remove_free_event(event, timestamp = Time.now)
|
|
336
|
+
unwrapped, proxy = remove_plan_object(event, proxy_events)
|
|
337
|
+
if proxy
|
|
338
|
+
unmarked_permanent_events.delete(unwrapped)
|
|
339
|
+
end
|
|
340
|
+
super(proxy || event, timestamp)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def may_wrap(objects, create: true)
|
|
344
|
+
if objects.respond_to?(:to_ary)
|
|
345
|
+
objects.map { |obj| may_wrap(obj, create: create) }
|
|
346
|
+
elsif objects.respond_to?(:each)
|
|
347
|
+
raise ArgumentError, "don't know how to wrap containers of class #{objects.class}"
|
|
348
|
+
elsif objects.kind_of?(PlanObject)
|
|
349
|
+
wrap(objects, create: create)
|
|
350
|
+
else
|
|
351
|
+
objects
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# If +object+ is in this transaction, may_unwrap will return the
|
|
356
|
+
# underlying plan object. In all other cases, returns object.
|
|
357
|
+
def may_unwrap(object)
|
|
358
|
+
if object.respond_to?(:plan)
|
|
359
|
+
if object.plan == self && object.respond_to?(:__getobj__)
|
|
360
|
+
object.__getobj__
|
|
361
|
+
elsif object.plan == self.plan
|
|
362
|
+
object
|
|
363
|
+
else
|
|
364
|
+
object
|
|
365
|
+
end
|
|
366
|
+
else object
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# The list of missions of the underlying plan that have been unmarked in
|
|
371
|
+
# the transaction
|
|
372
|
+
attr_reader :unmarked_mission_tasks
|
|
373
|
+
# The list of permanent tasks of the underlying plan that have been unmarked in
|
|
374
|
+
# the transaction
|
|
375
|
+
attr_reader :unmarked_permanent_tasks
|
|
376
|
+
# The list of permanent events of the underlying plan that have been unmarked in
|
|
377
|
+
# the transaction
|
|
378
|
+
attr_reader :unmarked_permanent_events
|
|
379
|
+
# The plan this transaction applies on
|
|
380
|
+
attr_reader :plan
|
|
381
|
+
# The proxy objects built for tasks in this transaction
|
|
382
|
+
attr_reader :proxy_tasks
|
|
383
|
+
# The proxy objects built for events this transaction
|
|
384
|
+
attr_reader :proxy_events
|
|
385
|
+
# The option hash given at initialization
|
|
386
|
+
attr_reader :options
|
|
387
|
+
|
|
388
|
+
# The decision control object associated with this transaction. It is
|
|
389
|
+
# in general plan.control
|
|
390
|
+
def control; plan.control end
|
|
391
|
+
|
|
392
|
+
# Creates a new transaction which applies on +plan+
|
|
393
|
+
def initialize(plan, options = {})
|
|
394
|
+
if !plan
|
|
395
|
+
raise ArgumentError, "cannot create a transaction with no plan"
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
@options = options
|
|
399
|
+
@frozen = false
|
|
400
|
+
@disable_proxying = false
|
|
401
|
+
@invalid = false
|
|
402
|
+
|
|
403
|
+
super()
|
|
404
|
+
|
|
405
|
+
@plan = plan
|
|
406
|
+
|
|
407
|
+
@proxy_tasks = Hash.new
|
|
408
|
+
@proxy_events = Hash.new
|
|
409
|
+
@unmarked_mission_tasks = Set.new
|
|
410
|
+
@unmarked_permanent_tasks = Set.new
|
|
411
|
+
@unmarked_permanent_events = Set.new
|
|
412
|
+
|
|
413
|
+
plan.transactions << self
|
|
414
|
+
plan.added_transaction(self)
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
# Calls the given block in the execution thread of the engine of the
|
|
418
|
+
# underlying plan. If there is no engine attached to this plan, yields
|
|
419
|
+
# immediately.
|
|
420
|
+
#
|
|
421
|
+
# See Plan#execute and ExecutionEngine#execute
|
|
422
|
+
def execute(&block)
|
|
423
|
+
plan.execute(&block)
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def add_mission_task(t)
|
|
427
|
+
raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
|
|
428
|
+
if t.transaction_proxy?
|
|
429
|
+
unmarked_mission_tasks.delete(t.__getobj__)
|
|
430
|
+
end
|
|
431
|
+
super(t)
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def add_permanent_task(t)
|
|
435
|
+
raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
|
|
436
|
+
if t.transaction_proxy?
|
|
437
|
+
unmarked_permanent_tasks.delete(t.__getobj__)
|
|
438
|
+
end
|
|
439
|
+
super(t)
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
def add_permanent_event(e)
|
|
443
|
+
raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
|
|
444
|
+
if e.transaction_proxy?
|
|
445
|
+
unmarked_permanent_events.delete(e.__getobj__)
|
|
446
|
+
end
|
|
447
|
+
super(e)
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def add(objects)
|
|
451
|
+
raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
|
|
452
|
+
super(objects)
|
|
453
|
+
self
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def unmark_permanent_event(t)
|
|
457
|
+
raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
|
|
458
|
+
t = t.as_plan
|
|
459
|
+
if proxy = find_local_object_for_event(t)
|
|
460
|
+
super(proxy)
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
t = may_unwrap(t)
|
|
464
|
+
if t.plan == self.plan
|
|
465
|
+
unmarked_permanent_events.add(t)
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
def unmark_permanent_task(t)
|
|
470
|
+
raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
|
|
471
|
+
t = t.as_plan
|
|
472
|
+
if proxy = find_local_object_for_task(t)
|
|
473
|
+
super(proxy)
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
t = may_unwrap(t)
|
|
477
|
+
if t.plan == self.plan
|
|
478
|
+
unmarked_permanent_tasks.add(t)
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
def unmark_mission_task(t)
|
|
483
|
+
raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
|
|
484
|
+
t = t.as_plan
|
|
485
|
+
if proxy = find_local_object_for_task(t)
|
|
486
|
+
super(proxy)
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
t = may_unwrap(t)
|
|
490
|
+
if t.plan == self.plan
|
|
491
|
+
unmarked_mission_tasks.add(t)
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# The set of invalidation reasons registered with {#invalidate}. It is
|
|
496
|
+
# cleared if the transaction is marked as valid again by calling
|
|
497
|
+
# {#invalid=}.
|
|
498
|
+
#
|
|
499
|
+
# @return [Array<String>]
|
|
500
|
+
attribute(:invalidation_reasons) { Array.new }
|
|
501
|
+
|
|
502
|
+
# Marks this transaction as either invalid or valid. If it is marked as
|
|
503
|
+
# valid, it clears {#invalidation_reasons}.
|
|
504
|
+
def invalid=(flag)
|
|
505
|
+
if !flag
|
|
506
|
+
invalidation_reasons.clear
|
|
507
|
+
end
|
|
508
|
+
@invalid = flag
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
# True if {#invalidate} has been called, and {#invalid=} has not been
|
|
512
|
+
# called to clear the invalidation afterwards.
|
|
513
|
+
def invalid?; @invalid end
|
|
514
|
+
|
|
515
|
+
# Tests if it is safe to commit this transaction
|
|
516
|
+
#
|
|
517
|
+
# @return [Boolean] it returns false if there are other transactions on
|
|
518
|
+
# top of it. They must be committed or discarded before this transaction
|
|
519
|
+
# can be committed or discarded. It also returns safe if if this
|
|
520
|
+
# transaction has been marked as invalid with {#invalidate}
|
|
521
|
+
def valid_transaction?; transactions.empty? && !invalid? end
|
|
522
|
+
|
|
523
|
+
# Marks this transaction as valid
|
|
524
|
+
def invalidate(reason = nil)
|
|
525
|
+
self.invalid = true
|
|
526
|
+
invalidation_reasons << [reason, caller(1)] if reason
|
|
527
|
+
Roby.debug do
|
|
528
|
+
"invalidating #{self}: #{reason}"
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
# Tests if it is safe to commit this transaction
|
|
533
|
+
#
|
|
534
|
+
# @return [void]
|
|
535
|
+
# @raise [InvalidTransaction] in all cases where {#valid_transaction?}
|
|
536
|
+
# returns false
|
|
537
|
+
def check_valid_transaction
|
|
538
|
+
return if valid_transaction?
|
|
539
|
+
|
|
540
|
+
unless transactions.empty?
|
|
541
|
+
raise InvalidTransaction, "there is still transactions on top of this one"
|
|
542
|
+
end
|
|
543
|
+
message = invalidation_reasons.map do |reason, trace|
|
|
544
|
+
"#{trace[0]}: #{reason}\n #{trace[1..-1].join("\n ")}"
|
|
545
|
+
end.join("\n")
|
|
546
|
+
raise InvalidTransaction, "invalid transaction: #{message}"
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
# @api private
|
|
550
|
+
#
|
|
551
|
+
# Apply the graph modifications returned by
|
|
552
|
+
# {#compute_graph_modifications_for}
|
|
553
|
+
def apply_graph_modifications(work_graphs, added, removed, updated)
|
|
554
|
+
added.each do |graph, parent, child, info|
|
|
555
|
+
work_graphs[graph].add_edge(parent, child, info)
|
|
556
|
+
end
|
|
557
|
+
removed.each do |graph, parent, child|
|
|
558
|
+
work_graphs[graph].remove_edge(parent, child)
|
|
559
|
+
end
|
|
560
|
+
updated.each do |graph, parent, child, info|
|
|
561
|
+
work_graphs[graph].set_edge_info(parent, child, info)
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
# @api private
|
|
566
|
+
#
|
|
567
|
+
# Compute the graph modifications that are involving the given proxy
|
|
568
|
+
#
|
|
569
|
+
# It only computes the parent modifications involving objects that are
|
|
570
|
+
# not proxies themselves. It computes the child modifications for every
|
|
571
|
+
# child
|
|
572
|
+
def compute_graph_modifications_for(proxy, new_relations, removed_relations, updated_relations)
|
|
573
|
+
real_object = proxy.__getobj__
|
|
574
|
+
proxy.partition_new_old_relations(:each_parent_object, include_proxies: false) do |trsc_objects, rel, new, del, existing|
|
|
575
|
+
trsc_graph = proxy.relation_graph_for(rel)
|
|
576
|
+
plan_graph = proxy.__getobj__.relation_graph_for(rel)
|
|
577
|
+
|
|
578
|
+
new.each do |task|
|
|
579
|
+
edge_info = trsc_graph.edge_info(trsc_objects[task], proxy)
|
|
580
|
+
new_relations << [plan_graph, task, real_object, edge_info]
|
|
581
|
+
end
|
|
582
|
+
del.each do |task|
|
|
583
|
+
removed_relations << [plan_graph, task, real_object]
|
|
584
|
+
end
|
|
585
|
+
existing.each do |task|
|
|
586
|
+
edge_info = trsc_graph.edge_info(trsc_objects[task], proxy)
|
|
587
|
+
if plan_graph.edge_info(task, real_object) != edge_info
|
|
588
|
+
updated_relations << [plan_graph, task, real_object, edge_info]
|
|
589
|
+
end
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
proxy.partition_new_old_relations(:each_child_object) do |trsc_objects, rel, new, del, existing|
|
|
594
|
+
trsc_graph = proxy.relation_graph_for(rel)
|
|
595
|
+
plan_graph = proxy.__getobj__.relation_graph_for(rel)
|
|
596
|
+
|
|
597
|
+
new.each do |task|
|
|
598
|
+
edge_info = trsc_graph.edge_info(proxy, trsc_objects[task])
|
|
599
|
+
new_relations << [plan_graph, real_object, task, edge_info]
|
|
600
|
+
end
|
|
601
|
+
del.each do |task|
|
|
602
|
+
removed_relations << [plan_graph, real_object, task]
|
|
603
|
+
end
|
|
604
|
+
existing.each do |task|
|
|
605
|
+
edge_info = trsc_graph.edge_info(proxy, trsc_objects[task])
|
|
606
|
+
if plan_graph.edge_info(real_object, task) != edge_info
|
|
607
|
+
updated_relations << [plan_graph, real_object, task, edge_info]
|
|
608
|
+
end
|
|
609
|
+
end
|
|
610
|
+
end
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
# @api private
|
|
614
|
+
#
|
|
615
|
+
# This compute the triggers that shoul be applied if we commit this
|
|
616
|
+
# transaction
|
|
617
|
+
def compute_triggers_for_committed_transaction
|
|
618
|
+
trigger_matches = Hash.new
|
|
619
|
+
plan.triggers.each do |tr|
|
|
620
|
+
tr.each(self) do |t|
|
|
621
|
+
trigger_matches[t] = tr
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
proxy_tasks.each do |obj, proxy|
|
|
625
|
+
if tr = trigger_matches.delete(proxy)
|
|
626
|
+
if !(tr === obj) # already triggered
|
|
627
|
+
trigger_matches[obj] = tr
|
|
628
|
+
end
|
|
629
|
+
end
|
|
630
|
+
end
|
|
631
|
+
trigger_matches
|
|
632
|
+
end
|
|
633
|
+
|
|
634
|
+
# @api private
|
|
635
|
+
#
|
|
636
|
+
# Apply the triggers as returned by
|
|
637
|
+
# {#compute_triggers_for_committed_transaction}
|
|
638
|
+
def apply_triggers_on_committed_transaction(triggered_matches)
|
|
639
|
+
triggered_matches.each do |task, trigger|
|
|
640
|
+
trigger.call(task)
|
|
641
|
+
end
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
# Apply the modifications represented by self to the underlying plan
|
|
645
|
+
# snippet in your redefinition if you do so.
|
|
646
|
+
def apply_modifications_to_plan
|
|
647
|
+
new_mission_tasks = Set.new
|
|
648
|
+
new_permanent_tasks = Set.new
|
|
649
|
+
new_permanent_events = Set.new
|
|
650
|
+
|
|
651
|
+
added_relations = Array.new
|
|
652
|
+
removed_relations = Array.new
|
|
653
|
+
updated_relations = Array.new
|
|
654
|
+
|
|
655
|
+
# We're doing a lot of modifications of this plan .. store some of
|
|
656
|
+
# the sets we need for later, one part to keep them unchanged, one
|
|
657
|
+
# part to make sure we don't do modify-while-iterate
|
|
658
|
+
proxy_tasks = self.proxy_tasks.dup
|
|
659
|
+
proxy_events = self.proxy_events.dup
|
|
660
|
+
plan_services = self.plan_services.dup
|
|
661
|
+
unmarked_mission_tasks = self.unmarked_mission_tasks.dup
|
|
662
|
+
unmarked_permanent_tasks = self.unmarked_permanent_tasks.dup
|
|
663
|
+
unmarked_permanent_events = self.unmarked_permanent_events.dup
|
|
664
|
+
# We're taking care of the proxies first, so that we can merge the
|
|
665
|
+
# transaction using Plan#merge!. However, this means that
|
|
666
|
+
# #may_unwrap does not work after the first few steps. We therefore
|
|
667
|
+
# have to store the object-to-proxy mapping
|
|
668
|
+
real_objects = Hash.new
|
|
669
|
+
|
|
670
|
+
# We make a copy of all relation graphs, and update them with the
|
|
671
|
+
# transaction data. The underlying plan graphs are not modified
|
|
672
|
+
#
|
|
673
|
+
# We do not #dup them because we don't want to dup the edge info.
|
|
674
|
+
# Instead, we instanciate anew and merge. The add_vertex calls are
|
|
675
|
+
# needed to make sure that the graph dups the in/out sets instead
|
|
676
|
+
# of just copying them
|
|
677
|
+
task_work_graphs, event_work_graphs =
|
|
678
|
+
plan.class.instanciate_relation_graphs
|
|
679
|
+
work_graphs, transaction_graphs = Hash.new, Hash.new
|
|
680
|
+
plan.each_task_relation_graph do |g|
|
|
681
|
+
work_g = work_graphs[g] = task_work_graphs[g.class]
|
|
682
|
+
g.each_vertex { |v| work_g.add_vertex(v) }
|
|
683
|
+
work_g.merge(g)
|
|
684
|
+
transaction_graphs[g] = task_relation_graph_for(g.class)
|
|
685
|
+
end
|
|
686
|
+
plan.each_event_relation_graph do |g|
|
|
687
|
+
work_g = work_graphs[g] = event_work_graphs[g.class]
|
|
688
|
+
g.each_vertex { |v| work_g.add_vertex(v) }
|
|
689
|
+
work_g.merge(g)
|
|
690
|
+
transaction_graphs[g] = event_relation_graph_for(g.class)
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
# First apply all changes related to the proxies to the underlying
|
|
694
|
+
# plan. This adds some new tasks to the plan graph, but does not add
|
|
695
|
+
# them to the plan itself
|
|
696
|
+
#
|
|
697
|
+
# Note that we need to do that in two passes. The first one keeps
|
|
698
|
+
# the transaction unchanged, the second one removes the proxies from
|
|
699
|
+
# the transaction. This is needed so that #commit_transaction sees
|
|
700
|
+
# the graph unchanged
|
|
701
|
+
proxy_objects = proxy_tasks.merge(proxy_events)
|
|
702
|
+
|
|
703
|
+
proxy_objects.each do |object, proxy|
|
|
704
|
+
real_objects[proxy] = object
|
|
705
|
+
compute_graph_modifications_for(
|
|
706
|
+
proxy, added_relations, removed_relations, updated_relations)
|
|
707
|
+
proxy.commit_transaction
|
|
708
|
+
end
|
|
709
|
+
proxy_tasks.dup.each do |object, proxy|
|
|
710
|
+
if proxy.self_owned?
|
|
711
|
+
if mission_task?(proxy)
|
|
712
|
+
new_mission_tasks << object
|
|
713
|
+
elsif permanent_task?(proxy)
|
|
714
|
+
new_permanent_tasks << object
|
|
715
|
+
end
|
|
716
|
+
end
|
|
717
|
+
remove_task(proxy)
|
|
718
|
+
end
|
|
719
|
+
proxy_events.dup.each do |object, proxy|
|
|
720
|
+
if proxy.root_object?
|
|
721
|
+
if permanent_event?(proxy)
|
|
722
|
+
new_permanent_events << object
|
|
723
|
+
end
|
|
724
|
+
remove_free_event(proxy)
|
|
725
|
+
end
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
work_graphs.each do |plan_g, work_g|
|
|
729
|
+
work_g.merge(transaction_graphs[plan_g])
|
|
730
|
+
end
|
|
731
|
+
apply_graph_modifications(work_graphs, added_relations, removed_relations, updated_relations)
|
|
732
|
+
|
|
733
|
+
begin
|
|
734
|
+
validate_graphs(work_graphs.values)
|
|
735
|
+
rescue Exception => e
|
|
736
|
+
raise e, "cannot apply #{self}: #{e.message}", e.backtrace
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
#### UNTIL THIS POINT we have not modified the underlying plan AT ALL
|
|
740
|
+
# We DID update the transaction, though
|
|
741
|
+
|
|
742
|
+
# Apply #commit_transaction on the remaining tasks
|
|
743
|
+
tasks.each(&:commit_transaction)
|
|
744
|
+
free_events.each(&:commit_transaction)
|
|
745
|
+
|
|
746
|
+
# What is left in the transaction is the network of new tasks. Just
|
|
747
|
+
# merge it
|
|
748
|
+
plan.merge_transaction!(self, work_graphs,
|
|
749
|
+
added_relations, removed_relations, updated_relations)
|
|
750
|
+
|
|
751
|
+
# Update the plan services on the underlying plan. The only
|
|
752
|
+
# thing we need to take care of is replacements and new
|
|
753
|
+
# services. Other modifications will be applied automatically
|
|
754
|
+
plan_services.each do |task, services|
|
|
755
|
+
services.each do |srv|
|
|
756
|
+
if srv.transaction_proxy?
|
|
757
|
+
# Modified service. Might be moved to a new task
|
|
758
|
+
original = srv.__getobj__
|
|
759
|
+
# Do NOT use may_unwrap here ... See comments at the top
|
|
760
|
+
# of the method
|
|
761
|
+
task = real_objects[task] || task
|
|
762
|
+
srv.commit_transaction
|
|
763
|
+
if original.task != task
|
|
764
|
+
plan.move_plan_service(original, task)
|
|
765
|
+
end
|
|
766
|
+
elsif task.transaction_proxy?
|
|
767
|
+
# New service on an already existing task
|
|
768
|
+
srv.task = task.__getobj__
|
|
769
|
+
plan.add_plan_service(srv)
|
|
770
|
+
else
|
|
771
|
+
# New service on a new task
|
|
772
|
+
plan.add_plan_service(srv)
|
|
773
|
+
end
|
|
774
|
+
end
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
new_mission_tasks.each { |t| plan.add_mission_task(t) }
|
|
778
|
+
new_permanent_tasks.each { |t| plan.add_permanent_task(t) }
|
|
779
|
+
new_permanent_events.each { |e| plan.add_permanent_event(e) }
|
|
780
|
+
|
|
781
|
+
active_fault_response_tables.each do |tbl|
|
|
782
|
+
plan.use_fault_response_table tbl.model, tbl.arguments
|
|
783
|
+
end
|
|
784
|
+
|
|
785
|
+
unmarked_permanent_events.each { |t| plan.unmark_permanent_event(t) }
|
|
786
|
+
unmarked_permanent_tasks.each { |t| plan.unmark_permanent_task(t) }
|
|
787
|
+
unmarked_mission_tasks.each { |t| plan.unmark_mission_task(t) }
|
|
788
|
+
|
|
789
|
+
proxy_objects.each do |object, proxy|
|
|
790
|
+
forwarder_module = Transaction::Proxying.forwarder_module_for(object.model)
|
|
791
|
+
proxy.extend forwarder_module
|
|
792
|
+
proxy.__getobj__ = object
|
|
793
|
+
proxy.__freeze__
|
|
794
|
+
end
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
# Commit all modifications that have been registered
|
|
798
|
+
# in this transaction
|
|
799
|
+
def commit_transaction
|
|
800
|
+
check_valid_transaction
|
|
801
|
+
trigger_matches = compute_triggers_for_committed_transaction
|
|
802
|
+
apply_modifications_to_plan
|
|
803
|
+
apply_triggers_on_committed_transaction(trigger_matches)
|
|
804
|
+
frozen!
|
|
805
|
+
|
|
806
|
+
@committed = true
|
|
807
|
+
committed_transaction
|
|
808
|
+
plan.remove_transaction(self)
|
|
809
|
+
@plan = nil
|
|
810
|
+
|
|
811
|
+
yield if block_given?
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
# Hook called just after this transaction has been committed
|
|
815
|
+
#
|
|
816
|
+
# @return [void]
|
|
817
|
+
def committed_transaction; end
|
|
818
|
+
|
|
819
|
+
def enable_proxying; @disable_proxying = false end
|
|
820
|
+
def disable_proxying
|
|
821
|
+
@disable_proxying = true
|
|
822
|
+
if block_given?
|
|
823
|
+
begin
|
|
824
|
+
yield
|
|
825
|
+
ensure
|
|
826
|
+
@disable_proxying = false
|
|
827
|
+
end
|
|
828
|
+
end
|
|
829
|
+
end
|
|
830
|
+
def proxying?; !@frozen && !@disable_proxying end
|
|
831
|
+
|
|
832
|
+
# Discards this transaction and all the transactions it is part of
|
|
833
|
+
#
|
|
834
|
+
# @return [void]
|
|
835
|
+
def discard_transaction!
|
|
836
|
+
transactions.each do |trsc|
|
|
837
|
+
trsc.discard_transaction!
|
|
838
|
+
end
|
|
839
|
+
discard_transaction
|
|
840
|
+
end
|
|
841
|
+
|
|
842
|
+
# Discard all the modifications that have been registered
|
|
843
|
+
# in this transaction
|
|
844
|
+
#
|
|
845
|
+
# @return [void]
|
|
846
|
+
def discard_transaction
|
|
847
|
+
if !transactions.empty?
|
|
848
|
+
raise InvalidTransaction, "there is still transactions on top of this one"
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
frozen!
|
|
852
|
+
|
|
853
|
+
discarded_transaction
|
|
854
|
+
plan.remove_transaction(self)
|
|
855
|
+
@plan = nil
|
|
856
|
+
end
|
|
857
|
+
|
|
858
|
+
# Hook called just after this transaction has been discarded
|
|
859
|
+
#
|
|
860
|
+
# @return [void]
|
|
861
|
+
def discarded_transaction; end
|
|
862
|
+
|
|
863
|
+
def frozen!
|
|
864
|
+
@frozen = true
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
# Clears this transaction
|
|
868
|
+
#
|
|
869
|
+
# A cleared transaction behaves as a new transaction on the same plan
|
|
870
|
+
# @return [void]
|
|
871
|
+
def clear
|
|
872
|
+
unmarked_mission_tasks.clear
|
|
873
|
+
unmarked_permanent_tasks.clear
|
|
874
|
+
unmarked_permanent_events.clear
|
|
875
|
+
proxy_tasks.each_value { |proxy| proxy.clear_relations }
|
|
876
|
+
proxy_tasks.clear
|
|
877
|
+
proxy_events.each_value { |proxy| proxy.clear_relations }
|
|
878
|
+
proxy_events.clear
|
|
879
|
+
super
|
|
880
|
+
end
|
|
881
|
+
|
|
882
|
+
# Hook called when a task included in self got finalized from {#plan}
|
|
883
|
+
#
|
|
884
|
+
# It invalidates the transaction and calls
|
|
885
|
+
# DecisionControl#finalized_plan_task(self, event) for further actions
|
|
886
|
+
#
|
|
887
|
+
# @param [Task] task the finalized task represented by its proxy in self
|
|
888
|
+
# @return [void]
|
|
889
|
+
def finalized_plan_task(task)
|
|
890
|
+
proxied_task = task.__getobj__
|
|
891
|
+
|
|
892
|
+
invalidate("task #{task} has been removed from the plan")
|
|
893
|
+
discard_modifications(proxied_task)
|
|
894
|
+
control.finalized_plan_task(self, task)
|
|
895
|
+
end
|
|
896
|
+
|
|
897
|
+
# Hook called when an event included in self got finalized from {#plan}
|
|
898
|
+
#
|
|
899
|
+
# It invalidates the transaction and calls
|
|
900
|
+
# DecisionControl#finalized_plan_event(self, event) for further actions
|
|
901
|
+
#
|
|
902
|
+
# @param [EventGenerator] event the finalized event represented by its proxy in self
|
|
903
|
+
# @return [void]
|
|
904
|
+
def finalized_plan_event(event)
|
|
905
|
+
proxied_event = event.__getobj__
|
|
906
|
+
|
|
907
|
+
invalidate("event #{event} has been removed from the plan")
|
|
908
|
+
discard_modifications(proxied_event)
|
|
909
|
+
control.finalized_plan_event(self, event)
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
# Hook called when a relation is added between plan objects that are
|
|
913
|
+
# present in the transaction
|
|
914
|
+
#
|
|
915
|
+
# If the new relation is not present in the transaction as well, it
|
|
916
|
+
# invalidates the transaction and calls
|
|
917
|
+
# DecisionControl#adding_plan_relation(self, parent, child, relations, info) for further action
|
|
918
|
+
#
|
|
919
|
+
# @param [PlanObject] parent the parent object represented by its proxy in self
|
|
920
|
+
# @param [PlanObject] child the child object represented by its proxy in self
|
|
921
|
+
# @param [Array<Relations::Graph>] relations the graphs in which a relation
|
|
922
|
+
# has been added
|
|
923
|
+
# @param [Object] info the added information for the new edges
|
|
924
|
+
# (relation specific)
|
|
925
|
+
# @return [void]
|
|
926
|
+
def adding_plan_relation(parent, child, relations, info)
|
|
927
|
+
missing_relations = relations.find_all do |rel|
|
|
928
|
+
!parent.child_object?(child, rel)
|
|
929
|
+
end
|
|
930
|
+
unless missing_relations.empty?
|
|
931
|
+
invalidate("plan added a relation #{parent} -> #{child} in #{relations} with info #{info}")
|
|
932
|
+
control.adding_plan_relation(self, parent, child, relations, info)
|
|
933
|
+
end
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
# Hook called when a relation is removed between plan objects that are
|
|
937
|
+
# present in the transaction
|
|
938
|
+
#
|
|
939
|
+
# If the removed relation is still present in the transaction as well, it
|
|
940
|
+
# invalidates the transaction and calls
|
|
941
|
+
# DecisionControl#removing_plan_relation(self, parent, child, relations, info) for further action
|
|
942
|
+
#
|
|
943
|
+
# @param [PlanObject] parent the parent object represented by its proxy in self
|
|
944
|
+
# @param [PlanObject] child the child object represented by its proxy in self
|
|
945
|
+
# @param [Array<Relations::Graph>] relations the graphs in which a relation
|
|
946
|
+
# has been added
|
|
947
|
+
# @return [void]
|
|
948
|
+
def removing_plan_relation(parent, child, relations)
|
|
949
|
+
present_relations = relations.find_all do |rel|
|
|
950
|
+
parent.child_object?(child, rel)
|
|
951
|
+
end
|
|
952
|
+
unless present_relations.empty?
|
|
953
|
+
invalidate("plan removed a relation #{parent} -> #{child} in #{relations}")
|
|
954
|
+
control.removing_plan_relation(self, parent, child, relations)
|
|
955
|
+
end
|
|
956
|
+
end
|
|
957
|
+
|
|
958
|
+
# Returns [plan_set, transaction_set], where the first is the set of
|
|
959
|
+
# plan tasks matching +matcher+ and the second the set of transaction
|
|
960
|
+
# tasks matching it. The two sets are disjoint.
|
|
961
|
+
#
|
|
962
|
+
# This will be stored by the Query object as the query result. Note
|
|
963
|
+
# that, at this point, the transaction has not been modified even though
|
|
964
|
+
# it applies on the global scope. New proxies will only be created when
|
|
965
|
+
# Query#each is called.
|
|
966
|
+
def query_result_set(matcher) # :nodoc:
|
|
967
|
+
plan_set = Set.new
|
|
968
|
+
if matcher.scope == :global
|
|
969
|
+
plan_result_set = plan.query_result_set(matcher)
|
|
970
|
+
plan.query_each(plan_result_set) do |task|
|
|
971
|
+
plan_set << task if !has_proxy_for_task?(task)
|
|
972
|
+
end
|
|
973
|
+
end
|
|
974
|
+
|
|
975
|
+
transaction_set = super
|
|
976
|
+
[plan_set, transaction_set]
|
|
977
|
+
end
|
|
978
|
+
|
|
979
|
+
# Yields tasks in the result set of +query+. Unlike Query#result_set,
|
|
980
|
+
# all the tasks are included in the transaction
|
|
981
|
+
#
|
|
982
|
+
# +result_set+ is the value returned by #query_result_set.
|
|
983
|
+
def query_each(result_set) # :nodoc:
|
|
984
|
+
plan_set, trsc_set = result_set
|
|
985
|
+
plan_set.each { |task| yield(wrap_task(task)) }
|
|
986
|
+
trsc_set.each { |task| yield(task) }
|
|
987
|
+
end
|
|
988
|
+
|
|
989
|
+
class ReachabilityVisitor < RGL::DFSVisitor
|
|
990
|
+
attr_reader :transaction
|
|
991
|
+
attr_reader :start_vertex
|
|
992
|
+
|
|
993
|
+
def initialize(graph, transaction)
|
|
994
|
+
super(graph)
|
|
995
|
+
@transaction = transaction
|
|
996
|
+
end
|
|
997
|
+
|
|
998
|
+
def handle_start_vertex(v)
|
|
999
|
+
@start_vertex = v
|
|
1000
|
+
end
|
|
1001
|
+
end
|
|
1002
|
+
|
|
1003
|
+
class ReachabilityPlanVisitor < ReachabilityVisitor
|
|
1004
|
+
attr_reader :transaction_seeds
|
|
1005
|
+
attr_reader :plan_set
|
|
1006
|
+
|
|
1007
|
+
def initialize(graph, transaction, transaction_seeds, plan_set)
|
|
1008
|
+
super(graph, transaction)
|
|
1009
|
+
@transaction_seeds = transaction_seeds
|
|
1010
|
+
@plan_set = plan_set
|
|
1011
|
+
end
|
|
1012
|
+
|
|
1013
|
+
def follow_edge?(u, v)
|
|
1014
|
+
if transaction.find_local_object_for_task(u) && transaction.find_local_object_for_task(v)
|
|
1015
|
+
false
|
|
1016
|
+
else true
|
|
1017
|
+
end
|
|
1018
|
+
end
|
|
1019
|
+
|
|
1020
|
+
def handle_examine_vertex(v)
|
|
1021
|
+
if (start_vertex != v) && plan_set.include?(v)
|
|
1022
|
+
throw :reachable, true
|
|
1023
|
+
elsif proxy = transaction.find_local_object_for_task(v)
|
|
1024
|
+
transaction_seeds << proxy
|
|
1025
|
+
end
|
|
1026
|
+
end
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1029
|
+
class ReachabilityTransactionVisitor < ReachabilityVisitor
|
|
1030
|
+
attr_reader :transaction_set
|
|
1031
|
+
attr_reader :plan_seeds
|
|
1032
|
+
|
|
1033
|
+
def initialize(graph, transaction, plan_seeds, transaction_set)
|
|
1034
|
+
super(graph, transaction)
|
|
1035
|
+
@plan_seeds = plan_seeds
|
|
1036
|
+
@transaction_set = transaction_set
|
|
1037
|
+
end
|
|
1038
|
+
|
|
1039
|
+
def handle_examine_vertex(v)
|
|
1040
|
+
if (start_vertex != v) && transaction_set.include?(v)
|
|
1041
|
+
throw :reachable, true
|
|
1042
|
+
elsif v.transaction_proxy?
|
|
1043
|
+
plan_seeds << v.__getobj__
|
|
1044
|
+
end
|
|
1045
|
+
end
|
|
1046
|
+
end
|
|
1047
|
+
|
|
1048
|
+
# @api private
|
|
1049
|
+
#
|
|
1050
|
+
# Tests whether a task in plan_set or proxy_set would be reachable from
|
|
1051
|
+
# 'task' if the transaction was applied
|
|
1052
|
+
def reachable_on_applied_transaction?(transaction_seeds, transaction_set, transaction_graph,
|
|
1053
|
+
plan_seeds, plan_set, plan_graph)
|
|
1054
|
+
transaction_visitor = ReachabilityTransactionVisitor.new(
|
|
1055
|
+
transaction_graph, self, plan_seeds, transaction_set)
|
|
1056
|
+
if task = transaction_seeds.first
|
|
1057
|
+
transaction_visitor.handle_start_vertex(task)
|
|
1058
|
+
end
|
|
1059
|
+
plan_visitor = ReachabilityPlanVisitor.new(
|
|
1060
|
+
plan_graph, self, transaction_seeds, plan_set)
|
|
1061
|
+
if task = plan_seeds.first
|
|
1062
|
+
plan_visitor.handle_start_vertex(task)
|
|
1063
|
+
end
|
|
1064
|
+
|
|
1065
|
+
catch(:reachable) do
|
|
1066
|
+
while !transaction_seeds.empty? || !plan_seeds.empty?
|
|
1067
|
+
transaction_seeds.each do |seed|
|
|
1068
|
+
seed = transaction_seeds.shift
|
|
1069
|
+
if !transaction_visitor.finished_vertex?(seed)
|
|
1070
|
+
transaction_graph.depth_first_visit(seed, transaction_visitor) {}
|
|
1071
|
+
end
|
|
1072
|
+
end
|
|
1073
|
+
transaction_seeds.clear
|
|
1074
|
+
|
|
1075
|
+
plan_seeds.each do |seed|
|
|
1076
|
+
seed = plan_seeds.shift
|
|
1077
|
+
if !plan_visitor.finished_vertex?(seed)
|
|
1078
|
+
plan_graph.depth_first_visit(seed, plan_visitor) {}
|
|
1079
|
+
end
|
|
1080
|
+
end
|
|
1081
|
+
plan_seeds.clear
|
|
1082
|
+
end
|
|
1083
|
+
return false
|
|
1084
|
+
end
|
|
1085
|
+
true
|
|
1086
|
+
end
|
|
1087
|
+
|
|
1088
|
+
# @api private
|
|
1089
|
+
#
|
|
1090
|
+
# Given the result set of +query+, returns the subset of tasks which
|
|
1091
|
+
# have no parent in +query+
|
|
1092
|
+
#
|
|
1093
|
+
# This is never called directly, but is used by the Query API
|
|
1094
|
+
def query_roots(result_set, relation) # :nodoc:
|
|
1095
|
+
plan_set , trsc_set = *result_set
|
|
1096
|
+
plan_children , trsc_children = Set.new , Set.new
|
|
1097
|
+
|
|
1098
|
+
trsc_graph = task_relation_graph_for(relation).reverse
|
|
1099
|
+
plan_graph = plan.task_relation_graph_for(relation).reverse
|
|
1100
|
+
|
|
1101
|
+
plan_result = plan_set.find_all do |task|
|
|
1102
|
+
!reachable_on_applied_transaction?(
|
|
1103
|
+
[], trsc_set, trsc_graph,
|
|
1104
|
+
[task], plan_set, plan_graph)
|
|
1105
|
+
end
|
|
1106
|
+
|
|
1107
|
+
trsc_result = trsc_set.find_all do |task|
|
|
1108
|
+
!reachable_on_applied_transaction?(
|
|
1109
|
+
[task], trsc_set, trsc_graph,
|
|
1110
|
+
[], plan_set, plan_graph)
|
|
1111
|
+
end
|
|
1112
|
+
|
|
1113
|
+
[plan_result.to_set, trsc_result.to_set]
|
|
1114
|
+
end
|
|
1115
|
+
|
|
1116
|
+
def discard_modifications(object)
|
|
1117
|
+
if object.respond_to?(:to_task)
|
|
1118
|
+
remove_task(object.to_task)
|
|
1119
|
+
else
|
|
1120
|
+
remove_event(object.to_task)
|
|
1121
|
+
end
|
|
1122
|
+
end
|
|
1123
|
+
end
|
|
1124
|
+
end
|
|
1125
|
+
|