diff --git a/lib/syskit/component.rb b/lib/syskit/component.rb index 249870614..5ee082d0e 100644 --- a/lib/syskit/component.rb +++ b/lib/syskit/component.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true module Syskit - Roby::EventStructure.relation "SyskitConfigurationPrecedence", strong: true + Roby::EventStructure.relation( + "SyskitConfigurationPrecedence", strong: true, copy_on_replace: true + ) # Base class for models that represent components (TaskContext, # Composition) diff --git a/lib/syskit/task_context.rb b/lib/syskit/task_context.rb index 3fa2bbbb5..dd0802b07 100644 --- a/lib/syskit/task_context.rb +++ b/lib/syskit/task_context.rb @@ -1033,11 +1033,15 @@ def prepare_for_setup(promise) ) do |needs_reconfiguration, state| if state == :EXCEPTION info "reconfiguring #{self}: the task was in exception state" - orocos_task.reset_exception(false) + log_remote_call(:reset_exception, orocos_name) do + orocos_task.reset_exception(false) + end orocos_task.port_names elsif needs_reconfiguration && (state != :PRE_OPERATIONAL) info "cleaning up #{self}" - orocos_task.cleanup(false) + log_remote_call(:cleanup, orocos_name) do + orocos_task.cleanup(false) + end orocos_task.port_names end end @@ -1048,6 +1052,13 @@ def prepare_for_setup(promise) end end + # @api private + # + # Helper to have log events for each remote call done by the task context + def log_remote_call(*params, &block) + execution_engine.log_call(:syskit_remote_call, *params, &block) + end + # (see Component#perform_setup) def perform_setup(promise) return if read_only? @@ -1077,13 +1088,18 @@ def perform_setup(promise) if properties_updated_in_configure && state != :PRE_OPERATIONAL info "properties have been changed within #configure, " \ "cleaning up #{self}" - orocos_task.cleanup(false) + log_remote_call(:cleanup, orocos_name) do + orocos_task.cleanup(false) + end state = :PRE_OPERATIONAL end if state == :PRE_OPERATIONAL info "setting up #{self}" - orocos_task.configure(false) + + log_remote_call(:configure, orocos_name) do + orocos_task.configure(false) + end else info "#{self} was already configured" end @@ -1147,7 +1163,10 @@ def setup_failed!(exception) "not have a port named #{sink_p}" end end - orocos_task.start(false) + + log_remote_call(:start, orocos_name) do + orocos_task.start(false) + end end start_event.achieve_asynchronously(promise, emit_on_success: false) promise.on_error do |exception| @@ -1236,7 +1255,9 @@ def handle_state_changes # :nodoc: # task, or a task that raises StateTransitionFailed but stops # anyways def stop_orocos_task - orocos_task.stop(false) + log_remote_call(:stop, orocos_name) do + orocos_task.stop(false) + end nil rescue Orocos::StateTransitionFailed # Could be that we already have stopped, for instance because there diff --git a/test/network_generation/test_engine.rb b/test/network_generation/test_engine.rb index 06cc9e3be..3af638360 100644 --- a/test/network_generation/test_engine.rb +++ b/test/network_generation/test_engine.rb @@ -348,24 +348,40 @@ def deploy_dev_and_bus bus_driver.orocos_task.local_ruby_task .create_output_port("dev", "/int") - flexmock(bus_driver.orocos_task, "bus") - .should_receive(:start).once.pass_thru - .globally.ordered(:bus_startup) - mock_raw_port(bus_driver, "dev") - .should_receive(:connect_to).once.pass_thru - .globally.ordered(:bus_startup) - flexmock(dev_driver.orocos_task, "dev") - .should_receive(:configure).once.pass_thru - .globally.ordered - - syskit_configure(bus_driver) - capture_log(bus_driver, :info) do - capture_log(dev_driver, :info) do - expect_execution.scheduler(true).to do - emit bus_driver.start_event, dev_driver.start_event + + events = record_events do + capture_log(bus_driver, :info) do + capture_log(dev_driver, :info) do + expect_execution.scheduler(true).to do + emit bus_driver.start_event, dev_driver.start_event + end end end end + events = events.filter_map do |ev| + if ev.name == :syskit_remote_call + ev.args + elsif ev.name == :syskit_connect + [ev.name, *ev.args] + end + end + + dev_name = dev_driver.orocos_name + bus_name = bus_driver.orocos_name + expected = [ + [:calling, :configure, bus_name], + [:success, :configure, bus_name], + [:syskit_connect, :success, + bus_name, "dev", dev_name, "bus_in", {}], + [:calling, :start, bus_name], + [:success, :start, bus_name], + [:calling, :configure, dev_name], + [:success, :configure, dev_name], + [:calling, :start, dev_name], + [:success, :start, dev_name] + ] + + assert_equal expected, events end it "supports busses-on-busses" do diff --git a/test/test_component.rb b/test/test_component.rb index 3c30ed329..705cb24b5 100644 --- a/test/test_component.rb +++ b/test/test_component.rb @@ -452,6 +452,37 @@ end end + describe Roby::EventStructure::SyskitConfigurationPrecedence do + it "is copied and not moved on replace" do + # This is a regression test, really. Syskit adds a configuration precedence + # constraint between busses and devices, but at some point the configuration + # had been made strong (to solve another bug). The issue was that because of + # this, the relation was not propagated through replacements, which is + # the main operation used by Syskit during network gen/deployment/adaptation + + combus_m = Syskit::ComBus.new_submodel message_type: "/int" + combus_driver_m = Syskit::TaskContext.new_submodel do + input_port "root_bus_in", "/double" + dynamic_output_port(/.*/, "/int") + end + combus_driver_m.provides combus_m, as: "driver" + + device_m = Syskit::Device.new_submodel + device_driver_m = Syskit::TaskContext.new_submodel { input_port "bus_in", "/int" } + device_driver_m.provides combus_m.client_in_srv, as: "bus" + device_driver_m.provides device_m, as: "driver" + + bus = robot.com_bus combus_m, as: "bus" + dev = robot.device device_m, as: "dev" + dev.attach_to(bus, client_to_bus: false) + syskit_stub_configured_deployment(device_driver_m) + syskit_stub_configured_deployment(combus_driver_m) + dev_driver = syskit_deploy(dev) + bus_driver = plan.find_tasks(combus_driver_m).with_parent(dev_driver).first + refute dev_driver.meets_configurationg_precedence_constraints? + end + end + describe "#should_configure_after" do it "adds a configuration precedence link between the given event and the start event of the receiver" do plan.add(component = Syskit::Component.new)