Class: KRPC::Client
- Inherits:
-
Object
- Object
- KRPC::Client
- Includes:
- Doc::SuffixMethods
- Defined in:
- lib/krpc/client.rb
Overview
A kRPC client, through which all Remote Procedure Calls are made. To make RPC calls client must first connect to server. This can be achieved by calling Client#connect or Client#connect! methods. Client object can connect and disconnect from the server many times during it’s lifetime. RPCs can be made by calling Client#execute_rpc method. After generating the services API (with Client#generate_services_api! call), RPCs can be also made using ‘client.service_name.procedure_name(parameter, …)`
### Example:
client = KRPC::Client.new(name: "my client").connect! # Notice that Client#connect! is shorthand for calling Client#connect and Client#generate_services_api! subsequently
ctrl = client.space_center.active_vessel.control
ctrl.activate_next_stage
ctrl.throttle = 1 # Full ahead!
client.close # Gracefully disconnect - and allow the spacecraft to crash ;)
client.connect do # Connect to server again
client.space_center.active_vessel.control.throttle = 0 # Save the spacecraft from imminent destruction ;)
end # Gracefully disconnect
Constant Summary collapse
- DEFAULT_NAME =
""
Constants included from Doc::SuffixMethods
Doc::SuffixMethods::DOCSTRING_SUFFIX, Doc::SuffixMethods::DOCSTRING_SUFFIX_REGEX
Instance Attribute Summary collapse
-
#core ⇒ Object
readonly
Returns the value of attribute core.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#rpc_connection ⇒ Object
readonly
Returns the value of attribute rpc_connection.
-
#stream_connection ⇒ Object
readonly
Returns the value of attribute stream_connection.
-
#streams_manager ⇒ Object
readonly
Returns the value of attribute streams_manager.
Instance Method Summary collapse
-
#build_exception(error) ⇒ Object
Build an exception from an PB::Error object.
-
#build_procedure_call(service, procedure, args = [], kwargs = {}, param_names = [], param_types = [], param_default = []) ⇒ Object
Build an PB::ProcedureCall object.
-
#build_request(service, procedure, args = [], kwargs = {}, param_names = [], param_types = [], param_default = []) ⇒ Object
Build an PB::Request object.
-
#close ⇒ Object
Close connection to kRPC server.
-
#connect(&block) ⇒ Object
Connect to a kRPC server on the IP address and port numbers specified during this client object creation and return ‘self`.
-
#connect!(&block) ⇒ Object
Connect to a kRPC server, generate the services API and return ‘self`.
-
#connected? ⇒ Boolean
Returns ‘true` if the client is connected to a server, `false` otherwise.
-
#execute_rpc(service, procedure, args = [], kwargs = {}, param_names = [], param_types = [], param_default = [], return_type: nil) ⇒ Object
Execute an RPC.
-
#generate_services_api! ⇒ Object
Interrogates the server to find out what functionality it provides and dynamically creates all of the classes and methods that form the services API.
-
#initialize(name: DEFAULT_NAME, host: Connection::DEFAULT_SERVER_HOST, rpc_port: Connection::DEFAULT_SERVER_RPC_PORT, stream_port: Connection::DEFAULT_SERVER_STREAM_PORT) ⇒ Client
constructor
Create new Client object, optionally specifying IP address and port numbers on witch kRPC server is listening and the name for this client.
-
#services_api_generated? ⇒ Boolean
Returns ‘true` if the services API has been already generated, `false` otherwise.
Methods included from Doc::SuffixMethods
included, #method_missing, #respond_to_missing?
Constructor Details
#initialize(name: DEFAULT_NAME, host: Connection::DEFAULT_SERVER_HOST, rpc_port: Connection::DEFAULT_SERVER_RPC_PORT, stream_port: Connection::DEFAULT_SERVER_STREAM_PORT) ⇒ Client
Create new Client object, optionally specifying IP address and port numbers on witch kRPC server is listening and the name for this client.
39 40 41 42 43 44 45 46 47 |
# File 'lib/krpc/client.rb', line 39 def initialize(name: DEFAULT_NAME, host: Connection::DEFAULT_SERVER_HOST, rpc_port: Connection::DEFAULT_SERVER_RPC_PORT, stream_port: Connection::DEFAULT_SERVER_STREAM_PORT) @name = name @rpc_connection = RPCConnection.new(name, host, rpc_port) @stream_connection = StreamConnection.new(rpc_connection, host, stream_port) @streams_manager = Streaming::StreamsManager.new(self) @services = {} @core = Services::Core.new(self) Doc.add_docstring_info(false, self.class, "core", return_type: @core.class, xmldoc: "<doc><summary>Core kRPC service, e.g. for querying for the available services. Most of this functionality is used internally by the Ruby client and therefore does not need to be used directly from application code. This service is hardcoded (in kRPC Ruby client) version of 'krpc' service, so 1) it is available even before the services API is generated, but 2) can be out of sync with 'krpc' service.</summary></doc>") end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method in the class KRPC::Doc::SuffixMethods
Instance Attribute Details
#core ⇒ Object (readonly)
Returns the value of attribute core.
35 36 37 |
# File 'lib/krpc/client.rb', line 35 def core @core end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
35 36 37 |
# File 'lib/krpc/client.rb', line 35 def name @name end |
#rpc_connection ⇒ Object (readonly)
Returns the value of attribute rpc_connection.
35 36 37 |
# File 'lib/krpc/client.rb', line 35 def rpc_connection @rpc_connection end |
#stream_connection ⇒ Object (readonly)
Returns the value of attribute stream_connection.
35 36 37 |
# File 'lib/krpc/client.rb', line 35 def stream_connection @stream_connection end |
#streams_manager ⇒ Object (readonly)
Returns the value of attribute streams_manager.
35 36 37 |
# File 'lib/krpc/client.rb', line 35 def streams_manager @streams_manager end |
Instance Method Details
#build_exception(error) ⇒ Object
Build an exception from an PB::Error object.
167 168 169 170 171 172 |
# File 'lib/krpc/client.rb', line 167 def build_exception(error) msg = error.description msg = "#{error.service}.#{error.name}: #{msg}" unless error.field_empty?(:service) || error.field_empty?(:name) msg += "\nServer stack trace:\n#{error.stack_trace}" unless error.field_empty?(:stack_trace) RPCError.new(msg) end |
#build_procedure_call(service, procedure, args = [], kwargs = {}, param_names = [], param_types = [], param_default = []) ⇒ Object
Build an PB::ProcedureCall object.
153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/krpc/client.rb', line 153 def build_procedure_call(service, procedure, args=[], kwargs={}, param_names=[], param_types=[], param_default=[]) begin raise(ArgumentError, "param_names and param_types should be equal length\n\tparam_names = #{param_names}\n\tparam_types = #{param_types}") unless param_names.length == param_types.length raise(ArgumentError, "param_names and param_default should be equal length\n\tparam_names = #{param_names}\n\tparam_default = #{param_default}") unless param_names.length == param_default.length required_params_count = param_default.take_while{|pd| pd == :no_default_value}.count raise ArgumentsNumberErrorSig.new(args.count, required_params_count..param_names.count) unless args.count <= param_names.count call_args = construct_arguments(args, kwargs, param_names, param_types, param_default, required_params_count) rescue ArgumentErrorSig => err raise err.with_signature(Doc.docstring_for_procedure(service, procedure, false)) end PB::ProcedureCall.new(service: service, procedure: procedure, arguments: call_args) end |
#build_request(service, procedure, args = [], kwargs = {}, param_names = [], param_types = [], param_default = []) ⇒ Object
Build an PB::Request object.
147 148 149 150 |
# File 'lib/krpc/client.rb', line 147 def build_request(service, procedure, args=[], kwargs={}, param_names=[], param_types=[], param_default=[]) call = build_procedure_call(service, procedure, args, kwargs, param_names, param_types, param_default) PB::Request.new(calls: [call]) end |
#close ⇒ Object
Close connection to kRPC server. Returns ‘true` if the connection has closed or `false` if the client had been already disconnected.
73 74 75 76 77 78 |
# File 'lib/krpc/client.rb', line 73 def close streams_manager.remove_all_streams streams_manager.stop_streaming_thread stream_connection.close rpc_connection.close end |
#connect(&block) ⇒ Object
Connect to a kRPC server on the IP address and port numbers specified during this client object creation and return ‘self`. Calling this method while the client is already connected will raise an exception. If the block is given, then it’s called passing ‘self` and the connection to kRPC server is closed at the end of the block.
53 54 55 56 57 58 59 |
# File 'lib/krpc/client.rb', line 53 def connect(&block) rpc_connection.connect stream_connection.connect streams_manager.start_streaming_thread call_block_and_close(block) if block_given? self end |
#connect!(&block) ⇒ Object
Connect to a kRPC server, generate the services API and return ‘self`. Shorthand for calling Client#connect and Client#generate_services_api! subsequently. If the block is given, then it’s called passing ‘self` and the connection to kRPC server is closed at the end of the block.
64 65 66 67 68 69 |
# File 'lib/krpc/client.rb', line 64 def connect!(&block) connect generate_services_api! call_block_and_close(block) if block_given? self end |
#connected? ⇒ Boolean
Returns ‘true` if the client is connected to a server, `false` otherwise.
81 82 83 |
# File 'lib/krpc/client.rb', line 81 def connected? rpc_connection.connected? end |
#execute_rpc(service, procedure, args = [], kwargs = {}, param_names = [], param_types = [], param_default = [], return_type: nil) ⇒ Object
Execute an RPC.
132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/krpc/client.rb', line 132 def execute_rpc(service, procedure, args=[], kwargs={}, param_names=[], param_types=[], param_default=[], return_type: nil) send_request(service, procedure, args, kwargs, param_names, param_types, param_default) result = receive_result raise build_exception(result.error) unless result.field_empty? :error unless return_type.nil? Decoder.decode(result.value, return_type, self) else nil end rescue IOError => e raise(Error, "RPC call attempt while not connected to a server -- call Client#connect first") if not connected? raise e end |
#generate_services_api! ⇒ Object
Interrogates the server to find out what functionality it provides and dynamically creates all of the classes and methods that form the services API. For each service that server provides:
-
Class ‘KRPC::Services::name here`, and module `KRPC::Gen::name here` are created.
-
‘KRPC::Gen::name here` module is filled with dynamically created classes.
-
Those classes in turn are filled with dynamically created methods, which form the API for this service.
-
Instance method ‘name here` is created in this client object that returns `KRPC::Services::name here` object. This object is entry point for accessing functionality provided by `name here` service.
Returns ‘self`. Invoking this method the second and subsequent times doesn’t regenerate the API. To regenerate the API create new Client object and call #generate_services_api! on it.
### Example
client = KRPC::Client.new(name: "my client").connect # Notice that it is 'Client#connect' being called, not 'Client#connect!'
sc = client.space_center # => Exception (undefined method "space_center")
client.generate_services_api!
sc = client.space_center # => KRPC::Services::SpaceCenter object
v = sc.active_vessel # => KRPC::Gen::SpaceCenter::Vessel object
v.mass # => {some number here}
client.close
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/krpc/client.rb', line 108 def generate_services_api! return self if services_api_generated? raise(Error, "Can't generate the services API while not connected to a server -- call Client#connect! to connect to server and generate the services API in one call") if not connected? resp = core.get_services resp.services.each do |service_msg| service_class = Services.create_service(service_msg) method_name = service_class.class_name.underscore self.class.instance_eval do define_method method_name do @services[service_class.class_name] ||= service_class.new(self) end end Doc.add_docstring_info(false, self.class, method_name, return_type: service_class, xmldoc: service_msg.documentation) end self end |
#services_api_generated? ⇒ Boolean
Returns ‘true` if the services API has been already generated, `false` otherwise.
127 128 129 |
# File 'lib/krpc/client.rb', line 127 def services_api_generated? respond_to? :space_center or respond_to? :test_service end |