Skip to content
Open
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
23 changes: 16 additions & 7 deletions lib/syskit/exceptions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -441,11 +441,15 @@ def can_merge?
!!@can_merge
end

def initialize(device, task0, task1, toplevel_tasks_to_requirements = {})
def initialize(
device, task0, task1, toplevel_tasks_to_requirements = {},
early_deploy:
)
@device = device
@tasks = [task0, task1]
@merge_result = NetworkGeneration::MergeSolver.resolve_merge(
tasks[0].plan, tasks[0], tasks[1], {}
tasks[0].plan, tasks[0], tasks[1], {},
merge_task_contexts_with_same_agent: early_deploy
)

@involved_definitions = @tasks.map do |t|
Expand All @@ -469,22 +473,27 @@ class ConflictingDeploymentAllocation < SpecError

attr_reader :orocos_name

def initialize(orocos_name, tasks, toplevel_tasks_to_requirements = {})
def initialize(
orocos_name, tasks, toplevel_tasks_to_requirements = {}, early_deploy:
)
@orocos_name = orocos_name
@tasks = tasks
@toplevel_tasks_to_requirements = toplevel_tasks_to_requirements
@agent = tasks.first.execution_agent

@configured_deployment = tasks.first.deployed_task.configured_deployment
@merge_result = NetworkGeneration::MergeSolver.resolve_merge(
tasks[0].plan, tasks[0], tasks[1], {}
tasks[0].plan, tasks[0], tasks[1], {},
merge_task_contexts_with_same_agent: early_deploy
)
end

