openc3 5.7.0 → 5.8.0
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 +1 -1
- data/data/config/widgets.yaml +6 -6
- data/lib/openc3/api/cmd_api.rb +29 -0
- data/lib/openc3/api/limits_api.rb +31 -1
- data/lib/openc3/api/tlm_api.rb +5 -7
- data/lib/openc3/core_ext/faraday.rb +8 -0
- data/lib/openc3/core_ext.rb +1 -1
- data/lib/openc3/io/json_api_object.rb +24 -12
- data/lib/openc3/io/json_drb_object.rb +3 -7
- data/lib/openc3/logs/buffered_packet_log_writer.rb +20 -12
- data/lib/openc3/logs/log_writer.rb +12 -7
- data/lib/openc3/logs/packet_log_writer.rb +9 -6
- data/lib/openc3/microservices/decom_microservice.rb +4 -0
- data/lib/openc3/microservices/interface_decom_common.rb +32 -0
- data/lib/openc3/microservices/reducer_microservice.rb +15 -11
- data/lib/openc3/models/cvt_model.rb +10 -5
- data/lib/openc3/models/gem_model.rb +0 -1
- data/lib/openc3/models/scope_model.rb +0 -3
- data/lib/openc3/models/target_model.rb +7 -7
- data/lib/openc3/models/timeline_model.rb +0 -1
- data/lib/openc3/models/trigger_group_model.rb +0 -1
- data/lib/openc3/script/metadata.rb +7 -7
- data/lib/openc3/script/screen.rb +5 -5
- data/lib/openc3/script/script_runner.rb +17 -17
- data/lib/openc3/script/storage.rb +2 -2
- data/lib/openc3/topics/config_topic.rb +1 -6
- data/lib/openc3/topics/decom_interface_topic.rb +62 -0
- data/lib/openc3/topics/telemetry_decom_topic.rb +0 -9
- data/lib/openc3/topics/topic.rb +4 -11
- data/lib/openc3/utilities/authentication.rb +4 -4
- data/lib/openc3/utilities/bucket_require.rb +65 -0
- data/lib/openc3/utilities/bucket_utilities.rb +38 -5
- data/lib/openc3/utilities/open_telemetry.rb +4 -4
- data/lib/openc3/utilities/process_manager.rb +4 -2
- data/lib/openc3/utilities/ruby_lex_utils.rb +62 -36
- data/lib/openc3/utilities/store_autoload.rb +7 -28
- data/lib/openc3/version.rb +5 -5
- data/lib/openc3.rb +1 -1
- data/templates/widget/package.json +7 -7
- data/templates/widget/yarn.lock +46 -47
- metadata +55 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ff64c4ea935a504f12164ff90107fbe35e10b31f0628ca21a8be8f411711e8f
|
4
|
+
data.tar.gz: 243be1e6c1e5f61cfc7789bcb6319db6cae3cd733e186ae33a1e8de08fa3a6e1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a379ce7d179aff5f45edd1413d7d10a19a49034685affda022e7cf1ad45285bcd496a522c3aa786f9325d591e9c5ff65201f5ba9486ffae8743c0c2b1b52052a
|
7
|
+
data.tar.gz: 991a47c1deb6d78415bf3e77d1569a8d6279f629d8ac8169491ebfd4dbd85652e422747d28d52fef49b1db02b04c6c2cc5164aa291a2946be4f6c4daccb67352
|
data/Gemfile
CHANGED
@@ -9,7 +9,7 @@ gemspec :name => 'openc3'
|
|
9
9
|
# Include the rails gems for the convenience of custom microservice plugins
|
10
10
|
gem 'rails', '~> 7.0.0'
|
11
11
|
gem 'bootsnap', '>= 1.9.3', require: false
|
12
|
-
gem 'rack-cors', '~>
|
12
|
+
gem 'rack-cors', '~> 2.0'
|
13
13
|
gem 'tzinfo-data'
|
14
14
|
gem 'rspec-rails', '~> 6.0'
|
15
15
|
gem 'simplecov', '~> 0.20'
|
data/data/config/widgets.yaml
CHANGED
@@ -12,7 +12,7 @@ Layout Widgets:
|
|
12
12
|
parameters:
|
13
13
|
- name: Margin
|
14
14
|
required: false
|
15
|
-
description: Margin between widgets (default =
|
15
|
+
description: Margin between widgets (default = 0px)
|
16
16
|
values: .*
|
17
17
|
example: |
|
18
18
|
VERTICAL 5px
|
@@ -30,7 +30,7 @@ Layout Widgets:
|
|
30
30
|
values: .*
|
31
31
|
- name: Margin
|
32
32
|
required: false
|
33
|
-
description: Margin between widgets (default =
|
33
|
+
description: Margin between widgets (default = 0px)
|
34
34
|
values: .*
|
35
35
|
example: |
|
36
36
|
VERTICALBOX Info
|
@@ -43,7 +43,7 @@ Layout Widgets:
|
|
43
43
|
parameters:
|
44
44
|
- name: Margin
|
45
45
|
required: false
|
46
|
-
description: Margin between widgets (default =
|
46
|
+
description: Margin between widgets (default = 0px)
|
47
47
|
values: .*
|
48
48
|
example: |
|
49
49
|
HORIZONTAL 100
|
@@ -60,7 +60,7 @@ Layout Widgets:
|
|
60
60
|
values: .*
|
61
61
|
- name: Margin
|
62
62
|
required: false
|
63
|
-
description: Margin between widgets (default =
|
63
|
+
description: Margin between widgets (default = 0px)
|
64
64
|
values: .*
|
65
65
|
example: |
|
66
66
|
HORIZONTALBOX Info 10
|
@@ -77,7 +77,7 @@ Layout Widgets:
|
|
77
77
|
values: .*
|
78
78
|
- name: Margin
|
79
79
|
required: false
|
80
|
-
description: Margin between widgets (default =
|
80
|
+
description: Margin between widgets (default = 0px)
|
81
81
|
values: .*
|
82
82
|
example: |
|
83
83
|
MATRIXBYCOLUMNS 3 10
|
@@ -98,7 +98,7 @@ Layout Widgets:
|
|
98
98
|
values: .*
|
99
99
|
- name: Margin
|
100
100
|
required: false
|
101
|
-
description: Margin between widgets (default =
|
101
|
+
description: Margin between widgets (default = 0px)
|
102
102
|
values: .*
|
103
103
|
example: |
|
104
104
|
SCROLLWINDOW 100 10
|
data/lib/openc3/api/cmd_api.rb
CHANGED
@@ -23,6 +23,7 @@
|
|
23
23
|
require 'openc3/models/target_model'
|
24
24
|
require 'openc3/topics/command_topic'
|
25
25
|
require 'openc3/topics/command_decom_topic'
|
26
|
+
require 'openc3/topics/decom_interface_topic'
|
26
27
|
require 'openc3/topics/interface_topic'
|
27
28
|
require 'openc3/script/extract'
|
28
29
|
|
@@ -38,6 +39,7 @@ module OpenC3
|
|
38
39
|
'cmd_raw_no_range_check',
|
39
40
|
'cmd_raw_no_hazardous_check',
|
40
41
|
'cmd_raw_no_checks',
|
42
|
+
'build_command',
|
41
43
|
'send_raw',
|
42
44
|
'get_all_commands',
|
43
45
|
'get_all_command_names',
|
@@ -95,6 +97,33 @@ module OpenC3
|
|
95
97
|
cmd_implementation('cmd_raw_no_checks', *args, range_check: false, hazardous_check: false, raw: true, **kwargs)
|
96
98
|
end
|
97
99
|
|
100
|
+
# Build a command binary
|
101
|
+
#
|
102
|
+
# @since 5.8.0
|
103
|
+
def build_command(*args, range_check: true, raw: false, scope: $openc3_scope, token: $openc3_token, **kwargs)
|
104
|
+
extract_string_kwargs_to_args(args, kwargs)
|
105
|
+
case args.length
|
106
|
+
when 1
|
107
|
+
target_name, cmd_name, cmd_params = extract_fields_from_cmd_text(args[0], scope: scope)
|
108
|
+
when 2, 3
|
109
|
+
target_name = args[0]
|
110
|
+
cmd_name = args[1]
|
111
|
+
if args.length == 2
|
112
|
+
cmd_params = {}
|
113
|
+
else
|
114
|
+
cmd_params = args[2]
|
115
|
+
end
|
116
|
+
else
|
117
|
+
# Invalid number of arguments
|
118
|
+
raise "ERROR: Invalid number of arguments (#{args.length}) passed to build_command()"
|
119
|
+
end
|
120
|
+
target_name = target_name.upcase
|
121
|
+
cmd_name = cmd_name.upcase
|
122
|
+
cmd_params = cmd_params.transform_keys(&:upcase)
|
123
|
+
authorize(permission: 'cmd_info', target_name: target_name, scope: scope, token: token)
|
124
|
+
DecomInterfaceTopic.build_cmd(target_name, cmd_name, cmd_params, range_check, raw, scope: scope)
|
125
|
+
end
|
126
|
+
|
98
127
|
# Send a raw binary string to the specified interface.
|
99
128
|
#
|
100
129
|
# @param interface_name [String] The interface to send the raw binary
|
@@ -21,6 +21,7 @@
|
|
21
21
|
# if purchased from OpenC3, Inc.
|
22
22
|
|
23
23
|
require 'openc3/api/target_api'
|
24
|
+
require 'openc3/models/cvt_model'
|
24
25
|
|
25
26
|
module OpenC3
|
26
27
|
module Api
|
@@ -191,7 +192,7 @@ module OpenC3
|
|
191
192
|
def get_limits(target_name, packet_name, item_name, scope: $openc3_scope, token: $openc3_token)
|
192
193
|
authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
|
193
194
|
limits = {}
|
194
|
-
item =
|
195
|
+
item = _get_item(target_name, packet_name, item_name, scope: scope)
|
195
196
|
item['limits'].each do |key, vals|
|
196
197
|
next unless vals.is_a?(Hash)
|
197
198
|
|
@@ -364,5 +365,34 @@ module OpenC3
|
|
364
365
|
TargetModel.set_packet(last_target_name, last_packet_name, packet, scope: scope)
|
365
366
|
end
|
366
367
|
end
|
368
|
+
|
369
|
+
# Gets an item. The code below is mostly duplicated from tlm_process_args in tlm_api.rb.
|
370
|
+
#
|
371
|
+
# @param target_name [String] target name
|
372
|
+
# @param packet_name [String] packet name
|
373
|
+
# @param item_name [String] item name
|
374
|
+
# @param scope [String] scope
|
375
|
+
# @return Hash The requested item based on the packet name
|
376
|
+
def _get_item(target_name, packet_name, item_name, scope:)
|
377
|
+
requested_item = nil
|
378
|
+
if packet_name == 'LATEST'
|
379
|
+
latest = -1
|
380
|
+
TargetModel.packets(target_name, scope: scope).each do |packet|
|
381
|
+
item = packet['items'].find { |item| item['name'] == item_name }
|
382
|
+
if item
|
383
|
+
hash = CvtModel.get(target_name: target_name, packet_name: packet['packet_name'], scope: scope)
|
384
|
+
if hash['PACKET_TIMESECONDS'] && hash['PACKET_TIMESECONDS'] > latest
|
385
|
+
latest = hash['PACKET_TIMESECONDS']
|
386
|
+
requested_item = item
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
raise "Item '#{target_name} LATEST #{item_name}' does not exist" if latest == -1
|
391
|
+
else
|
392
|
+
# Determine if this item exists, it will raise appropriate errors if not
|
393
|
+
requested_item = TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
|
394
|
+
end
|
395
|
+
return requested_item
|
396
|
+
end
|
367
397
|
end
|
368
398
|
end
|
data/lib/openc3/api/tlm_api.rb
CHANGED
@@ -25,7 +25,7 @@ require 'openc3/models/cvt_model'
|
|
25
25
|
require 'openc3/packets/packet'
|
26
26
|
require 'openc3/topics/telemetry_topic'
|
27
27
|
require 'openc3/topics/interface_topic'
|
28
|
-
require 'openc3/topics/
|
28
|
+
require 'openc3/topics/decom_interface_topic'
|
29
29
|
|
30
30
|
module OpenC3
|
31
31
|
module Api
|
@@ -150,7 +150,7 @@ module OpenC3
|
|
150
150
|
if interface_name
|
151
151
|
InterfaceTopic.inject_tlm(interface_name, target_name, packet_name, item_hash, type: type, scope: scope)
|
152
152
|
else
|
153
|
-
|
153
|
+
DecomInterfaceTopic.inject_tlm(target_name, packet_name, item_hash, type: type, scope: scope)
|
154
154
|
end
|
155
155
|
end
|
156
156
|
|
@@ -449,12 +449,10 @@ module OpenC3
|
|
449
449
|
TargetModel.packets(target_name, scope: scope).each do |packet|
|
450
450
|
item = packet['items'].find { |item| item['name'] == item_name }
|
451
451
|
if item
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
if msg_hash && msg_hash['time'] && msg_hash['time'].to_i > latest
|
452
|
+
hash = CvtModel.get(target_name: target_name, packet_name: packet['packet_name'], scope: scope)
|
453
|
+
if hash['PACKET_TIMESECONDS'] && hash['PACKET_TIMESECONDS'] > latest
|
454
|
+
latest = hash['PACKET_TIMESECONDS']
|
456
455
|
packet_name = packet['packet_name']
|
457
|
-
latest = msg_hash['time'].to_i
|
458
456
|
end
|
459
457
|
end
|
460
458
|
end
|
data/lib/openc3/core_ext.rb
CHANGED
@@ -23,9 +23,9 @@
|
|
23
23
|
require 'openc3/core_ext/array'
|
24
24
|
require 'openc3/core_ext/binding'
|
25
25
|
require 'openc3/core_ext/class'
|
26
|
-
require 'openc3/core_ext/httpclient'
|
27
26
|
require 'openc3/core_ext/openc3_io'
|
28
27
|
require 'openc3/core_ext/exception'
|
28
|
+
require 'openc3/core_ext/faraday'
|
29
29
|
require 'openc3/core_ext/file'
|
30
30
|
require 'openc3/core_ext/hash'
|
31
31
|
require 'openc3/core_ext/io'
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
|
16
16
|
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright
|
17
|
+
# All changes Copyright 2023, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -30,7 +30,7 @@ require 'json'
|
|
30
30
|
# require 'drb/acl'
|
31
31
|
require 'drb/drb'
|
32
32
|
require 'uri'
|
33
|
-
require '
|
33
|
+
require 'faraday'
|
34
34
|
|
35
35
|
|
36
36
|
module OpenC3
|
@@ -101,7 +101,7 @@ module OpenC3
|
|
101
101
|
|
102
102
|
# Disconnects from http server
|
103
103
|
def disconnect
|
104
|
-
@http.
|
104
|
+
@http.close if @http
|
105
105
|
@http = nil
|
106
106
|
end
|
107
107
|
|
@@ -115,9 +115,14 @@ module OpenC3
|
|
115
115
|
|
116
116
|
def connect
|
117
117
|
begin
|
118
|
-
|
119
|
-
|
120
|
-
|
118
|
+
# Per https://github.com/lostisland/faraday/blob/main/lib/faraday/options/env.rb
|
119
|
+
# :timeout - time limit for the entire request (Integer in seconds)
|
120
|
+
# :open_timeout - time limit for just the connection phase (e.g. handshake) (Integer in seconds)
|
121
|
+
# :read_timeout - time limit for the first response byte received from the server (Integer in seconds)
|
122
|
+
# :write_timeout - time limit for the client to send the request to the server (Integer in seconds)
|
123
|
+
@http = Faraday.new(request: { open_timeout: @timeout.to_i, read_timeout: nil }) do |f|
|
124
|
+
f.adapter :net_http # adds the adapter to the connection, defaults to `Faraday.default_adapter`
|
125
|
+
end
|
121
126
|
rescue => e
|
122
127
|
raise JsonApiError, e.message
|
123
128
|
end
|
@@ -216,17 +221,24 @@ module OpenC3
|
|
216
221
|
def _http_request(method:, uri:, kwargs:)
|
217
222
|
case method
|
218
223
|
when 'get', :get
|
219
|
-
return @http.get(uri,
|
224
|
+
return @http.get(uri, kwargs[:query], kwargs[:headers])
|
220
225
|
when 'post', :post
|
221
|
-
return @http.post(uri
|
226
|
+
return @http.post(uri) do |req|
|
227
|
+
req.params = kwargs[:query]
|
228
|
+
req.headers = kwargs[:headers]
|
229
|
+
req.body = kwargs[:data]
|
230
|
+
end
|
222
231
|
when 'put', :put
|
223
|
-
return @http.put(uri
|
232
|
+
return @http.put(uri) do |req|
|
233
|
+
req.params = kwargs[:query]
|
234
|
+
req.headers = kwargs[:headers]
|
235
|
+
req.body = kwargs[:data]
|
236
|
+
end
|
224
237
|
when 'delete', :delete
|
225
|
-
return @http.delete(uri,
|
238
|
+
return @http.delete(uri, kwargs[:query], kwargs[:headers])
|
226
239
|
else
|
227
240
|
raise JsonApiError, "no method found: '#{method}'"
|
228
241
|
end
|
229
242
|
end
|
230
|
-
|
231
|
-
end # class JsonApiObject
|
243
|
+
end
|
232
244
|
end
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
|
16
16
|
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright
|
17
|
+
# All changes Copyright 2023, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -24,7 +24,6 @@ require 'openc3'
|
|
24
24
|
require 'openc3/io/json_api_object'
|
25
25
|
|
26
26
|
module OpenC3
|
27
|
-
|
28
27
|
class JsonDRbError < JsonApiError; end
|
29
28
|
|
30
29
|
# Used to forward all method calls to the remote server object. Before using
|
@@ -39,7 +38,6 @@ module OpenC3
|
|
39
38
|
# server.cmd(*args)
|
40
39
|
#
|
41
40
|
class JsonDRbObject < JsonApiObject
|
42
|
-
|
43
41
|
USER_AGENT = 'OpenC3 / v5 (ruby/openc3/lib/io/json_drb_object)'
|
44
42
|
|
45
43
|
# @param url [String] The url of openc3-cosmos-cmd-tlm-api http://openc3-cosmos-cmd-tlm-api:2901
|
@@ -80,7 +78,6 @@ module OpenC3
|
|
80
78
|
|
81
79
|
private
|
82
80
|
|
83
|
-
#
|
84
81
|
def make_request(data:, token: nil)
|
85
82
|
token = @authentication.token if @authentication and not token
|
86
83
|
if token
|
@@ -98,7 +95,7 @@ module OpenC3
|
|
98
95
|
begin
|
99
96
|
@log[0] = "Request: #{@uri.to_s} #{USER_AGENT} #{data.to_s}"
|
100
97
|
STDOUT.puts @log[0] if JsonDRb.debug?
|
101
|
-
resp = @http.post(@uri,
|
98
|
+
resp = @http.post(@uri, data, headers)
|
102
99
|
@log[1] = "Response: #{resp.status} #{resp.headers} #{resp.body}"
|
103
100
|
@response_data = resp.body
|
104
101
|
STDOUT.puts @log[1] if JsonDRb.debug?
|
@@ -109,7 +106,6 @@ module OpenC3
|
|
109
106
|
end
|
110
107
|
end
|
111
108
|
|
112
|
-
#
|
113
109
|
def handle_response(response:)
|
114
110
|
# The code below will always either raise or return breaking out of the loop
|
115
111
|
if JsonRpcErrorResponse === response
|
@@ -122,5 +118,5 @@ module OpenC3
|
|
122
118
|
return response.result
|
123
119
|
end
|
124
120
|
end
|
125
|
-
end
|
121
|
+
end
|
126
122
|
end
|
@@ -96,23 +96,31 @@ module OpenC3
|
|
96
96
|
return nil
|
97
97
|
end
|
98
98
|
|
99
|
-
def
|
100
|
-
|
101
|
-
|
99
|
+
def close_file(take_mutex = true)
|
100
|
+
@mutex.lock if take_mutex
|
101
|
+
begin
|
102
|
+
# Need to write out the buffer before closing out the file
|
103
|
+
if @buffer.length > 0
|
104
|
+
start_new_file() unless @file
|
105
|
+
write_buffer()
|
106
|
+
end
|
107
|
+
return super(false) # Someone has already taken mutex here
|
108
|
+
ensure
|
109
|
+
@mutex.unlock if take_mutex
|
110
|
+
end
|
102
111
|
end
|
103
112
|
|
113
|
+
# Mutex is already taken when this is called so we need to adjust
|
114
|
+
# prepare_write and write accordingly
|
104
115
|
def write_buffer
|
105
|
-
|
106
|
-
|
116
|
+
begin
|
117
|
+
@buffer.each do |entry|
|
118
|
+
write(*entry, allow_new_file: false, take_mutex: false)
|
119
|
+
end
|
120
|
+
rescue => err
|
121
|
+
Logger.instance.error "Error writing out buffer : #{err.formatted}"
|
107
122
|
end
|
108
123
|
@buffer = []
|
109
124
|
end
|
110
|
-
|
111
|
-
# Need to write out all remaining buffer entries and then shutdown
|
112
|
-
# Returns thread that moves final log to bucket
|
113
|
-
def shutdown
|
114
|
-
write_buffer()
|
115
|
-
return super()
|
116
|
-
end
|
117
125
|
end
|
118
126
|
end
|
@@ -130,6 +130,7 @@ module OpenC3
|
|
130
130
|
@cleanup_offsets = []
|
131
131
|
@cleanup_times = []
|
132
132
|
@previous_time_nsec_since_epoch = nil
|
133
|
+
@tmp_dir = Dir.mktmpdir
|
133
134
|
|
134
135
|
# This is an optimization to avoid creating a new entry object
|
135
136
|
# each time we create an entry which we do a LOT!
|
@@ -157,7 +158,7 @@ module OpenC3
|
|
157
158
|
# Stops all logging and closes the current log file.
|
158
159
|
def stop
|
159
160
|
threads = nil
|
160
|
-
@mutex.synchronize {
|
161
|
+
@mutex.synchronize { threads = close_file(false); @logging_enabled = false; }
|
161
162
|
return threads
|
162
163
|
end
|
163
164
|
|
@@ -187,10 +188,11 @@ module OpenC3
|
|
187
188
|
while true
|
188
189
|
filename_parts = [attempt]
|
189
190
|
filename_parts.unshift @label if @label
|
190
|
-
filename = File.join(
|
191
|
+
filename = File.join(@tmp_dir, File.build_timestamped_filename([@label, attempt], ext))
|
191
192
|
if File.exist?(filename)
|
192
193
|
attempt ||= 0
|
193
194
|
attempt += 1
|
195
|
+
Logger.warn("Unexpected file name conflict: #{filename}")
|
194
196
|
else
|
195
197
|
return filename
|
196
198
|
end
|
@@ -260,7 +262,7 @@ module OpenC3
|
|
260
262
|
# wrapped with a rescue and handled with handle_critical_exception
|
261
263
|
# Assumes mutex has already been taken
|
262
264
|
def start_new_file
|
263
|
-
close_file(false)
|
265
|
+
close_file(false) if @file
|
264
266
|
|
265
267
|
# Start log file
|
266
268
|
@filename = create_unique_filename()
|
@@ -279,16 +281,16 @@ module OpenC3
|
|
279
281
|
OpenC3.handle_critical_exception(err)
|
280
282
|
end
|
281
283
|
|
282
|
-
def prepare_write(time_nsec_since_epoch, data_length, redis_topic = nil, redis_offset = nil)
|
284
|
+
def prepare_write(time_nsec_since_epoch, data_length, redis_topic = nil, redis_offset = nil, allow_new_file: true)
|
283
285
|
# This check includes logging_enabled again because it might have changed since we acquired the mutex
|
284
286
|
# Ensures new files based on size, and ensures always increasing time order in files
|
285
287
|
if @logging_enabled
|
286
288
|
if !@file
|
287
289
|
Logger.debug("Log writer start new file because no file opened")
|
288
|
-
start_new_file()
|
290
|
+
start_new_file() if allow_new_file
|
289
291
|
elsif @cycle_size and ((@file_size + data_length) > @cycle_size)
|
290
292
|
Logger.debug("Log writer start new file due to cycle size #{@cycle_size}")
|
291
|
-
start_new_file()
|
293
|
+
start_new_file() if allow_new_file
|
292
294
|
elsif @enforce_time_order and @previous_time_nsec_since_epoch and (@previous_time_nsec_since_epoch > time_nsec_since_epoch)
|
293
295
|
# Warning: Creating new files here can cause lots of files to be created if packets make it through out of order
|
294
296
|
# Changed to just a error to prevent file thrashing
|
@@ -315,6 +317,9 @@ module OpenC3
|
|
315
317
|
Logger.debug "Log File Closed : #{@filename}"
|
316
318
|
date = first_timestamp[0..7] # YYYYMMDD
|
317
319
|
bucket_key = File.join(@remote_log_directory, date, bucket_filename())
|
320
|
+
# Cleanup timestamps here so they are unset for the next file
|
321
|
+
@first_time = nil
|
322
|
+
@last_time = nil
|
318
323
|
threads << BucketUtilities.move_log_file_to_bucket(@filename, bucket_key)
|
319
324
|
# Now that the file is in storage, trim the Redis stream after a delay
|
320
325
|
@cleanup_offsets << {}
|
@@ -324,7 +329,7 @@ module OpenC3
|
|
324
329
|
@cleanup_times << Time.now + CLEANUP_DELAY
|
325
330
|
@last_offsets.clear
|
326
331
|
rescue Exception => err
|
327
|
-
Logger.
|
332
|
+
Logger.error "Error closing #{@filename} : #{err.formatted}"
|
328
333
|
end
|
329
334
|
|
330
335
|
@file = nil
|
@@ -97,12 +97,15 @@ module OpenC3
|
|
97
97
|
# @param data [String] Binary string of data
|
98
98
|
# @param id [Integer] Target ID
|
99
99
|
# @param redis_offset [Integer] The offset of this packet in its Redis stream
|
100
|
-
def write(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id = nil, redis_topic = nil, redis_offset = '0-0')
|
100
|
+
def write(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id = nil, redis_topic = nil, redis_offset = '0-0', take_mutex: true, allow_new_file: true)
|
101
101
|
return if !@logging_enabled
|
102
102
|
|
103
|
-
@mutex.
|
104
|
-
|
103
|
+
@mutex.lock if take_mutex
|
104
|
+
begin
|
105
|
+
prepare_write(time_nsec_since_epoch, data.length, redis_topic, redis_offset, allow_new_file: allow_new_file)
|
105
106
|
write_entry(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id) if @file
|
107
|
+
ensure
|
108
|
+
@mutex.unlock if take_mutex
|
106
109
|
end
|
107
110
|
rescue => err
|
108
111
|
Logger.instance.error "Error writing #{@filename} : #{err.formatted}"
|
@@ -121,7 +124,6 @@ module OpenC3
|
|
121
124
|
@index_filename = create_unique_filename('.idx'.freeze)
|
122
125
|
@index_file = File.new(@index_filename, 'wb')
|
123
126
|
@index_file.write(OPENC3_INDEX_HEADER)
|
124
|
-
|
125
127
|
@cmd_packet_table = {}
|
126
128
|
@tlm_packet_table = {}
|
127
129
|
@key_map_table = {}
|
@@ -149,8 +151,6 @@ module OpenC3
|
|
149
151
|
write_entry(:OFFSET_MARKER, nil, nil, nil, nil, nil, last_offset + ',' + redis_topic, nil) if @file
|
150
152
|
end
|
151
153
|
|
152
|
-
threads.concat(super(false))
|
153
|
-
|
154
154
|
if @index_file
|
155
155
|
begin
|
156
156
|
write_index_file_footer()
|
@@ -166,6 +166,9 @@ module OpenC3
|
|
166
166
|
@index_file = nil
|
167
167
|
@index_filename = nil
|
168
168
|
end
|
169
|
+
|
170
|
+
threads.concat(super(false))
|
171
|
+
|
169
172
|
ensure
|
170
173
|
@mutex.unlock if take_mutex
|
171
174
|
end
|
@@ -60,6 +60,10 @@ module OpenC3
|
|
60
60
|
handle_inject_tlm(msg_hash['inject_tlm'])
|
61
61
|
next
|
62
62
|
end
|
63
|
+
if msg_hash.key?('build_cmd')
|
64
|
+
handle_build_cmd(msg_hash['build_cmd'], msg_id)
|
65
|
+
next
|
66
|
+
end
|
63
67
|
else
|
64
68
|
decom_packet(topic, msg_id, msg_hash, redis)
|
65
69
|
@metric.set(name: 'decom_total', value: @count, type: 'counter')
|
@@ -37,5 +37,37 @@ module OpenC3
|
|
37
37
|
packet.received_time = Time.now.sys
|
38
38
|
TelemetryTopic.write_packet(packet, scope: @scope)
|
39
39
|
end
|
40
|
+
|
41
|
+
def handle_build_cmd(build_cmd_json, msg_id)
|
42
|
+
build_cmd_hash = JSON.parse(build_cmd_json, allow_nan: true, create_additions: true)
|
43
|
+
target_name = build_cmd_hash['target_name']
|
44
|
+
cmd_name = build_cmd_hash['cmd_name']
|
45
|
+
cmd_params = build_cmd_hash['cmd_params']
|
46
|
+
range_check = build_cmd_hash['range_check']
|
47
|
+
raw = build_cmd_hash['raw']
|
48
|
+
ack_topic = "{#{@scope}__ACKCMD}TARGET__#{target_name}"
|
49
|
+
begin
|
50
|
+
command = System.commands.build_cmd(target_name, cmd_name, cmd_params, range_check, raw)
|
51
|
+
msg_hash = {
|
52
|
+
id: msg_id,
|
53
|
+
result: 'SUCCESS',
|
54
|
+
time: command.packet_time.to_nsec_from_epoch,
|
55
|
+
received_time: command.received_time.to_nsec_from_epoch,
|
56
|
+
target_name: command.target_name,
|
57
|
+
packet_name: command.packet_name,
|
58
|
+
received_count: command.received_count,
|
59
|
+
buffer: command.buffer(false)
|
60
|
+
}
|
61
|
+
# If there is an error due to parameter out of range, etc, we rescue it so we can
|
62
|
+
# write the ACKCMD}TARGET topic and allow the TelemetryDecomTopic.build_cmd to return
|
63
|
+
rescue => error
|
64
|
+
msg_hash = {
|
65
|
+
id: msg_id,
|
66
|
+
result: 'ERROR',
|
67
|
+
message: error.message
|
68
|
+
}
|
69
|
+
end
|
70
|
+
Topic.write_topic(ack_topic, msg_hash)
|
71
|
+
end
|
40
72
|
end
|
41
73
|
end
|
@@ -386,7 +386,7 @@ module OpenC3
|
|
386
386
|
# Write out all entries in progress
|
387
387
|
write_all_entries(reducer_state, plw, type, target_name, stored)
|
388
388
|
reducer_state.clear
|
389
|
-
plw.
|
389
|
+
plw.close_file
|
390
390
|
return true
|
391
391
|
else
|
392
392
|
return false
|
@@ -450,20 +450,24 @@ module OpenC3
|
|
450
450
|
# We've collected all the values so calculate the AVG and STDDEV
|
451
451
|
if type == 'minute'
|
452
452
|
raw_keys.each do |key|
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
453
|
+
if reduced["#{key}__VALS"]
|
454
|
+
reduced["_NUM_SAMPLES"] ||= reduced["#{key}__VALS"].length # Keep a single sample count per packet
|
455
|
+
reduced["#{key}__A"], reduced["#{key}__S"] =
|
456
|
+
Math.stddev_population(reduced["#{key}__VALS"])
|
457
|
+
# Remove the raw values as they're only used for AVG / STDDEV calculation
|
458
|
+
reduced.delete("#{key}__VALS")
|
459
|
+
end
|
458
460
|
end
|
459
461
|
|
460
462
|
converted_keys.each do |key|
|
461
|
-
|
462
|
-
|
463
|
-
|
463
|
+
if reduced["#{key}__CVALS"]
|
464
|
+
reduced["_NUM_SAMPLES"] ||= reduced["#{key}__CVALS"].length # Keep a single sample count per packet
|
465
|
+
reduced["#{key}__CA"], reduced["#{key}__CS"] =
|
466
|
+
Math.stddev_population(reduced["#{key}__CVALS"])
|
464
467
|
|
465
|
-
|
466
|
-
|
468
|
+
# Remove the converted values as they're only used for AVG / STDDEV calculation
|
469
|
+
reduced.delete("#{key}__CVALS")
|
470
|
+
end
|
467
471
|
end
|
468
472
|
else
|
469
473
|
samples = reduced["_NUM_SAMPLES__VALS"]
|
@@ -40,9 +40,16 @@ module OpenC3
|
|
40
40
|
Store.hset("#{scope}__tlm__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true)))
|
41
41
|
end
|
42
42
|
|
43
|
+
# Get the hash for packet in the CVT
|
44
|
+
def self.get(target_name:, packet_name:, scope: $openc3_scope)
|
45
|
+
packet = Store.hget("#{scope}__tlm__#{target_name}", packet_name)
|
46
|
+
raise "Packet '#{target_name} #{packet_name}' does not exist" unless packet
|
47
|
+
JSON.parse(packet, :allow_nan => true, :create_additions => true)
|
48
|
+
end
|
49
|
+
|
43
50
|
# Set an item in the current value table
|
44
51
|
def self.set_item(target_name, packet_name, item_name, value, type:, scope: $openc3_scope)
|
45
|
-
hash =
|
52
|
+
hash = get(target_name: target_name, packet_name: packet_name, scope: scope)
|
46
53
|
case type
|
47
54
|
when :WITH_UNITS
|
48
55
|
hash["#{item_name}__U"] = value.to_s # WITH_UNITS should always be a string
|
@@ -87,7 +94,7 @@ module OpenC3
|
|
87
94
|
result = JSON.parse(overrides, :allow_nan => true, :create_additions => true)[override_key]
|
88
95
|
return result if result
|
89
96
|
end
|
90
|
-
hash =
|
97
|
+
hash = get(target_name: target_name, packet_name: packet_name, scope: scope)
|
91
98
|
hash.values_at(*types).each do |result|
|
92
99
|
if result
|
93
100
|
if type == :FORMATTED or type == :WITH_UNITS
|
@@ -115,9 +122,7 @@ module OpenC3
|
|
115
122
|
|
116
123
|
lookups.each do |target_packet_key, target_name, packet_name, value_keys|
|
117
124
|
unless packet_lookup[target_packet_key]
|
118
|
-
|
119
|
-
raise "Packet '#{target_name} #{packet_name}' does not exist" unless packet
|
120
|
-
packet_lookup[target_packet_key] = JSON.parse(packet, :allow_nan => true, :create_additions => true)
|
125
|
+
packet_lookup[target_packet_key] = get(target_name: target_name, packet_name: packet_name, scope: scope)
|
121
126
|
end
|
122
127
|
hash = packet_lookup[target_packet_key]
|
123
128
|
item_result = []
|