openc3 5.4.3.pre.beta0 → 5.5.0.pre.beta0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of openc3 might be problematic. Click here for more details.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47eef747a6f82da0e4cdf93863f00357e07be1002d58762c4345c611e53da9a5
|
4
|
+
data.tar.gz: 6b6b804cac37eab03765a619aedd6ec4e6d4e8a9dfe5048507d23bd9c0987542
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f862cfdc31ded71fe8eaa59f8771ccb88d45b0363fa17636c6ed8a0c1cfcb30343f4850b57fba89dc9fd221e9a70cb7af7524081bb4d7f24e8d263c816b3879
|
7
|
+
data.tar.gz: 3604545c30b970d8ed6e6c0bf69c8d6ba0eeffefd3cb0c043d65ea140dfb15936bcd3d9ffb94bdb21493d40c05213838bdf7df215ee12597e41275cc903d16ee
|
data/lib/openc3/script/script.rb
CHANGED
@@ -31,6 +31,7 @@ require 'openc3/script/limits'
|
|
31
31
|
require 'openc3/script/exceptions'
|
32
32
|
require 'openc3/script/script_runner'
|
33
33
|
require 'openc3/script/storage'
|
34
|
+
require 'openc3/script/web_socket_api'
|
34
35
|
require 'openc3/utilities/authentication'
|
35
36
|
|
36
37
|
$api_server = nil
|
@@ -99,6 +100,15 @@ module OpenC3
|
|
99
100
|
# NOOP
|
100
101
|
end
|
101
102
|
|
103
|
+
def openc3_script_sleep(sleep_time = nil)
|
104
|
+
if sleep_time
|
105
|
+
sleep(sleep_time)
|
106
|
+
else
|
107
|
+
prompt("Press any key to continue...")
|
108
|
+
end
|
109
|
+
return false
|
110
|
+
end
|
111
|
+
|
102
112
|
def ask_string(question, blank_or_default = false, password = false)
|
103
113
|
answer = ''
|
104
114
|
default = ''
|
@@ -0,0 +1,423 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2023 OpenC3, Inc.
|
4
|
+
# All Rights Reserved.
|
5
|
+
#
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
7
|
+
# under the terms of the GNU Affero General Public License
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# This file may also be used under the terms of a commercial license
|
17
|
+
# if purchased from OpenC3, Inc.
|
18
|
+
|
19
|
+
require 'openc3/streams/web_socket_client_stream'
|
20
|
+
require 'openc3/utilities/authentication'
|
21
|
+
require 'openc3/io/json_rpc'
|
22
|
+
|
23
|
+
module OpenC3
|
24
|
+
# Base class - Do not use directly
|
25
|
+
class WebSocketApi
|
26
|
+
USER_AGENT = 'OpenC3 / v5 (ruby/openc3/lib/io/web_socket_api)'.freeze
|
27
|
+
|
28
|
+
# Create the WebsocketApi object. If a block is given will automatically connect/disconnect
|
29
|
+
def initialize(url:, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope, &block)
|
30
|
+
@scope = scope
|
31
|
+
@authentication = authentication.nil? ? generate_auth() : authentication
|
32
|
+
@url = url
|
33
|
+
@write_timeout = write_timeout
|
34
|
+
@read_timeout = read_timeout
|
35
|
+
@connect_timeout = connect_timeout
|
36
|
+
@subscribed = false
|
37
|
+
if block_given?
|
38
|
+
begin
|
39
|
+
connect()
|
40
|
+
yield self
|
41
|
+
ensure
|
42
|
+
disconnect()
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Read the next message without filtering / parsing
|
48
|
+
def read_message
|
49
|
+
subscribe()
|
50
|
+
return @stream.read
|
51
|
+
end
|
52
|
+
|
53
|
+
# Read the next message with json parsing, filtering, and timeout support
|
54
|
+
def read(ignore_protocol_messages: true, timeout: nil)
|
55
|
+
start_time = Time.now
|
56
|
+
while true
|
57
|
+
message = read_message()
|
58
|
+
if message
|
59
|
+
json_hash = JSON.parse(message, allow_nan: true, create_additions: true)
|
60
|
+
if ignore_protocol_messages
|
61
|
+
type = json_hash['type']
|
62
|
+
if type # ping, welcome, confirm_subscription, reject_subscription, disconnect
|
63
|
+
if type == 'disconnect'
|
64
|
+
if json_hash['reason'] == 'unauthorized'
|
65
|
+
raise "Unauthorized"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
if type == 'reject_subscription'
|
69
|
+
raise "Subscription Rejected"
|
70
|
+
end
|
71
|
+
if timeout
|
72
|
+
end_time = Time.now
|
73
|
+
if (start_time - end_time) > timeout
|
74
|
+
raise Timeout::Error, "No Data Timeout"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
if defined? RunningScript and RunningScript.instance
|
78
|
+
raise StopScript if RunningScript.instance.stop?
|
79
|
+
end
|
80
|
+
next
|
81
|
+
end
|
82
|
+
end
|
83
|
+
return json_hash['message']
|
84
|
+
end
|
85
|
+
return message
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Will subscribe to the channel based on @identifier
|
90
|
+
def subscribe
|
91
|
+
unless @subscribed
|
92
|
+
json_hash = {}
|
93
|
+
json_hash['command'] = 'subscribe'
|
94
|
+
json_hash['identifier'] = JSON.generate(@identifier)
|
95
|
+
@stream.write(JSON.generate(json_hash))
|
96
|
+
@subscribed = true
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Will unsubscribe to the channel based on @identifier
|
101
|
+
def unsubscribe
|
102
|
+
if @subscribed
|
103
|
+
json_hash = {}
|
104
|
+
json_hash['command'] = 'unsubscribe'
|
105
|
+
json_hash['identifier'] = JSON.generate(@identifier)
|
106
|
+
@stream.write(JSON.generate(json_hash))
|
107
|
+
@subscribed = false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Send an ActionCable command
|
112
|
+
def write_action(data_hash)
|
113
|
+
json_hash = {}
|
114
|
+
json_hash['command'] = 'message'
|
115
|
+
json_hash['identifier'] = JSON.generate(@identifier)
|
116
|
+
json_hash['data'] = JSON.generate(data_hash)
|
117
|
+
write(JSON.generate(json_hash))
|
118
|
+
end
|
119
|
+
|
120
|
+
# General write to the websocket
|
121
|
+
def write(data)
|
122
|
+
subscribe()
|
123
|
+
@stream.write(data)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Connect to the websocket with authorization in query params
|
127
|
+
def connect
|
128
|
+
disconnect()
|
129
|
+
final_url = @url + "?scope=#{@scope}&authorization=#{@authentication.token}"
|
130
|
+
@stream = WebSocketClientStream.new(final_url, @write_timeout, @read_timeout, @connect_timeout)
|
131
|
+
@stream.headers = {
|
132
|
+
'Sec-WebSocket-Protocol' => 'actioncable-v1-json, actioncable-unsupported',
|
133
|
+
'User-Agent' => USER_AGENT
|
134
|
+
}
|
135
|
+
@stream.connect
|
136
|
+
end
|
137
|
+
|
138
|
+
# Are we connected?
|
139
|
+
def connected?
|
140
|
+
if @stream
|
141
|
+
@stream.connected?
|
142
|
+
else
|
143
|
+
false
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Disconnect from the websocket and attempt to send unsubscribe message
|
148
|
+
def disconnect
|
149
|
+
if connected?()
|
150
|
+
begin
|
151
|
+
unsubscribe()
|
152
|
+
rescue
|
153
|
+
# Oh well, we tried
|
154
|
+
end
|
155
|
+
@stream.disconnect
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# private
|
160
|
+
|
161
|
+
# Generate the appropriate token for OpenC3
|
162
|
+
def generate_auth
|
163
|
+
if ENV['OPENC3_API_TOKEN'].nil? and ENV['OPENC3_API_USER'].nil?
|
164
|
+
if ENV['OPENC3_API_PASSWORD'] || ENV['OPENC3_SERVICE_PASSWORD']
|
165
|
+
return OpenC3Authentication.new()
|
166
|
+
else
|
167
|
+
raise "Environment Variables Not Set for Authentication"
|
168
|
+
end
|
169
|
+
else
|
170
|
+
return OpenC3KeycloakAuthentication.new(ENV['OPENC3_KEYCLOAK_URL'])
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Base class for cmd-tlm-api websockets - Do not use directly
|
176
|
+
class CmdTlmWebSocketApi < WebSocketApi
|
177
|
+
def initialize(url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
|
178
|
+
url = generate_url() unless url
|
179
|
+
super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
|
180
|
+
end
|
181
|
+
|
182
|
+
def generate_url
|
183
|
+
schema = ENV['OPENC3_API_SCHEMA'] || 'http'
|
184
|
+
hostname = ENV['OPENC3_API_HOSTNAME'] || (ENV['OPENC3_DEVEL'] ? '127.0.0.1' : 'openc3-cosmos-cmd-tlm-api')
|
185
|
+
port = ENV['OPENC3_API_PORT'] || '2901'
|
186
|
+
port = port.to_i
|
187
|
+
return "#{schema}://#{hostname}:#{port}/openc3-api/cable"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Base class for script-runner-api websockets - Do not use directly
|
192
|
+
class ScriptWebSocketApi < WebSocketApi
|
193
|
+
def initialize(url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_token)
|
194
|
+
url = generate_url() unless url
|
195
|
+
super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
|
196
|
+
end
|
197
|
+
|
198
|
+
def generate_url
|
199
|
+
schema = ENV['OPENC3_SCRIPT_API_SCHEMA'] || 'http'
|
200
|
+
hostname = ENV['OPENC3_SCRIPT_API_HOSTNAME'] || (ENV['OPENC3_DEVEL'] ? '127.0.0.1' : 'openc3-cosmos-script-runner-api')
|
201
|
+
port = ENV['OPENC3_SCRIPT_API_PORT'] || '2902'
|
202
|
+
port = port.to_i
|
203
|
+
return "#{schema}://#{hostname}:#{port}/script-api/cable"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Running Script WebSocket
|
208
|
+
class RunningScriptWebSocketApi < ScriptWebSocketApi
|
209
|
+
def initialize(id:, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
|
210
|
+
@identifier = {
|
211
|
+
channel: "RunningScriptChannel",
|
212
|
+
id: id
|
213
|
+
}
|
214
|
+
super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Log Messages WebSocket
|
219
|
+
class MessagesWebSocketApi < CmdTlmWebSocketApi
|
220
|
+
def initialize(history_count: 0, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
|
221
|
+
@identifier = {
|
222
|
+
channel: "MessagesChannel",
|
223
|
+
history_count: history_count
|
224
|
+
}
|
225
|
+
super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# Notifications WebSocket
|
230
|
+
class NotificationsWebSocketApi < CmdTlmWebSocketApi
|
231
|
+
def initialize(history_count: 0, start_offset: nil, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
|
232
|
+
@identifier = {
|
233
|
+
channel: "NotificationsChannel",
|
234
|
+
history_count: history_count,
|
235
|
+
start_offset: start_offset
|
236
|
+
}
|
237
|
+
super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# Autonomic Events WebSocket
|
242
|
+
class AutonomicEventsWebSocketApi < CmdTlmWebSocketApi
|
243
|
+
def initialize(history_count: 0, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
|
244
|
+
@identifier = {
|
245
|
+
channel: "AutonomicEventsChannel",
|
246
|
+
history_count: history_count
|
247
|
+
}
|
248
|
+
super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# Calendar Events WebSocket
|
253
|
+
class CalendarEventsWebSocketApi < CmdTlmWebSocketApi
|
254
|
+
def initialize(history_count: 0, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
|
255
|
+
@identifier = {
|
256
|
+
channel: "CalendarEventsChannel",
|
257
|
+
history_count: history_count
|
258
|
+
}
|
259
|
+
super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# Config Events WebSocket
|
264
|
+
class ConfigEventsWebSocketApi < CmdTlmWebSocketApi
|
265
|
+
def initialize(history_count: 0, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
|
266
|
+
@identifier = {
|
267
|
+
channel: "ConfigEventsChannel",
|
268
|
+
history_count: history_count
|
269
|
+
}
|
270
|
+
super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# Limits Events WebSocket
|
275
|
+
class LimitsEventsWebSocketApi < CmdTlmWebSocketApi
|
276
|
+
def initialize(history_count: 0, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
|
277
|
+
@identifier = {
|
278
|
+
channel: "LimitsEventsChannel",
|
279
|
+
history_count: history_count
|
280
|
+
}
|
281
|
+
super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# Timeline WebSocket
|
286
|
+
class TimelineEventsWebSocketApi < CmdTlmWebSocketApi
|
287
|
+
def initialize(history_count: 0, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
|
288
|
+
@identifier = {
|
289
|
+
channel: "TimelineEventsChannel",
|
290
|
+
history_count: history_count
|
291
|
+
}
|
292
|
+
super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# Streaming API WebSocket
|
297
|
+
class StreamingWebSocketApi < CmdTlmWebSocketApi
|
298
|
+
def initialize(url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
|
299
|
+
@identifier = {
|
300
|
+
channel: "StreamingChannel"
|
301
|
+
}
|
302
|
+
super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
|
303
|
+
end
|
304
|
+
|
305
|
+
# Request to add data to the stream
|
306
|
+
#
|
307
|
+
# arguments:
|
308
|
+
# scope: scope name
|
309
|
+
# start_time: 64-bit nanoseconds from unix epoch - If not present then realtime
|
310
|
+
# end_time: 64-bit nanoseconds from unix epoch - If not present stream forever
|
311
|
+
# items: [ [ MODE__CMDORTLM__TARGET__PACKET__ITEM__VALUETYPE__REDUCEDTYPE, item_key] ]
|
312
|
+
# MODE - RAW, DECOM, REDUCED_MINUTE, REDUCED_HOUR, or REDUCED_DAY
|
313
|
+
# CMDORTLM - CMD or TLM
|
314
|
+
# TARGET - Target name
|
315
|
+
# PACKET - Packet name
|
316
|
+
# ITEM - Item Name
|
317
|
+
# VALUETYPE - RAW, CONVERTED, FORMATTED, or WITH_UNITS
|
318
|
+
# REDUCEDTYPE - MIN, MAX, AVG, STDDEV (only for reduced modes)
|
319
|
+
# item_key is an optional shortened name to return the data as
|
320
|
+
# packets: [ MODE__CMDORTLM__TARGET__PACKET__VALUETYPE ]
|
321
|
+
# MODE - RAW, DECOM, REDUCED_MINUTE, REDUCED_HOUR, or REDUCED_DAY
|
322
|
+
# CMDORTLM - CMD or TLM
|
323
|
+
# TARGET - Target name
|
324
|
+
# PACKET - Packet name
|
325
|
+
# VALUETYPE - RAW, CONVERTED, FORMATTED, WITH_UNITS, or PURE (pure means all types as stored in log)
|
326
|
+
#
|
327
|
+
def add(items: nil, packets: nil, start_time: nil, end_time: nil, scope: $openc3_scope)
|
328
|
+
data_hash = {}
|
329
|
+
data_hash['action'] = 'add'
|
330
|
+
if start_time
|
331
|
+
if Time === start_time
|
332
|
+
start_time = start_time.to_nsec_from_epoch
|
333
|
+
end
|
334
|
+
data_hash['start_time'] = start_time
|
335
|
+
end
|
336
|
+
if end_time
|
337
|
+
if Time === end_time
|
338
|
+
end_time = end_time.to_nsec_from_epoch
|
339
|
+
end
|
340
|
+
data_hash['end_time'] = end_time
|
341
|
+
end
|
342
|
+
data_hash['items'] = items if items
|
343
|
+
data_hash['packets'] = packets if packets
|
344
|
+
data_hash['scope'] = scope
|
345
|
+
data_hash['token'] = @authentication.token
|
346
|
+
write_action(data_hash)
|
347
|
+
end
|
348
|
+
|
349
|
+
# Request to remove data from the stream
|
350
|
+
#
|
351
|
+
# arguments:
|
352
|
+
# scope: scope name
|
353
|
+
# items: [ [ MODE__CMDORTLM__TARGET__PACKET__ITEM__VALUETYPE__REDUCEDTYPE] ]
|
354
|
+
# MODE - RAW, DECOM, REDUCED_MINUTE, REDUCED_HOUR, or REDUCED_DAY
|
355
|
+
# CMDORTLM - CMD or TLM
|
356
|
+
# TARGET - Target name
|
357
|
+
# PACKET - Packet name
|
358
|
+
# ITEM - Item Name
|
359
|
+
# VALUETYPE - RAW, CONVERTED, FORMATTED, or WITH_UNITS
|
360
|
+
# REDUCEDTYPE - MIN, MAX, AVG, STDDEV (only for reduced modes)
|
361
|
+
# packets: [ MODE__CMDORTLM__TARGET__PACKET__VALUETYPE ]
|
362
|
+
# MODE - RAW, DECOM, REDUCED_MINUTE, REDUCED_HOUR, or REDUCED_DAY
|
363
|
+
# CMDORTLM - CMD or TLM
|
364
|
+
# TARGET - Target name
|
365
|
+
# PACKET - Packet name
|
366
|
+
# VALUETYPE - RAW, CONVERTED, FORMATTED, WITH_UNITS, or PURE (pure means all types as stored in log)
|
367
|
+
#
|
368
|
+
def remove(items: nil, packets: nil, scope: $openc3_scope)
|
369
|
+
data_hash = {}
|
370
|
+
data_hash['action'] = 'remove'
|
371
|
+
data_hash['items'] = items if items
|
372
|
+
data_hash['packets'] = packets if packets
|
373
|
+
data_hash['scope'] = scope
|
374
|
+
data_hash['token'] = @authentication.token
|
375
|
+
write_action(data_hash)
|
376
|
+
end
|
377
|
+
|
378
|
+
# Convenience method to read all data until end marker is received.
|
379
|
+
# Warning: DATA IS STORED IN RAM. Do not use this with large queries
|
380
|
+
def self.read_all(items: nil, packets: nil, start_time: nil, end_time:, scope: $openc3_scope, timeout: nil)
|
381
|
+
read_all_start_time = Time.now
|
382
|
+
data = []
|
383
|
+
self.new do |api|
|
384
|
+
api.add(items: items, packets: packets, start_time: start_time, end_time: end_time, scope: scope)
|
385
|
+
while true
|
386
|
+
batch = api.read
|
387
|
+
if batch.length == 0
|
388
|
+
return data
|
389
|
+
else
|
390
|
+
data.concat(batch)
|
391
|
+
end
|
392
|
+
if timeout
|
393
|
+
if (Time.now - read_all_start_time) > timeout
|
394
|
+
return data
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
# # Example Use
|
404
|
+
# $openc3_scope = 'DEFAULT'
|
405
|
+
# ENV['OPENC3_API_HOSTNAME'] = 'localhost'
|
406
|
+
# ENV['OPENC3_API_PORT'] = '2900'
|
407
|
+
# ENV['OPENC3_SCRIPT_API_HOSTNAME'] = 'localhost'
|
408
|
+
# ENV['OPENC3_SCRIPT_API_PORT'] = '2900'
|
409
|
+
# ENV['OPENC3_API_PASSWORD'] = 'password'
|
410
|
+
#
|
411
|
+
# OpenC3::StreamingWebSocketApi.new do |api|
|
412
|
+
# api.add(items: ['DECOM__TLM__INST__HEALTH_STATUS__TEMP1__CONVERTED', 'DECOM__TLM__INST__HEALTH_STATUS__TEMP2__CONVERTED'])
|
413
|
+
# 5.times do
|
414
|
+
# puts api.read
|
415
|
+
# end
|
416
|
+
# api.remove(items: ['DECOM__TLM__INST__HEALTH_STATUS__TEMP1__CONVERTED'])
|
417
|
+
# 5.times do
|
418
|
+
# puts api.read
|
419
|
+
# end
|
420
|
+
# end
|
421
|
+
#
|
422
|
+
# # Warning this saves all data to RAM. Do not use for large queries
|
423
|
+
# data = OpenC3::StreamingWebSocketApi.read_all(items: ['DECOM__TLM__INST__HEALTH_STATUS__TEMP1__CONVERTED', 'DECOM__TLM__INST__HEALTH_STATUS__TEMP2__CONVERTED'], end_time: Time.now + 30)
|
@@ -17,7 +17,7 @@
|
|
17
17
|
# All changes Copyright 2022, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
|
-
# This file may also be used under the terms of a commercial license
|
20
|
+
# This file may also be used under the terms of a commercial license
|
21
21
|
# if purchased from OpenC3, Inc.
|
22
22
|
|
23
23
|
require 'socket'
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2023 OpenC3, Inc.
|
4
|
+
# All Rights Reserved.
|
5
|
+
#
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
7
|
+
# under the terms of the GNU Affero General Public License
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# This file may also be used under the terms of a commercial license
|
17
|
+
# if purchased from OpenC3, Inc.
|
18
|
+
|
19
|
+
require 'openssl'
|
20
|
+
require 'openc3/streams/tcpip_client_stream'
|
21
|
+
require 'websocket'
|
22
|
+
require 'uri'
|
23
|
+
|
24
|
+
module OpenC3
|
25
|
+
class WebSocketClientStream < TcpipClientStream
|
26
|
+
attr_accessor :headers
|
27
|
+
|
28
|
+
# @param url [String] The host to connect to
|
29
|
+
# @param write_timeout (see TcpipSocketStream#initialize)
|
30
|
+
# @param read_timeout (see TcpipSocketStream#initialize)
|
31
|
+
# @param connect_timeout (see TcpipClientStream#initialize)
|
32
|
+
def initialize(url, write_timeout, read_timeout, connect_timeout = 5.0)
|
33
|
+
@url = url
|
34
|
+
@uri = URI.parse @url
|
35
|
+
port = ((@uri.scheme == 'wss' or @uri.scheme == 'https') ? 443 : 80)
|
36
|
+
port = @uri.port if @uri.port
|
37
|
+
super(@uri.host, port, port, write_timeout, read_timeout, connect_timeout)
|
38
|
+
if ['https', 'wss'].include? @uri.scheme
|
39
|
+
socket = ::OpenSSL::SSL::SSLSocket.new(@write_socket)
|
40
|
+
socket.sync_close = true
|
41
|
+
socket.hostname = @uri.host
|
42
|
+
@write_socket = socket
|
43
|
+
@read_socket = socket
|
44
|
+
end
|
45
|
+
@headers = {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def connect
|
49
|
+
super()
|
50
|
+
@handshake = ::WebSocket::Handshake::Client.new(:url => @url, :headers => @headers)
|
51
|
+
@frame = ::WebSocket::Frame::Incoming::Client.new
|
52
|
+
@handshaked = false
|
53
|
+
@write_socket.write(@handshake.to_s)
|
54
|
+
start_time = Time.now
|
55
|
+
read() # This should wait for the handshake
|
56
|
+
return true
|
57
|
+
end
|
58
|
+
|
59
|
+
def read
|
60
|
+
while true
|
61
|
+
if @handshaked
|
62
|
+
msg = @frame.next
|
63
|
+
return msg.data if msg
|
64
|
+
end
|
65
|
+
|
66
|
+
data = super()
|
67
|
+
return data if data.length <= 0
|
68
|
+
|
69
|
+
if @handshaked
|
70
|
+
@frame << data
|
71
|
+
msg = @frame.next
|
72
|
+
return msg.data if msg
|
73
|
+
else
|
74
|
+
index = 0
|
75
|
+
chars = ""
|
76
|
+
data.each_char do |char|
|
77
|
+
@handshake << char
|
78
|
+
chars << char
|
79
|
+
index += 1
|
80
|
+
if @handshake.finished?
|
81
|
+
@handshaked = true
|
82
|
+
break
|
83
|
+
end
|
84
|
+
end
|
85
|
+
if @handshaked
|
86
|
+
data = data[index..-1]
|
87
|
+
@frame << data
|
88
|
+
return
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def write(data, type: :text)
|
95
|
+
frame = ::WebSocket::Frame::Outgoing::Client.new(:data => data, :type => type, :version => @handshake.version)
|
96
|
+
super(frame.to_s)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/openc3/version.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
-
OPENC3_VERSION = '5.
|
3
|
+
OPENC3_VERSION = '5.5.0-beta0'
|
4
4
|
module OpenC3
|
5
5
|
module Version
|
6
6
|
MAJOR = '5'
|
7
|
-
MINOR = '
|
8
|
-
PATCH = '
|
7
|
+
MINOR = '5'
|
8
|
+
PATCH = '0'
|
9
9
|
OTHER = 'pre.beta0'
|
10
|
-
BUILD = '
|
10
|
+
BUILD = 'abab088dbe97761fca351752b3396f1c05dd852b'
|
11
11
|
end
|
12
|
-
VERSION = '5.
|
13
|
-
GEM_VERSION = '5.
|
12
|
+
VERSION = '5.5.0-beta0'
|
13
|
+
GEM_VERSION = '5.5.0.pre.beta0'
|
14
14
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openc3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.5.0.pre.beta0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Melton
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-02-
|
12
|
+
date: 2023-02-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -445,6 +445,34 @@ dependencies:
|
|
445
445
|
- - "~>"
|
446
446
|
- !ruby/object:Gem::Version
|
447
447
|
version: '0.3'
|
448
|
+
- !ruby/object:Gem::Dependency
|
449
|
+
name: websocket
|
450
|
+
requirement: !ruby/object:Gem::Requirement
|
451
|
+
requirements:
|
452
|
+
- - ">="
|
453
|
+
- !ruby/object:Gem::Version
|
454
|
+
version: '0'
|
455
|
+
type: :runtime
|
456
|
+
prerelease: false
|
457
|
+
version_requirements: !ruby/object:Gem::Requirement
|
458
|
+
requirements:
|
459
|
+
- - ">="
|
460
|
+
- !ruby/object:Gem::Version
|
461
|
+
version: '0'
|
462
|
+
- !ruby/object:Gem::Dependency
|
463
|
+
name: websocket-native
|
464
|
+
requirement: !ruby/object:Gem::Requirement
|
465
|
+
requirements:
|
466
|
+
- - ">="
|
467
|
+
- !ruby/object:Gem::Version
|
468
|
+
version: '0'
|
469
|
+
type: :runtime
|
470
|
+
prerelease: false
|
471
|
+
version_requirements: !ruby/object:Gem::Requirement
|
472
|
+
requirements:
|
473
|
+
- - ">="
|
474
|
+
- !ruby/object:Gem::Version
|
475
|
+
version: '0'
|
448
476
|
- !ruby/object:Gem::Dependency
|
449
477
|
name: dead_end
|
450
478
|
requirement: !ruby/object:Gem::Requirement
|
@@ -916,10 +944,12 @@ files:
|
|
916
944
|
- lib/openc3/script/suite_results.rb
|
917
945
|
- lib/openc3/script/suite_runner.rb
|
918
946
|
- lib/openc3/script/telemetry.rb
|
947
|
+
- lib/openc3/script/web_socket_api.rb
|
919
948
|
- lib/openc3/streams/serial_stream.rb
|
920
949
|
- lib/openc3/streams/stream.rb
|
921
950
|
- lib/openc3/streams/tcpip_client_stream.rb
|
922
951
|
- lib/openc3/streams/tcpip_socket_stream.rb
|
952
|
+
- lib/openc3/streams/web_socket_client_stream.rb
|
923
953
|
- lib/openc3/system.rb
|
924
954
|
- lib/openc3/system/system.rb
|
925
955
|
- lib/openc3/system/system_config.rb
|