krpc 0.3.2 → 0.4.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ef6917b47511f404073511fc327657a34d46967f
4
- data.tar.gz: 06ef229097a2524042524b155ff38c3ff110f5ee
3
+ metadata.gz: f8d2668d37ed41486d38de335c5d77eb9fbf7ea4
4
+ data.tar.gz: 76ccaf2af65825e916d3202ba06be94cc8c5d9a9
5
5
  SHA512:
6
- metadata.gz: cf70656dc94287f0d52f00af03d338258779612a56e09957a0b02e740155477baeb0c508e95dcff5dd5d3ebeb5e31949a2eb72b1e2e0296abcf098638ba5b176
7
- data.tar.gz: 1e54734369a2c4f72add79e9325e71698f19ba3409bb84747f9351b8df9c72a020be5d1950b516ba6ebe5984351724774e612717d77a65eaa8b5c3555d3fa4e8
6
+ metadata.gz: fa42767df76f7f37d10e493ecfed5fcde5bb23a7b5e2d07b944f7e15ff1ef98c89c00b997326d318a4503b0159f03e7c681184cefd22e309afa574cabef0d20e
7
+ data.tar.gz: 449e4c7cd429e6b5bb10e05559ebf1692a59d0a5044f60a71912c19e623ffdd8183391b7f98854308846e17de6b0e2c2e98823ed345bc907ed8e2dba23d48ec4
data/README.md CHANGED
@@ -10,8 +10,6 @@ Installation
10
10
 
11
11
  gem install krpc
12
12
 
