openc3 5.4.2 → 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 +4 -4
- data/Gemfile +5 -10
- data/bin/openc3cli +8 -1
- data/data/config/_interfaces.yaml +6 -71
- data/data/config/interface_modifiers.yaml +0 -23
- data/data/config/microservice.yaml +8 -0
- data/data/config/target.yaml +1 -0
- data/data/config/widgets.yaml +6 -23
- data/lib/openc3/core_ext/string.rb +2 -2
- data/lib/openc3/io/json_rpc.rb +6 -2
- data/lib/openc3/microservices/plugin_microservice.rb +10 -1
- data/lib/openc3/models/cvt_model.rb +10 -8
- data/lib/openc3/models/metadata_model.rb +1 -1
- data/lib/openc3/models/microservice_model.rb +14 -2
- data/lib/openc3/models/model.rb +3 -11
- data/lib/openc3/models/note_model.rb +1 -1
- data/lib/openc3/models/timeline_model.rb +2 -3
- data/lib/openc3/models/traefik_model.rb +47 -0
- data/lib/openc3/models/trigger_group_model.rb +2 -3
- data/lib/openc3/script/script.rb +10 -0
- data/lib/openc3/script/storage.rb +4 -3
- data/lib/openc3/script/web_socket_api.rb +423 -0
- data/lib/openc3/streams/tcpip_client_stream.rb +1 -1
- data/lib/openc3/streams/web_socket_client_stream.rb +99 -0
- data/lib/openc3/top_level.rb +1 -16
- data/lib/openc3/utilities/authorization.rb +4 -2
- data/lib/openc3/utilities/aws_bucket.rb +4 -0
- data/lib/openc3/utilities/bucket.rb +4 -0
- data/lib/openc3/utilities/process_manager.rb +6 -5
- data/lib/openc3/version.rb +7 -7
- data/lib/openc3.rb +3 -0
- metadata +35 -6
- data/data/config/cmd_tlm_server.yaml +0 -136
- data/lib/openc3/tools/cmd_tlm_server/cmd_tlm_server_config.rb +0 -323
@@ -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/top_level.rb
CHANGED
@@ -461,22 +461,7 @@ module OpenC3
|
|
461
461
|
|
462
462
|
# @param filename [String] Name of the file to open in the web browser
|
463
463
|
def self.open_in_web_browser(filename)
|
464
|
-
|
465
|
-
if Kernel.is_windows?
|
466
|
-
self.run_process("cmd /c \"start \"\" \"#{filename.gsub('/', '\\')}\"\"")
|
467
|
-
elsif Kernel.is_mac?
|
468
|
-
self.run_process("open -a Safari \"#{filename}\"")
|
469
|
-
else
|
470
|
-
which_firefox = `which firefox`.chomp
|
471
|
-
if which_firefox =~ /Command not found/i or which_firefox =~ /no .* in/i
|
472
|
-
raise "Firefox not found"
|
473
|
-
else
|
474
|
-
system_call = "#{which_firefox} \"#{filename}\""
|
475
|
-
end
|
476
|
-
|
477
|
-
self.run_process(system_call)
|
478
|
-
end
|
479
|
-
end
|
464
|
+
puts "open_in_web_browser is DEPRECATED"
|
480
465
|
end
|
481
466
|
|
482
467
|
# Temporarily set the working directory during a block
|
@@ -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 'openc3/models/auth_model'
|
@@ -42,7 +42,9 @@ rescue LoadError
|
|
42
42
|
|
43
43
|
if $openc3_authorize
|
44
44
|
raise AuthError.new("Token is required") unless token
|
45
|
-
|
45
|
+
unless OpenC3::AuthModel.verify(token, permission: permission)
|
46
|
+
raise AuthError.new("Current role is invalid for '#{permission}' permission")
|
47
|
+
end
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
@@ -119,6 +119,8 @@ module OpenC3
|
|
119
119
|
end
|
120
120
|
# Array of objects with key and size methods
|
121
121
|
result
|
122
|
+
rescue Aws::S3::Errors::NoSuchBucket => error
|
123
|
+
raise NotFound, "Bucket '#{bucket}' does not exist."
|
122
124
|
end
|
123
125
|
|
124
126
|
# Lists the files under a specified path
|
@@ -164,6 +166,8 @@ module OpenC3
|
|
164
166
|
token = resp.next_continuation_token
|
165
167
|
end
|
166
168
|
result
|
169
|
+
rescue Aws::S3::Errors::NoSuchBucket => error
|
170
|
+
raise NotFound, "Bucket '#{bucket}' does not exist."
|
167
171
|
end
|
168
172
|
|
169
173
|
# put_object fires off the request to store but does not confirm
|
@@ -21,6 +21,10 @@ ENV['OPENC3_CLOUD'] ||= 'local'
|
|
21
21
|
# Interface class implemented by each cloud provider: AWS, GCS, Azure
|
22
22
|
module OpenC3
|
23
23
|
class Bucket
|
24
|
+
# Raised when the underlying bucket does not exist
|
25
|
+
class NotFound < RuntimeError
|
26
|
+
end
|
27
|
+
|
24
28
|
def self.getClient
|
25
29
|
raise 'OPENC3_CLOUD environment variable is required' unless ENV['OPENC3_CLOUD']
|
26
30
|
# Base is AwsBucket which works with MINIO, Enterprise implements additional
|
@@ -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 'openc3/operators/operator'
|
@@ -52,7 +52,7 @@ module OpenC3
|
|
52
52
|
|
53
53
|
# Spawns short lived processes and ensures they complete
|
54
54
|
class ProcessManager
|
55
|
-
MONITOR_CYCLE_SECONDS =
|
55
|
+
MONITOR_CYCLE_SECONDS = 5
|
56
56
|
CLEANUP_CYCLE_SECONDS = 600
|
57
57
|
|
58
58
|
@@instance = nil
|
@@ -68,6 +68,7 @@ module OpenC3
|
|
68
68
|
begin
|
69
69
|
monitor()
|
70
70
|
rescue => err
|
71
|
+
Logger.error("ProcessManager unexpectedly died\n#{err.formatted}", scope: 'DEFAULT')
|
71
72
|
raise "ProcessManager unexpectedly died\n#{err.formatted}"
|
72
73
|
end
|
73
74
|
end
|
@@ -111,10 +112,10 @@ module OpenC3
|
|
111
112
|
end
|
112
113
|
processes_to_delete.each do |process|
|
113
114
|
if process.status.state == "Complete"
|
114
|
-
Logger.info
|
115
|
+
Logger.info("Process #{process.status.name}:#{process.process_type}:#{process.detail} completed with state #{process.status.state}", scope: process.scope)
|
115
116
|
else
|
116
|
-
Logger.error
|
117
|
-
Logger.error
|
117
|
+
Logger.error("Process #{process.status.name}:#{process.process_type}:#{process.detail} completed with state #{process.status.state}", scope: process.scope)
|
118
|
+
Logger.error("Process Output:\n#{process.status.output}", scope: process.scope)
|
118
119
|
end
|
119
120
|
|
120
121
|
@processes.delete(process)
|
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 = '
|
9
|
-
OTHER = ''
|
10
|
-
BUILD = '
|
7
|
+
MINOR = '5'
|
8
|
+
PATCH = '0'
|
9
|
+
OTHER = 'pre.beta0'
|
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
|
data/lib/openc3.rb
CHANGED