class Orocos::RemoteProcesses::Client
Client-side API to a {Server} instance
Process servers allow to start/stop and monitor processes on remote machines. Instances of this class provides access to remote process servers.
Attributes
Mapping from deployment names to the corresponding orogen project name. It lists the deployments that are available on the remote process server.
Mapping from orogen project names to the corresponding content of the orogen files. These projects are the ones available to the remote process server
Mapping from deployment names to the corresponding XML type registry for the typekits available on the process server
The hostname we are connected to
A string that allows to uniquely identify this process server
The loader object that allows to access models from the remote server @return [Loader]
The name service object that allows to resolve tasks from this process server
The port on which we are connected on hostname
Mapping from a deployment name to the corresponding RemoteProcess instance, for processes that have been started by this client.
The root loader object @return [OroGen::Loaders::Base]
The PID of the server process
The socket instance used to communicate with the server
Public Class Methods
Connects to the process server at host:port
@option options [Orocos::NameService] :name_service
(Orocos.name_service). The name service object that should be used to resolve tasks started by this process server
@option options [OroGen::Loaders::Base] :root_loader
(Orocos.default_loader). The loader object that should be used as root for this client's loader
# File lib/orocos/remote_processes/client.rb, line 64 def initialize(host = 'localhost', port = DEFAULT_PORT, response_timeout: 10, root_loader: Orocos.default_loader, name_service: Orocos.name_service) @host = host @port = port @socket = begin TCPSocket.new(host, port) rescue Errno::ECONNREFUSED => e raise e.class, "cannot contact process server at '#{host}:#{port}': #{e.message}" end socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true) socket.fcntl(Fcntl::FD_CLOEXEC, 1) @name_service = name_service begin @server_pid = pid rescue EOFError raise StartupFailed, "process server failed at '#{host}:#{port}'" end @loader = Loader.new(self, root_loader) @root_loader = loader.root_loader @processes = Hash.new @death_queue = Array.new @host_id = "#{host}:#{port}:#{server_pid}" @response_timeout = response_timeout end
Public Instance Methods
# File lib/orocos/remote_processes/client.rb, line 277 def close socket.close end
Creates a new log dir, and save the given time tag in it (used later on by #save_log_dir)
# File lib/orocos/remote_processes/client.rb, line 197 def create_log_dir(log_dir, time_tag, metadata = Hash.new) socket.write(COMMAND_CREATE_LOG) Marshal.dump([log_dir, time_tag, metadata], socket) end
# File lib/orocos/remote_processes/client.rb, line 111 def disconnect socket.close end
# File lib/orocos/remote_processes/client.rb, line 103 def info(timeout: @response_timeout) socket.write(COMMAND_GET_INFO) if !select([socket], [], [], timeout) raise "timeout while reading process server at '#{host}:#{port}'" end Marshal.load(socket) end
# File lib/orocos/remote_processes/client.rb, line 54 def inspect; to_s end
# File lib/orocos/remote_processes/client.rb, line 263 def join(deployment_name) process = processes[deployment_name] return if !process while true result = wait_termination(nil) return if result[process] end end
# File lib/orocos/remote_processes/client.rb, line 91 def pid(timeout: @response_timeout) if @server_pid return @server_pid end socket.write(COMMAND_GET_PID) if !select([socket], [], [], timeout) raise "timeout while reading process server at '#{host}:#{port}'" end @server_pid = Integer(Marshal.load(socket).first) end
# File lib/orocos/remote_processes/client.rb, line 202 def queue_death_announcement @death_queue.push Marshal.load(socket) end
# File lib/orocos/remote_processes/client.rb, line 273 def quit_server socket.write(COMMAND_QUIT) end
Requests that the process server moves the log directory at
log_dir to results_dir
# File lib/orocos/remote_processes/client.rb, line 190 def save_log_dir(log_dir, results_dir) socket.write(COMMAND_MOVE_LOG) Marshal.dump([log_dir, results_dir], socket) end
Starts the given deployment on the remote server, without waiting for it to be ready.
Returns a RemoteProcess instance that represents the process on the remote side.
Raises Failed if the server reports a startup failure
# File lib/orocos/remote_processes/client.rb, line 154 def start(process_name, deployment, name_mappings = Hash.new, options = Hash.new) if processes[process_name] raise ArgumentError, "this client already started a process called #{process_name}" end if deployment.respond_to?(:to_str) deployment_model = loader.root_loader.deployment_model_from_name(deployment) if !loader.has_deployment?(deployment) raise OroGen::DeploymentModelNotFound, "deployment #{deployment} exists locally but not on the remote process server #{self}" end else deployment_model = deployment end prefix_mappings = Orocos::ProcessBase.resolve_prefix(deployment_model, options.delete(:prefix)) name_mappings = prefix_mappings.merge(name_mappings) socket.write(COMMAND_START) Marshal.dump([process_name, deployment_model.name, name_mappings, options], socket) wait_for_answer do |pid_s| if pid_s == RET_NO msg = Marshal.load(socket) raise Failed, "failed to start #{deployment_model.name}: #{msg}" elsif pid_s == RET_STARTED_PROCESS pid = Marshal.load(socket) process = Process.new(process_name, deployment_model, self, pid) process.name_mappings = name_mappings processes[process_name] = process return process else raise InternalError, "unexpected reply #{pid_s} to the start command" end end end
Requests to stop the given deployment
The call does not block until the process has quit. You will have to call wait_termination to wait for the process end.
# File lib/orocos/remote_processes/client.rb, line 251 def stop(deployment_name, wait) socket.write(COMMAND_END) Marshal.dump(deployment_name, socket) if !wait_for_ack raise Failed, "failed to quit #{deployment_name}" end if wait join(deployment_name) end end
# File lib/orocos/remote_processes/client.rb, line 51 def to_s "#<Orocos::RemoteProcesses::Client #{host}:#{port}>" end
# File lib/orocos/remote_processes/client.rb, line 135 def wait_for_ack wait_for_answer do |reply| if reply == RET_YES return true elsif reply == RET_NO return false else raise InternalError, "unexpected reply #{reply}" end end end
# File lib/orocos/remote_processes/client.rb, line 118 def wait_for_answer(timeout: @response_timeout) while true if !select([socket], [], [], timeout) raise TimeoutError, "reached timeout of #{timeout}s in #wait_for_answer" end reply = socket.read(1) if !reply raise Orocos::ComError, "failed to read from process server #{self}" elsif reply == EVENT_DEAD_PROCESS queue_death_announcement else yield(reply) end end end
Waits for processes to terminate. timeout is the number of
milliseconds we should wait. If set to nil, the call will block until a
process terminates
Returns a hash that maps deployment names to the Process::Status object that represents their exit status.
# File lib/orocos/remote_processes/client.rb, line 212 def wait_termination(timeout = nil) if @death_queue.empty? reader = select([socket], nil, nil, timeout) return Hash.new if !reader while reader if socket.eof? # remote closed, probably a crash raise ComError, "communication to process server closed" end data = socket.read(1) if !data return Hash.new elsif data != EVENT_DEAD_PROCESS raise "unexpected message #{data} from process server" end queue_death_announcement reader = select([socket], nil, nil, 0) end end result = Hash.new @death_queue.each do |name, status| Orocos.debug "#{name} died" if p = processes.delete(name) p.dead! result[p] = status else Orocos.warn "process server reported the exit of '#{name}', but no process with that name is registered" end end @death_queue.clear result end