def pretty_print(pp)
deployment_m = @agent.deployed_orogen_model_by_name(orocos_name)
deployment_m = @configured_deployment.model.orogen_model
process_server_name = @configured_deployment.process_server_name
pp.text(
"deployed task '#{orocos_name}' from deployment " \
"'#{deployment_m.name}' defined in " \
"'#{deployment_m.project.name}' on '#{@agent.process_server_name}' " \
"'#{deployment_m.project.name}' on '#{process_server_name}' " \
"is assigned to #{@tasks.size} tasks. Below is the list of " \
"the dependent non-deployed actions. Right after the list " \
"is a detailed explanation of why the first two tasks are not merged:"
Expand Down
5 changes: 4 additions & 1 deletion lib/syskit/network_generation/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ def compute_deployed_network(
required_instances: [],
default_deployment_group: Syskit.conf.deployment_group,
compute_policies: true,
early_deploy: Syskit.conf.early_deploy?,
lazy_deploy: Syskit.conf.lazy_deploy?,
validate_deployed_network: true
)
Expand All @@ -139,7 +140,8 @@ def compute_deployed_network(
work_plan, default_deployment_group, lazy: lazy_deploy
)
SystemNetworkGenerator.verify_all_deployments_are_unique(
work_plan, toplevel_tasks_to_requirements.dup
work_plan, toplevel_tasks_to_requirements.dup,
early_deploy: early_deploy
)
end

Expand Down Expand Up @@ -499,6 +501,7 @@ def resolve_system_network(
required_instances: required_instances,
default_deployment_group: default_deployment_group,
compute_policies: compute_policies,
early_deploy: early_deploy,
lazy_deploy: lazy_deploy,
validate_deployed_network: validate_deployed_network
)
Expand Down
26 changes: 20 additions & 6 deletions lib/syskit/network_generation/merge_solver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ class MergeSolver # rubocop:disable Metrics/ClassLength
def initialize(
plan,
event_logger: plan.event_logger,
resolution_control: Async::Control.new
resolution_control: Async::Control.new,
merge_task_contexts_with_same_agent: false
)
@plan = plan
@event_logger = event_logger
Expand All @@ -49,7 +50,7 @@ def initialize(
@task_replacement_graph = Roby::Relations::BidirectionalDirectedAdjacencyGraph.new
@resolved_replacements = {}
@invalid_merges = Set.new
@merge_task_contexts_with_same_agent = false
@merge_task_contexts_with_same_agent = merge_task_contexts_with_same_agent
@resolution_control = resolution_control
end

Expand Down Expand Up @@ -212,8 +213,14 @@ def self.trace_export(plan, phase: 1, highlights: [], **dataflow_options)

# Create a new solver on the given plan and perform
# {#merge_identical_tasks}
def self.merge_identical_tasks(plan)
solver = MergeSolver.new(plan)
def self.merge_identical_tasks(
plan, merge_task_contexts_with_same_agent: false
)
solver = MergeSolver.new(
plan,
merge_task_contexts_with_same_agent:
merge_task_contexts_with_same_agent
)
solver.merge_identical_tasks
end

Expand Down Expand Up @@ -516,8 +523,15 @@ def may_merge?(merged_task, task)
end
end

def self.resolve_merge(plan, merged_task, task, mappings)
solver = MergeSolver.new(plan)
def self.resolve_merge(
plan, merged_task, task, mappings,
merge_task_contexts_with_same_agent: false
)
solver = MergeSolver.new(
plan,
merge_task_contexts_with_same_agent:
merge_task_contexts_with_same_agent
)
solver.resolve_merge(merged_task, task, mappings)
end

Expand Down
42 changes: 25 additions & 17 deletions lib/syskit/network_generation/system_network_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ def lazy_deploy?

def initialize(plan, # rubocop:disable Metrics/ParameterLists
event_logger: plan.event_logger,
default_deployment_group: nil,
early_deploy: false,
lazy_deploy: false,
default_deployment_group: Syskit.conf.deployment_group,
early_deploy: Syskit.conf.early_deploy?,
lazy_deploy: Syskit.conf.lazy_deploy?,
error_handler: RaiseErrorHandler.new,
resolution_control: Async::Control.new,
merge_solver: nil)
Expand Down Expand Up @@ -236,7 +236,8 @@ def deploy(deployment_tasks)
network_deployer = SystemNetworkDeployer.new(
plan,
merge_solver: merge_solver,
default_deployment_group: default_deployment_group
default_deployment_group: default_deployment_group,
resolution_control: @resolution_control
)

network_deployer.deploy(
Expand Down Expand Up @@ -336,12 +337,14 @@ def early_deploy(deployment_tasks)
# Compute in #plan the network needed to fullfill the requirements
#
# This network is neither validated nor tied to actual deployments
def compute_system_network(instance_requirements,
def compute_system_network( # rubocop:disable Metrics/ParameterLists
instance_requirements,
error_handler: RaiseErrorHandler.new,
garbage_collect: true,
validate_abstract_network: true,
validate_generated_network: true,
validate_deployed_network: true)
error_handler = RaiseErrorHandler.new
validate_deployed_network: true
)
instanciate_system_network(instance_requirements)
resolve_system_network(
error_handler: error_handler,
Expand All @@ -362,12 +365,12 @@ def validate_network(error_handler: @error_handler,
end

if validate_generated_network
self.validate_generated_network(error_handler: error_handler)
validate_generated_network(error_handler: error_handler)
log_timepoint "syskit-netgen:validate_generated_network"
end
return unless early_deploy? && validate_deployed_network

self.validate_deployed_network(error_handler: error_handler)
validate_deployed_network(error_handler: error_handler)
log_timepoint "syskit-netgen:validate_deployed_network"
end

Expand Down Expand Up @@ -444,15 +447,16 @@ def self.verify_no_multiplexing_connections(plan)
# components due to a conflicting device allocation
def self.verify_conflicting_device_allocation(
components, toplevel_tasks_to_requirements = {},
error_handler: RaiseErrorHandler.new
early_deploy:, error_handler: RaiseErrorHandler.new
)
devices = {}
components.each do |task|
task.each_master_device do |dev|
device_name = dev.full_name
if (old_task = devices[device_name])
allocation_err = ConflictingDeviceAllocation.new(
dev, task, old_task, toplevel_tasks_to_requirements
dev, task, old_task, toplevel_tasks_to_requirements,
early_deploy: early_deploy
)
error_handler.register_resolution_failures_from_exception(
[task, old_task], allocation_err
Expand Down Expand Up @@ -480,7 +484,7 @@ def self.verify_conflicting_device_allocation(
# components due to bad device allocation
def self.verify_device_allocation(
plan, toplevel_tasks_to_requirements = {},
error_handler: RaiseErrorHandler.new
early_deploy:, error_handler: RaiseErrorHandler.new
)
components = plan.find_local_tasks(Syskit::Device).to_a

Expand All @@ -503,12 +507,13 @@ def self.verify_device_allocation(

verify_conflicting_device_allocation(
allocated_devices, toplevel_tasks_to_requirements,
error_handler: error_handler
error_handler: error_handler, early_deploy: early_deploy
)
end

def self.verify_all_deployments_are_unique(
plan, toplevel_tasks_to_requirements, error_handler: RaiseErrorHandler.new
plan, toplevel_tasks_to_requirements,
early_deploy:, error_handler: RaiseErrorHandler.new
)
deployment_to_task_map = plan.find_local_tasks(Syskit::TaskContext)
.group_by(&:orocos_name)
Expand All @@ -522,7 +527,8 @@ def self.verify_all_deployments_are_unique(

using_same_deployment.each do |orocos_name, tasks|
exception = ConflictingDeploymentAllocation.new(
orocos_name, tasks, toplevel_tasks_to_requirements
orocos_name, tasks, toplevel_tasks_to_requirements,
early_deploy: early_deploy
)
exception = exception.exception("deployment used multiple times")
error_handler.register_resolution_failures_from_exception(
Expand All @@ -545,7 +551,8 @@ def validate_generated_network(error_handler: @error_handler)
self.class.verify_task_allocation(plan, error_handler: error_handler)

self.class.verify_device_allocation(
plan, toplevel_tasks_to_requirements, error_handler: error_handler
plan, toplevel_tasks_to_requirements,
early_deploy: early_deploy?, error_handler: error_handler
)
super if defined? super
end
Expand All @@ -556,7 +563,8 @@ def validate_deployed_network(error_handler: @error_handler)
error_handler: error_handler, lazy: lazy_deploy?
)
self.class.verify_all_deployments_are_unique(
plan, toplevel_tasks_to_requirements.dup, error_handler: error_handler
plan, toplevel_tasks_to_requirements.dup,
error_handler: error_handler, early_deploy: early_deploy?
)
super if defined? super
end
Expand Down
67 changes: 48 additions & 19 deletions test/network_generation/test_system_network_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ module NetworkGeneration
@requirements = component_m.to_instance_requirements
end

subject { SystemNetworkGenerator.new(Roby::Plan.new) }
subject do
SystemNetworkGenerator.new(
Roby::Plan.new,
default_deployment_group: default_deployment_group
)
end

it "adds instanciated tasks as permanent tasks" do
flexmock(requirements).should_receive(:instanciate)
Expand Down Expand Up @@ -80,7 +85,13 @@ def arg=(value)
@task = cmp.test_child
end

subject { SystemNetworkGenerator.new(Roby::ExecutablePlan.new) }
subject do
SystemNetworkGenerator.new(
Roby::ExecutablePlan.new,
default_deployment_group: default_deployment_group
)
end

it "sets missing devices from its selections" do
task.requirements.push_dependency_injection(Syskit::DependencyInjection.new(dev_m => device))
subject.allocate_devices(task)
Expand All @@ -101,15 +112,20 @@ def arg=(value)
end

describe "#compute_system_network" do
subject do
SystemNetworkGenerator.new(
Roby::ExecutablePlan.new,
default_deployment_group: default_deployment_group
)
end

it "runs the validate_abstract_network handler if asked to" do
generator = SystemNetworkGenerator.new(plan)
flexmock(generator).should_receive(:validate_abstract_network).once
generator.compute_system_network([], validate_abstract_network: true)
flexmock(subject).should_receive(:validate_abstract_network).once
subject.compute_system_network([], validate_abstract_network: true)
end
it "runs the validate_generated_network handler if asked to" do
generator = SystemNetworkGenerator.new(plan)
flexmock(generator).should_receive(:validate_generated_network).once
generator.compute_system_network([], validate_generated_network: true)
flexmock(subject).should_receive(:validate_generated_network).once
subject.compute_system_network([], validate_generated_network: true)
end

describe "early deploy" do
Expand Down Expand Up @@ -234,7 +250,7 @@ def arg=(value)
local_net_gen = SystemNetworkGenerator.new(
Roby::Plan.new,
default_deployment_group: Models::DeploymentGroup.new,
early_deploy: true, lazy_deploy: true
early_deploy: true
)
toplevel_tasks = local_net_gen.compute_system_network(
[task_m.to_instance_requirements
Expand Down Expand Up @@ -452,17 +468,17 @@ def arg=(value)
Chain 1:
T<id:X> pending
arguments:
orocos_name: "task1",
read_only: false,
arg: 1,
conf: ["default"],
arg: 1
read_only: false,
orocos_name: "task1"
Chain 2:
T<id:X> pending
arguments:
orocos_name: "task1",
read_only: false,
arg: 2,
conf: ["default"],
arg: 2
read_only: false,
orocos_name: "task1"
MSG
errors.zip(requirement_tasks).each do |error, task|
assert_exception(error, task.planned_task, expected_message)
Expand Down Expand Up @@ -533,7 +549,12 @@ def make_generator
task_m.provides srv_m, as: "test"
end

subject { SystemNetworkGenerator.new(plan) }
subject do
SystemNetworkGenerator.new(
plan,
default_deployment_group: default_deployment_group
)
end

def compute_system_network(*requirements)
requirements = requirements.map(&:to_instance_requirements)
Expand All @@ -544,6 +565,7 @@ def compute_system_network(*requirements)
end

it "keeps the compositions' optional dependencies that are not abstract" do
syskit_stub_configured_deployment(task_m)
cmp = compute_system_network(cmp_m.use("test" => task_m))
assert cmp.has_role?("test")
end
Expand All @@ -554,13 +576,20 @@ def compute_system_network(*requirements)
end
it "removes the compositions' optional dependencies that are still abstract" do
cmp = compute_system_network(cmp_m)
assert !cmp.has_role?("test")
refute cmp.has_role?("test")
end
it "enables the use of the abstract flag in InstanceRequirements to use an optional dep only if it is instanciated by other means" do
cmp = compute_system_network(cmp_m.use("test" => task_m.to_instance_requirements.abstract))
syskit_stub_configured_deployment(task_m)
cmp = compute_system_network(
cmp_m.use("test" => task_m.to_instance_requirements.abstract)
)
refute cmp.has_role?("test")
execute { plan.remove_task(cmp) }
cmp = compute_system_network(cmp_m.use("test" => task_m.to_instance_requirements.abstract), task_m)

cmp = compute_system_network(
cmp_m.use("test" => task_m.to_instance_requirements.abstract),
task_m
)
assert cmp.has_role?("test")
end
end
Expand Down
Loading