Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/syskit/component.rb
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
33 changes: 27 additions & 6 deletions lib/syskit/task_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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?
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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|
Expand Down Expand Up @@ -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
Expand Down
46 changes: 31 additions & 15 deletions test/network_generation/test_engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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", {}],
Comment thread
wvmcastro marked this conversation as resolved.
[: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
Expand Down
31 changes: 31 additions & 0 deletions test/test_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, what happens if syskit_configure(dev_driver) is called? It will be a no-op or some kind of exception will be thrown?

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)
Expand Down