class Syskit::NetworkGeneration::SystemNetworkDeployer

Algorithm that transforms a network generated by {SystemNetworkGenerator} into a deployed network

It does not deal with adapting an existing network

Attributes

event_logger[R]
merge_solver[R]
plan[R]
task_context_deployment_candidates[R]

Public Class Methods

new(plan, event_logger: plan.event_logger, merge_solver: MergeSolver.new(plan)) click to toggle source
# File lib/syskit/network_generation/system_network_deployer.rb, line 18
def initialize(plan, event_logger: plan.event_logger,
               merge_solver: MergeSolver.new(plan))

    @plan = plan
    @event_logger = event_logger
    @merge_solver = merge_solver
    @task_context_deployment_candidates = compute_task_context_deployment_candidates
end

Public Instance Methods

apply_selected_deployments(selected_deployments) click to toggle source
# File lib/syskit/network_generation/system_network_deployer.rb, line 123
def apply_selected_deployments(selected_deployments)
    deployment_tasks = Hash.new
    selected_deployments.each do |task, selected|
        machine, configured_deployment, task_name = *selected

        deployment_task =
            (deployment_tasks[[machine, configured_deployment]] ||= configured_deployment.new(on: machine))
        if Syskit.conf.permanent_deployments?
            plan.add_permanent_task(deployment_task)
        else
            plan.add(deployment_task)
        end
        deployed_task = deployment_task.task(task_name)
        debug { "deploying #{task} with #{task_name} of #{configured_deployment.short_name} (#{deployed_task})" }
        # We MUST merge one-by-one here. Calling apply_merge_group
        # on all the merges at once would NOT copy the connections
        # that exist between the tasks of the "from" group to the
        # "to" group, which is really not what we want
        #
        # Calling with all the mappings would be useful if what
        # we wanted is replace a subnet of the plan by another
        # subnet. This is not the goal here.
        merge_solver.apply_merge_group(task => deployed_task)
    end
end
available_deployments() click to toggle source

Returns the set of deployments that are available for this network generation

# File lib/syskit/network_generation/system_network_deployer.rb, line 29
def available_deployments
    Syskit.conf.deployments
end
deploy(validate: true) click to toggle source
# File lib/syskit/network_generation/system_network_deployer.rb, line 33
def deploy(validate: true)
    debug do
        debug "Deploying the system network"
        debug "Available deployments"
        log_nest(2) do
            task_context_deployment_candidates.each do |model, deployments|
                if !deployments
                    debug "#{model}: no deployments"
                elsif deployments.size == 1
                    debug "#{model}: #{deployments.first}"
                else
                    debug "#{model}"
                    log_nest(2) do
                        deployments.each do |deployment|
                            debug deployment.to_s
                        end
                    end
                end
            end
        end
        break
    end

    selected_deployments, missing_deployments = select_deployments
    log_timepoint 'select_deployments'

    apply_selected_deployments(selected_deployments)
    log_timepoint 'apply_selected_deployments'

    if validate
        validate_deployed_network
        log_timepoint 'validate_deployed_network'
    end

    return missing_deployments
end
find_suitable_deployment_for(task) click to toggle source
# File lib/syskit/network_generation/system_network_deployer.rb, line 94
def find_suitable_deployment_for(task)
    # task.model would be wrong here as task.model could be the
    # singleton class (if there are dynamic services)
    candidates = task_context_deployment_candidates[task.model]
    if !candidates || candidates.empty?
        candidates = task_context_deployment_candidates[task.concrete_model]
        if !candidates || candidates.empty?
            debug { "no deployments found for #{task} (#{task.concrete_model})" }
            return
        end
    end

    if candidates.size > 1
        debug { "#{candidates.size} deployments available for #{task} (#{task.concrete_model}), trying to resolve" }
        selected = log_nest(2) do
            resolve_deployment_ambiguity(candidates, task)
        end
        if selected
            debug { "  selected #{selected}" }
            return selected
        else
            debug { "  deployment of #{task} (#{task.concrete_model}) is ambiguous" }
            return
        end
    else
        return candidates.first
    end
end
resolve_deployment_ambiguity(candidates, task) click to toggle source

Try to resolve a set of deployment candidates for a given task

@param [Array<(String,Model<Deployment>,String)>] candidates set

of deployment candidates as
(process_server_name,deployment_model,task_name) tuples

@param [Syskit::TaskContext] task the task context for which

candidates are possible deployments

@return [(String,Model<Deployment>,String),nil] the resolved

deployment, if finding a single best candidate was possible, or
nil otherwise.
# File lib/syskit/network_generation/system_network_deployer.rb, line 210
def resolve_deployment_ambiguity(candidates, task)
    if task.orocos_name
        debug { "#{task} requests orocos_name to be #{task.orocos_name}" }
        resolved = candidates.find { |_, _, task_name| task_name == task.orocos_name }
        if !resolved
            debug { "cannot find requested orocos name #{task.orocos_name}" }
        end
        return resolved
    end
    hints = task.deployment_hints
    debug { "#{task}.deployment_hints: #{hints.map(&:to_s).join(", ")}" }
    # Look to disambiguate using deployment hints
    resolved = candidates.find_all do |_, deployment_model, task_name|
        task.deployment_hints.any? do |rx|
            rx == deployment_model || rx === task_name
        end
    end
    if resolved.size != 1
        info do
            info { "ambiguous deployment for #{task} (#{task.model})" }
            candidates.each do |machine, deployment_model, task_name|
                info { "  #{task_name} of #{deployment_model.short_name} on #{machine}" }
            end
            break
        end
        return
    end
    return resolved.first
end
select_deployments() click to toggle source
# File lib/syskit/network_generation/system_network_deployer.rb, line 70
def select_deployments
    used_deployments = Set.new
    missing_deployments = Set.new
    selected_deployments = Hash.new

    all_tasks = plan.find_local_tasks(TaskContext).to_a
    all_tasks.each do |task|
        next if task.execution_agent
        if !(selected = find_suitable_deployment_for(task))
            missing_deployments << task
        elsif used_deployments.include?(selected)
            debug do
                machine, configured_deployment, task_name = *selected
                "#{task} resolves to #{configured_deployment}.#{task_name} on #{machine} for its deployment, but it is already used"
            end
            missing_deployments << task
        else
            used_deployments << selected
            selected_deployments[task] = selected
        end
    end
    return selected_deployments, missing_deployments
end
validate_deployed_network() click to toggle source

Sanity checks to verify that the result of deploy_system_network is valid

@raise [MissingDeployments] if some tasks could not be deployed

# File lib/syskit/network_generation/system_network_deployer.rb, line 153
def validate_deployed_network
    verify_all_tasks_deployed
end
verify_all_tasks_deployed() click to toggle source
# File lib/syskit/network_generation/system_network_deployer.rb, line 157
def verify_all_tasks_deployed
    not_deployed = plan.find_local_tasks(TaskContext).
        not_finished.not_abstract.
        find_all { |t| !t.execution_agent }

    if !not_deployed.empty?
        tasks_with_candidates = Hash.new
        not_deployed.each do |task|
            candidates = task_context_deployment_candidates[task.concrete_model] || []
            candidates = candidates.map do |process_server_name, deployment, task_name|
                existing = plan.find_local_tasks(task.model).
                    find_all { |t| t.orocos_name == task_name }
                [process_server_name, deployment, task_name, existing]
            end

            tasks_with_candidates[task] = candidates
        end
        raise MissingDeployments.new(tasks_with_candidates),
            "there are tasks for which it exists no deployed equivalent: #{not_deployed.map { |m| "#{m}(#{m.orogen_model.name})" }}"
    end
end