openc3 5.7.2 → 5.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +6 -6
- 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 = []
|