13
- or install the latest pre-release version (if [available](https://rubygems.org/gems/krpc/versions)): `gem install krpc --pre`
14
-
15
13
  Basic usage
16
14
  -------
17
15
 
@@ -96,7 +94,7 @@ SpaceCenter.transform_position(
96
94
  to :ReferenceFrame - The reference frame to covert the position vector to.
97
95
  ) :Array[Float, Float, Float] - The corresponding position vector in reference frame to.
98
96
 
99
- Converts a position vector from one reference frame to another.
97
+ Converts a position vector from one reference frame to another.
100
98
  => nil
101
99
  ```
102
100
 
@@ -135,6 +133,6 @@ pos_stream.remove #note: dead code - just as an example
135
133
  Want to know more?
136
134
  -------
137
135
  * Read official **kRPC documentation** at https://krpc.github.io/krpc, with many great [tutorials and examples](https://krpc.github.io/krpc/tutorials.html).
138
- * Refer to **kRPC-rb documentation** at https://tewu.github.io/krpc-rb-apidocs
136
+ * Refer to **kRPC-rb API documentation** at https://tewu.github.io/krpc-rb-apidocs
139
137
  * See official **kRPC forum thread** at http://forum.kerbalspaceprogram.com/threads/69313
140
138
 
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
15
15
  s.homepage = "https://github.com/TeWu/krpc-rb"
16
16
  s.license = "GPL-3.0"
17
17
 
18
- s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(doc|test|spec|features|bin)/|Rakefile|CHANGELOG}) || f.start_with?('.') }
18
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(doc|test|spec|features|bin/TestServer)/|Rakefile|CHANGELOG}) || f.start_with?('.') }
19
19
  s.require_paths = ["lib"]
20
20
  s.extra_rdoc_files = ["README.md"]
21
21
  s.rdoc_options << "--markup" << "markdown" <<
@@ -24,12 +24,12 @@ Gem::Specification.new do |s|
24
24
 
25
25
  s.required_ruby_version = ">= 2.1.0"
26
26
 
27
- s.add_runtime_dependency "google-protobuf", "~> 3.3"
27
+ s.add_runtime_dependency "google-protobuf", "~> 3.1"
28
28
  s.add_runtime_dependency "colorize", "~> 0.8"
29
- s.add_runtime_dependency "nokogiri", "~> 1.8"
30
- s.add_development_dependency "bundler", "~> 1.15"
29
+ s.add_runtime_dependency "nokogiri", "~> 1.6"
30
+ s.add_development_dependency "bundler", "~> 1.13"
31
31
  s.add_development_dependency "pry", "~> 0.10"
32
- s.add_development_dependency "rspec", "~> 3.6"
33
- s.add_development_dependency "rake", "~> 12.0"
32
+ s.add_development_dependency "rspec", "~> 3.5"
33
+ s.add_development_dependency "rake", "~> 11.3"
34
34
  s.add_development_dependency "hanna-nouveau", "~> 1.0"
35
35
  end
@@ -4,13 +4,13 @@ require 'krpc/client'
4
4
 
5
5
  module KRPC
6
6
  class << self
7
-
7
+
8
8
  # Connect to a kRPC server, generate services API and return Client object. If the block is
9
9
  # given, then it's called passing Client object and the connection to kRPC server is closed
10
10
  # at the end of the block.
11
11
  def connect(*args, &block)
12
12
  Client.new(*args).connect!(&block)
13
13
  end
14
-
14
+
15
15
  end
16
16
  end
@@ -9,17 +9,17 @@ require 'krpc/core_extensions'
9
9
  require 'krpc/krpc.pb'
10
10
 
11
11
  module KRPC
12
-
12
+
13
13
  ##
14
- # A kRPC client, through which all Remote Procedure Calls are made. To make RPC calls client
14
+ # A kRPC client, through which all Remote Procedure Calls are made. To make RPC calls client
15
15
  # must first connect to server. This can be achieved by calling Client#connect or Client#connect!
16
- # methods. Client object can connect and disconnect from the server many times during it's
17
- # lifetime. RPCs can be made by calling Client#execute_rpc method. After generating services API (with
18
- # Client#generate_services_api! call), RPCs can be also made using
16
+ # methods. Client object can connect and disconnect from the server many times during it's
17
+ # lifetime. RPCs can be made by calling Client#execute_rpc method. After generating the services API
18
+ # (with Client#generate_services_api! call), RPCs can be also made using
19
19
  # `client.service_name.procedure_name(parameter, ...)`
20
20
  #
21
21
  # ### Example:
22
- # client = KRPC::Client.new(name: "my client").connect!
22
+ # client = KRPC::Client.new(name: "my client").connect! # Notice that Client#connect! is shorthand for calling Client#connect and Client#generate_services_api! subsequently
23
23
  # ctrl = client.space_center.active_vessel.control
24
24
  # ctrl.activate_next_stage
25
25
  # ctrl.throttle = 1 # Full ahead!
@@ -34,8 +34,8 @@ module KRPC
34
34
 
35
35
  attr_reader :name, :rpc_connection, :stream_connection, :streams_manager, :core
36
36
 
37
- # Create new Client object, optionally specifying IP address and port numbers on witch kRPC
38
- # server is listening and the name for this client (up to 32 bytes of UTF-8 encoded text).
37
+ # Create new Client object, optionally specifying IP address and port numbers on witch kRPC
38
+ # server is listening and the name for this client.
39
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)
40
40
  @name = name
41
41
  @rpc_connection = RPCConnection.new(name, host, rpc_port)
@@ -43,12 +43,12 @@ module KRPC
43
43
  @streams_manager = Streaming::StreamsManager.new(self)
44
44
  @services = {}
45
45
  @core = Services::Core.new(self)
46
- 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 services API is generated, but 2) can be out of sync with 'krpc' service.</summary></doc>")
46
+ 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>")
47
47
  end
48
-
48
+
49
49
  # Connect to a kRPC server on the IP address and port numbers specified during this client
50
50
  # object creation and return `self`. Calling this method while the client is already connected
51
- # will raise an exception. If the block is given, then it's called passing `self` and the
51
+ # will raise an exception. If the block is given, then it's called passing `self` and the
52
52
  # connection to kRPC server is closed at the end of the block.
53
53
  def connect(&block)
54
54
  rpc_connection.connect
@@ -57,9 +57,9 @@ module KRPC
57
57
  call_block_and_close(block) if block_given?
58
58
  self
59
59
  end
60
-
61
- # Connect to a kRPC server, generate services API and return `self`. Shorthand for calling
62
- # Client#connect and Client#generate_services_api! subsequently. If the block is given, then
60
+
61
+ # Connect to a kRPC server, generate the services API and return `self`. Shorthand for calling
62
+ # Client#connect and Client#generate_services_api! subsequently. If the block is given, then
63
63
  # it's called passing `self` and the connection to kRPC server is closed at the end of the block.
64
64
  def connect!(&block)
65
65
  connect
@@ -67,8 +67,8 @@ module KRPC
67
67
  call_block_and_close(block) if block_given?
68
68
  self
69
69
  end
70
-
71
- # Close connection to kRPC server. Returns `true` if the connection has closed or `false` if
70
+
71
+ # Close connection to kRPC server. Returns `true` if the connection has closed or `false` if
72
72
  # the client had been already disconnected.
73
73
  def close
74
74
  streams_manager.remove_all_streams
@@ -81,21 +81,21 @@ module KRPC
81
81
  def connected?
82
82
  rpc_connection.connected?
83
83
  end
84
-
85
- # Interrogates the server to find out what functionality it provides and dynamically creates
86
- # all of the classes and methods that form services API. For each service that server provides:
87
- #
84
+
85
+ # Interrogates the server to find out what functionality it provides and dynamically creates
86
+ # all of the classes and methods that form the services API. For each service that server provides:
87
+ #
88
88
  # 1. Class `KRPC::Services::{service name here}`, and module `KRPC::Gen::{service name here}`
89
89
  # are created.
90
90
  # 2. `KRPC::Gen::{service name here}` module is filled with dynamically created classes.
91
- # 3. Those classes in turn are filled with dynamically created methods, that form API for
91
+ # 3. Those classes in turn are filled with dynamically created methods, which form the API for
92
92
  # this service.
93
- # 4. Instance method `{service name here}` is created in this client object that returns
93
+ # 4. Instance method `{service name here}` is created in this client object that returns
94
94
  # `KRPC::Services::{service name here}` object. This object is entry point for accessing
95
95
  # functionality provided by `{service name here}` service.
96
96
  #
97
- # Returns `self`. Invoking this method the second and subsequent times doesn't regenerate API.
98
- # To regenerate API create new Client object and call #generate_services_api! on it.
97
+ # Returns `self`. Invoking this method the second and subsequent times doesn't regenerate the API.
98
+ # To regenerate the API create new Client object and call #generate_services_api! on it.
99
99
  #
100
100
  # ### Example
101
101
  # client = KRPC::Client.new(name: "my client").connect # Notice that it is 'Client#connect' being called, not 'Client#connect!'
@@ -107,8 +107,8 @@ module KRPC
107
107
  # client.close
108
108
  def generate_services_api!
109
109
  return self if services_api_generated?
110
- raise(Exception, "Can't generate services API while not connected to server -- call Client#connect! to connect to server and generate services API in one call") if not connected?
111
-
110
+ raise(Exception, "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?
111
+
112
112
  resp = core.get_services
113
113
  resp.services.each do |service_msg|
114
114
  service_class = Services.create_service(service_msg)
@@ -122,48 +122,54 @@ module KRPC
122
122
  end
123
123
  self
124
124
  end
125
-
126
- # Returns `true` if services API has been already generated, `false` otherwise.
125
+
126
+ # Returns `true` if the services API has been already generated, `false` otherwise.
127
127
  def services_api_generated?
128
128
  respond_to? :space_center or respond_to? :test_service
129
129
  end
130
-
130
+
131
131
  # Execute an RPC.
132
132
  def execute_rpc(service, procedure, args=[], kwargs={}, param_names=[], param_types=[], param_default=[], return_type: nil)
133
133
  send_request(service, procedure, args, kwargs, param_names, param_types, param_default)
134
- resp = receive_response
135
- raise(RPCError, resp.error) if resp.has_error
134
+ result = receive_result
135
+ raise(RPCError, result.error) unless result.field_empty? :error
136
136
  unless return_type.nil?
137
- Decoder.decode(resp.return_value, return_type, self)
137
+ Decoder.decode(result.value, return_type, self)
138
138
  else
139
139
  nil
140
140
  end
141
141
  rescue IOError => e
142
- raise(Exception, "RPC call attempt while not connected to server -- call Client#connect first") if not connected?
142
+ raise(Exception, "RPC call attempt while not connected to a server -- call Client#connect first") if not connected?
143
143
  raise e
144
144
  end
145
145
 
146
146
  # Build a PB::Request object.
147
147
  def build_request(service, procedure, args=[], kwargs={}, param_names=[], param_types=[], param_default=[])
148
+ call = build_procedure_call(service, procedure, args, kwargs, param_names, param_types, param_default)
149
+ PB::Request.new(calls: [call])
150
+ end
151
+
152
+ # Build a PB::ProcedureCall object.
153
+ def build_procedure_call(service, procedure, args=[], kwargs={}, param_names=[], param_types=[], param_default=[])
148
154
  begin
149
155
  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
150
156
  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
151
157
  required_params_count = param_default.take_while{|pd| pd == :no_default_value}.count
152
158
  raise ArgumentsNumberErrorSig.new(args.count, required_params_count..param_names.count) unless args.count <= param_names.count
153
- req_args = construct_arguments(args, kwargs, param_names, param_types, param_default, required_params_count)
159
+ call_args = construct_arguments(args, kwargs, param_names, param_types, param_default, required_params_count)
154
160
  rescue ArgumentErrorSig => err
155
161
  raise err.with_signature(Doc.docstring_for_procedure(service, procedure, false))
156
162
  end
157
- PB::Request.new(service: service, procedure: procedure, arguments: req_args)
163
+ PB::ProcedureCall.new(service: service, procedure: procedure, arguments: call_args)
158
164
  end
159
165
 
160
166
  protected #----------------------------------
161
-
167
+
162
168
  def construct_arguments(args, kwargs, param_names, param_types, param_default, required_params_count)
163
169
  param_names_symbols = param_names.map(&:to_sym)
164
170
  kwargs_remaining = kwargs.count
165
-
166
- req_args = param_names_symbols.map.with_index do |name, i|
171
+
172
+ call_args = param_names_symbols.map.with_index do |name, i|
167
173
  is_kwarg = kwargs.has_key? name
168
174
  kwargs_remaining -= 1 if is_kwarg
169
175
  raise ArgumentErrorSig.new("there are both positional and keyword arguments for parameter \"#{name}\"") if is_kwarg && i < args.count
@@ -188,25 +194,25 @@ module KRPC
188
194
  PB::Argument.new(position: i, value: v)
189
195
  end
190
196
  end.compact
191
-
197
+
192
198
  raise ArgumentErrorSig.new("keyword arguments for non existing parameters: #{(kwargs.keys - param_names_symbols).join(", ")}") unless kwargs_remaining == 0
193
- req_args
199
+ call_args
194
200
  end
195
201
 
196
202
  def send_request(service, procedure, args, kwargs, param_names, param_types, param_default)
197
203
  req = build_request(service, procedure, args, kwargs, param_names, param_types, param_default)
198
- rpc_connection.send Encoder.encode_request(req)
204
+ rpc_connection.send_message req
199
205
  end
200
-
201
- def receive_response
202
- resp_length = rpc_connection.recv_varint
203
- resp_data = rpc_connection.recv resp_length
204
- resp = PB::Response.decode(resp_data)
206
+
207
+ def receive_result
208
+ resp = rpc_connection.receive_message PB::Response
209
+ raise(RPCError, resp.error) unless resp.field_empty? :error
210
+ resp.results[0]
205
211
  end
206
-
212
+
207
213
  def call_block_and_close(block)
208
214
  begin block.call(self) ensure close end
209
215
  end
210
-
216
+
211
217
  end
212
218
  end
@@ -10,18 +10,18 @@ module KRPC
10
10
  DEFAULT_SERVER_HOST = "127.0.0.1"
11
11
  DEFAULT_SERVER_RPC_PORT = 50000
12
12
  DEFAULT_SERVER_STREAM_PORT = 50001
13
-
13
+
14
14
  attr_reader :host, :port, :socket
15
-
15
+
16
16
  def initialize(host, port)
17
17
  @host, @port = host, port
18
18
  end
19
-
19
+
20
20
  # Connect and perform handshake.
21
21
  def connect
22
22
  if connected? then raise(ConnectionError, "Already connected")
23
- else
24
- @socket = TCPSocket.open(host, port)
23
+ else
24
+ @socket = TCPSocket.open(host, port)
25
25
  begin
26
26
  handshake
27
27
  rescue Exception => e
@@ -31,7 +31,7 @@ module KRPC
31
31
  end
32
32
  self
33
33
  end
34
-
34
+
35
35
  # Close connection and clean up.
36
36
  def close
37
37
  if connected?
@@ -40,20 +40,32 @@ module KRPC
40
40
  true
41
41
  else false end
42
42
  end
43
-
43
+
44
44
  # Return `true` if connected to a server, `false` otherwise.
45
45
  def connected?
46
46
  !socket.nil? && !socket.closed?
47
47
  end
48
-
48
+
49
49
  def handshake; end
50
50
  def cleanup; end
51
-
52
- def send(msg) @socket.send(msg, 0) end
51
+
52
+ def protobuf_handshake(type, **attrs)
53
+ send_message PB::ConnectionRequest.new(type: type, **attrs)
54
+ resp = receive_message PB::ConnectionResponse
55
+ raise(ConnectionError, "#{resp.status} -- #{resp.message}") unless resp.status == :OK
56
+ resp
57
+ end
58
+
59
+ def send(data)
60
+ @socket.send(data, 0)
61
+ end
62
+ def send_message(msg)
63
+ send Encoder.encode_message_with_size(msg)
64
+ end
65
+
53
66
  def recv(maxlen = 1)
54
67
  maxlen == 0 ? "" : @socket.read(maxlen)
55
68
  end
56
-
57
69
  def recv_varint
58
70
  int_val = 0
59
71
  shift = 0
@@ -65,33 +77,29 @@ module KRPC
65
77
  raise(RuntimeError, "too many bytes when decoding varint") if shift >= 64
66
78
  end
67
79
  end
68
-
69
- protected #----------------------------------
70
-
71
- def trim_fill(str, len, fill_char = "\x00")
72
- str = str.encode("UTF-8")[0, len]
73
- str + fill_char*(len-str.length)
80
+ def receive_message(msg_type)
81
+ msg_length = recv_varint
82
+ msg_data = recv(msg_length)
83
+ msg_type.decode(msg_data)
74
84
  end
75
85
  end
76
86
 
77
87
  ##
78
- # TCP connection for sending RPC calls and retrieving it's results.
88
+ # TCP connection for sending RPC calls and retrieving its results.
79
89
  class RPCConnection < Connection
80
90
  attr_reader :name, :client_id
81
-
82
- def initialize(name, host = DEFAULT_SERVER_HOST, port = DEFAULT_SERVER_RPC_PORT)
91
+
92
+ def initialize(name = Client::DEFAULT_NAME, host = DEFAULT_SERVER_HOST, port = DEFAULT_SERVER_RPC_PORT)
83
93
  super host, port
84
94
  @name = name
85
95
  end
86
96
 
87
97
  # Perform handshake with kRPC server, obtaining `@client_id`.
88
98
  def handshake
89
- send Encoder::RPC_HELLO_MESSAGE
90
- send trim_fill(name, Encoder::NAME_LENGTH)
91
- @client_id = recv Decoder::GUID_LENGTH
99
+ resp = protobuf_handshake(:RPC, client_name: name)
100
+ @client_id = resp.client_identifier
92
101
  end
93
-
94
- # Clean up - sets `@client_id` to `nil`.
102
+
95
103
  def cleanup
96
104
  @client_id = nil
97
105
  end
@@ -101,20 +109,17 @@ module KRPC
101
109
  # TCP connection for streaming.
102
110
  class StreamConnection < Connection
103
111
  attr_reader :rpc_connection
104
-
112
+
105
113
  def initialize(rpc_connection, host = DEFAULT_SERVER_HOST, port = DEFAULT_SERVER_STREAM_PORT)
106
114
  super host, port
107
115
  @rpc_connection = rpc_connection
108
116
  end
109
-
117
+
110
118
  # Perform handshake with kRPC server, sending `client_id` retrieved from `rpc_connection`.
111
119
  def handshake
112
- raise(ConnectionError, "RPC connection must obtain client_id before stream connection can perform valid handshake - closing stream connection") if rpc_connection.client_id.nil?
113
- send Encoder::STREAM_HELLO_MESSAGE
114
- send rpc_connection.client_id
115
- resp = recv Decoder::OK_LENGTH
116
- raise ConnectionError unless resp == Decoder::OK_MESSAGE
120
+ raise(ConnectionError, "RPC connection must obtain client_id before stream connection can perform valid handshake - closing stream connection") if rpc_connection.client_id.nil?
121
+ protobuf_handshake(:STREAM, client_identifier: rpc_connection.client_id)
117
122
  end
118
123
  end
119
-
124
+
120
125
  end
@@ -3,7 +3,7 @@ class Module
3
3
  def class_name
4
4
  name.rpartition("::").last
5
5
  end
6
-
6
+
7
7
  def const_get_or_create(module_name, value = nil, &block)
8
8
  return const_get(module_name) if const_defined?(module_name, false)
9
9
  value = block.call if block_given?
@@ -26,9 +26,9 @@ class String
26
26
  end
27
27
 
28
28
  def integer?
29
- Integer(self) != nil rescue false
29
+ Integer(self) != nil rescue false
30
30
  end
31
-
31
+
32
32
  def numeric?
33
33
  Float(self) != nil rescue false
34
34
  end