openc3 6.6.0 → 6.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/openc3cli +66 -10
- data/data/config/command_modifiers.yaml +3 -3
- data/data/config/interface_modifiers.yaml +1 -1
- data/data/config/table_manager.yaml +1 -1
- data/data/config/telemetry_modifiers.yaml +3 -3
- data/data/config/widgets.yaml +2 -2
- data/lib/openc3/accessors.rb +1 -1
- data/lib/openc3/api/settings_api.rb +8 -0
- data/lib/openc3/api/stash_api.rb +1 -1
- data/lib/openc3/api/tlm_api.rb +93 -14
- data/lib/openc3/core_ext/kernel.rb +3 -3
- data/lib/openc3/logs/log_writer.rb +16 -12
- data/lib/openc3/microservices/plugin_microservice.rb +2 -2
- data/lib/openc3/models/cvt_model.rb +140 -3
- data/lib/openc3/models/plugin_model.rb +7 -2
- data/lib/openc3/models/plugin_store_model.rb +70 -0
- data/lib/openc3/models/target_model.rb +26 -0
- data/lib/openc3/models/tool_model.rb +1 -1
- data/lib/openc3/packets/packet.rb +3 -3
- data/lib/openc3/packets/parsers/state_parser.rb +7 -1
- data/lib/openc3/packets/structure.rb +9 -2
- data/lib/openc3/script/calendar.rb +10 -10
- data/lib/openc3/script/commands.rb +1 -1
- data/lib/openc3/script/script_runner.rb +7 -2
- data/lib/openc3/script/tables.rb +3 -3
- data/lib/openc3/utilities/authorization.rb +1 -1
- data/lib/openc3/utilities/cosmos_rails_formatter.rb +1 -1
- data/lib/openc3/utilities/logger.rb +1 -1
- data/lib/openc3/utilities/running_script.rb +5 -1
- data/lib/openc3/version.rb +5 -5
- data/templates/tool_angular/package.json +2 -2
- data/templates/tool_react/package.json +1 -1
- data/templates/tool_svelte/package.json +1 -1
- data/templates/tool_vue/package.json +3 -3
- data/templates/widget/package.json +2 -2
- metadata +59 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 35ed91c6cd987b0a1c139ef5cd9670d204eb524ae7eebdf971d58aef3dbc5faa
|
4
|
+
data.tar.gz: 1a0d8ca81f317a7a5227d56be83ab847bed1a7c4ac48ea991f34ed46a3e80e1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f305acecd79bfd96b93829a7a3b67b4095ef45104fa7571f5d3c59ac70e753668f0a8e5bde7ad2818c3c22966bf55b524e1fa6bb41a14aa124a2c1f063115781
|
7
|
+
data.tar.gz: 85f4fc3479388969c39cdedcd180498cf1eeb3870d0c609590f4fb6c816d25efdbc02aff4cf499a96da88b3176846a6ba7e08e81dca483659928c475f673be36
|
data/bin/openc3cli
CHANGED
@@ -63,8 +63,9 @@ def print_usage
|
|
63
63
|
puts " cli rake # Runs rake in the local directory"
|
64
64
|
puts " cli irb # Runs irb in the local directory"
|
65
65
|
puts " cli script # Interact with scripts. Run with --help for more info."
|
66
|
-
puts " cli validate /PATH/FILENAME.gem SCOPE variables.
|
67
|
-
puts " cli load /PATH/FILENAME.gem SCOPE
|
66
|
+
puts " cli validate /PATH/FILENAME.gem SCOPE variables.json # Validate a COSMOS plugin gem file"
|
67
|
+
puts " cli load /PATH/FILENAME.gem SCOPE plugin_hash.json # Loads a COSMOS plugin gem file"
|
68
|
+
puts " OPTIONS: --variables lets you pass a path to a JSON file containing your plugin's variables"
|
68
69
|
puts " cli list <SCOPE> # Lists installed plugins, SCOPE is DEFAULT if not given"
|
69
70
|
puts " cli generate TYPE OPTIONS # Generate various COSMOS entities"
|
70
71
|
puts " OPTIONS: --ruby or --python is required to specify the language in the generated code unless OPENC3_LANGUAGE is set"
|
@@ -333,7 +334,7 @@ end
|
|
333
334
|
# Pass true as the last argument to force install even if a plugin with
|
334
335
|
# the same version number exists
|
335
336
|
#
|
336
|
-
def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil, force: false)
|
337
|
+
def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil, force: false, variables_file: nil)
|
337
338
|
scope ||= 'DEFAULT'
|
338
339
|
check_environment()
|
339
340
|
if $openc3_in_cluster
|
@@ -352,9 +353,10 @@ def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil, force: false)
|
|
352
353
|
end
|
353
354
|
|
354
355
|
begin
|
356
|
+
existing_variables = JSON.parse(File.read(variables_file)) if variables_file
|
355
357
|
if plugin_hash_file
|
356
358
|
# Admin Create / Edit / or Upgrade Plugin
|
357
|
-
OpenC3::PluginModel.install_phase1(plugin_file_path, scope: scope)
|
359
|
+
OpenC3::PluginModel.install_phase1(plugin_file_path, existing_variables: existing_variables, scope: scope)
|
358
360
|
plugin_hash = JSON.parse(File.read(plugin_hash_file), :allow_nan => true, :create_additions => true)
|
359
361
|
else
|
360
362
|
# Init or Command Line openc3cli load with no plugin_hash_file
|
@@ -378,7 +380,7 @@ def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil, force: false)
|
|
378
380
|
end
|
379
381
|
return if found
|
380
382
|
|
381
|
-
plugin_hash = OpenC3::PluginModel.install_phase1(plugin_file_path, scope: scope)
|
383
|
+
plugin_hash = OpenC3::PluginModel.install_phase1(plugin_file_path, existing_variables: existing_variables, scope: scope)
|
382
384
|
end
|
383
385
|
|
384
386
|
# Determine if plugin named in plugin_hash exists
|
@@ -640,12 +642,39 @@ def cli_script_list(args, options)
|
|
640
642
|
return 0
|
641
643
|
end
|
642
644
|
|
645
|
+
def parse_suite_runner_options(options)
|
646
|
+
suite_runner = nil
|
647
|
+
# suite must be given to enable Suite Runner execution
|
648
|
+
if options[:suite]
|
649
|
+
suite_runner = {}
|
650
|
+
suite_runner['suite'] = options[:suite]
|
651
|
+
if options[:group]
|
652
|
+
suite_runner['group'] = options[:group]
|
653
|
+
# script requires group to be set
|
654
|
+
if options[:script]
|
655
|
+
suite_runner['script'] = options[:script]
|
656
|
+
end
|
657
|
+
end
|
658
|
+
if options[:method]
|
659
|
+
suite_runner['method'] = options[:method]
|
660
|
+
else
|
661
|
+
suite_runner['method'] = 'start'
|
662
|
+
end
|
663
|
+
if options[:options]
|
664
|
+
suite_runner['options'] = options[:options].split(',')
|
665
|
+
else
|
666
|
+
suite_runner['options'] = ["continueAfterError"]
|
667
|
+
end
|
668
|
+
end
|
669
|
+
return suite_runner
|
670
|
+
end
|
671
|
+
|
643
672
|
def cli_script_run(args, options)
|
644
673
|
environment = get_env_from_args(args)
|
645
|
-
|
674
|
+
suite_runner = parse_suite_runner_options(options)
|
646
675
|
ret_code = ERROR_CODE
|
647
676
|
require 'openc3/script'
|
648
|
-
if (id = script_run(args[1], disconnect: options[:disconnect], environment: environment, scope: options[:scope]))
|
677
|
+
if (id = script_run(args[1], disconnect: options[:disconnect], environment: environment, suite_runner: suite_runner, scope: options[:scope]))
|
649
678
|
puts id
|
650
679
|
$script_interrupt_text = " Script #{args[1]} still running remotely.\n" # for Ctrl-C
|
651
680
|
if (options[:wait] < 1) then
|
@@ -673,10 +702,10 @@ end
|
|
673
702
|
|
674
703
|
def cli_script_spawn(args, options)
|
675
704
|
environment = get_env_from_args(args)
|
676
|
-
|
705
|
+
suite_runner = parse_suite_runner_options(options)
|
677
706
|
ret_code = ERROR_CODE
|
678
707
|
require 'openc3/script'
|
679
|
-
if (id = script_run(args[1], disconnect: options[:disconnect], environment: environment, scope: options[:scope]))
|
708
|
+
if (id = script_run(args[1], disconnect: options[:disconnect], environment: environment, suite_runner: suite_runner, scope: options[:scope]))
|
680
709
|
puts id
|
681
710
|
ret_code = 0
|
682
711
|
end
|
@@ -769,6 +798,22 @@ def cli_script(args=[])
|
|
769
798
|
opts.on("--scope SCOPE", "Run with specified scope (default = DEFAULT)") do |arg|
|
770
799
|
options[:scope] = arg
|
771
800
|
end
|
801
|
+
opts.on("--suite SUITE", "Run with specified suite") do |arg|
|
802
|
+
options[:suite] = arg
|
803
|
+
end
|
804
|
+
opts.on("--group GROUP", "Run with specified group") do |arg|
|
805
|
+
options[:group] = arg
|
806
|
+
end
|
807
|
+
opts.on("--script SCRIPT", "Run with specified script") do |arg|
|
808
|
+
options[:script] = arg
|
809
|
+
end
|
810
|
+
opts.on("--method start", "Method must be start, setup, or teardown. Default is start.") do |arg|
|
811
|
+
options[:method] = arg
|
812
|
+
end
|
813
|
+
# TODO 7.0: Should these all be snake case?
|
814
|
+
opts.on("--options options", "Options is a comma separated list consisting of continueAfterError,pauseOnError,abortAfterError,manual,loop,breakLoopOnError. Default is continueAfterError.") do |arg|
|
815
|
+
options[:options] = arg
|
816
|
+
end
|
772
817
|
opts.on("-d", "--disconnect", "Run a script in disconnect mode (default = false)") do |arg|
|
773
818
|
options[:disconnect] = arg
|
774
819
|
end
|
@@ -848,7 +893,18 @@ if not ARGV[0].nil? # argument(s) given
|
|
848
893
|
when 'load'
|
849
894
|
# force is a boolean so if they pass 'force' it is true
|
850
895
|
# See plugins_controller.rb install for usage
|
851
|
-
|
896
|
+
variables_option = ARGV.find_index('--variables')
|
897
|
+
if variables_option.nil?
|
898
|
+
scope = ARGV[2]
|
899
|
+
plugin_hash_file = ARGV[3]
|
900
|
+
force = ARGV[4] == 'force'
|
901
|
+
else
|
902
|
+
scope = ARGV[2] unless variables_option <= 2
|
903
|
+
plugin_hash_file = ARGV[3] unless variables_option <= 3
|
904
|
+
force = ARGV[4] == 'force' unless variables_option <= 4
|
905
|
+
variables_file = ARGV[variables_option + 1]
|
906
|
+
end
|
907
|
+
load_plugin(ARGV[1], scope: scope, plugin_hash_file: plugin_hash_file, force: force, variables_file: variables_file)
|
852
908
|
|
853
909
|
when 'list'
|
854
910
|
list_plugins(scope: ARGV[1])
|
@@ -11,7 +11,7 @@ PARAMETER:
|
|
11
11
|
- name: Bit Offset
|
12
12
|
required: true
|
13
13
|
description: Bit offset into the command packet of the Most Significant Bit of this parameter.
|
14
|
-
May be negative to indicate
|
14
|
+
May be negative to indicate an offset from the end of the packet.
|
15
15
|
Always use a bit offset of 0 for derived parameters.
|
16
16
|
values: '[-]?\d+'
|
17
17
|
<%= MetaConfigParser.load('_params.yaml').to_meta_config_yaml(4) %>
|
@@ -50,7 +50,7 @@ ID_PARAMETER:
|
|
50
50
|
- name: Bit Offset
|
51
51
|
required: true
|
52
52
|
description: Bit offset into the command packet of the Most Significant Bit of this parameter.
|
53
|
-
May be negative to indicate
|
53
|
+
May be negative to indicate an offset from the end of the packet.
|
54
54
|
values: '[-]?\d+'
|
55
55
|
<%= MetaConfigParser.load('_id_params.yaml').to_meta_config_yaml(4) %>
|
56
56
|
example: ID_PARAMETER OPCODE 32 32 UINT 2 2 2 "Opcode identifier"
|
@@ -80,7 +80,7 @@ ARRAY_PARAMETER:
|
|
80
80
|
- name: Bit Offset
|
81
81
|
required: true
|
82
82
|
description: Bit offset into the command packet of the Most Significant Bit of this parameter.
|
83
|
-
May be negative to indicate
|
83
|
+
May be negative to indicate an offset from the end of the packet.
|
84
84
|
Always use a bit offset of 0 for derived parameters.
|
85
85
|
values: '[-]?\d+'
|
86
86
|
<%= MetaConfigParser.load('_array_params.yaml').to_meta_config_yaml(4) %>
|
@@ -124,7 +124,7 @@ PROTOCOL:
|
|
124
124
|
python_example: |
|
125
125
|
INTERFACE DATA_INT openc3/interfaces/tcpip_client_interface.py host.docker.internal 8080 8081 10.0 nil BURST
|
126
126
|
MAP_TARGET DATA
|
127
|
-
PROTOCOL READ
|
127
|
+
PROTOCOL READ openc3/interfaces/protocols/ignore_packet_protocol.py INST IMAGE # Drop all INST IMAGE packets
|
128
128
|
OPTION:
|
129
129
|
summary: Set a parameter on an interface
|
130
130
|
description:
|
@@ -22,7 +22,7 @@ TABLE:
|
|
22
22
|
- name: Bit Offset
|
23
23
|
required: true
|
24
24
|
description: Bit offset into the table of the Most Significant Bit of this parameter.
|
25
|
-
May be negative to indicate
|
25
|
+
May be negative to indicate an offset from the end of the table.
|
26
26
|
Always use a bit offset of 0 for derived parameters.
|
27
27
|
values: '[-]?\d+'
|
28
28
|
<%= MetaConfigParser.load('_params.yaml').to_meta_config_yaml(8) %>
|
@@ -14,7 +14,7 @@ ITEM:
|
|
14
14
|
- name: Bit Offset
|
15
15
|
required: true
|
16
16
|
description: Bit offset into the telemetry packet of the Most Significant Bit of this item.
|
17
|
-
May be negative to indicate
|
17
|
+
May be negative to indicate an offset from the end of the packet.
|
18
18
|
Always use a bit offset of 0 for derived item.
|
19
19
|
values: '[-]?\d+'
|
20
20
|
<%= MetaConfigParser.load('_items.yaml').to_meta_config_yaml(4) %>
|
@@ -44,7 +44,7 @@ ID_ITEM:
|
|
44
44
|
- name: Bit Offset
|
45
45
|
required: true
|
46
46
|
description: Bit offset into the telemetry packet of the Most Significant Bit of this item.
|
47
|
-
May be negative to indicate
|
47
|
+
May be negative to indicate an offset from the end of the packet.
|
48
48
|
values: '[-]?\d+'
|
49
49
|
<%= MetaConfigParser.load('_id_items.yaml').to_meta_config_yaml(4) %>
|
50
50
|
APPEND_ID_ITEM:
|
@@ -71,7 +71,7 @@ ARRAY_ITEM:
|
|
71
71
|
- name: Bit Offset
|
72
72
|
required: true
|
73
73
|
description: Bit offset into the telemetry packet of the Most Significant Bit of this item.
|
74
|
-
May be negative to indicate
|
74
|
+
May be negative to indicate an offset from the end of the packet.
|
75
75
|
Always use a bit offset of 0 for derived item.
|
76
76
|
values: '[-]?\d+'
|
77
77
|
<%= MetaConfigParser.load('_array_params.yaml').to_meta_config_yaml(4) %>
|
data/data/config/widgets.yaml
CHANGED
@@ -746,11 +746,11 @@ Telemetry Widgets:
|
|
746
746
|
values: <%= %w(RAW CONVERTED FORMATTED WITH_UNITS) %>
|
747
747
|
- name: Width
|
748
748
|
required: false
|
749
|
-
description: Width of the LED circle (default =
|
749
|
+
description: Width of the LED circle (default = 20)
|
750
750
|
values: .*
|
751
751
|
- name: Height
|
752
752
|
required: false
|
753
|
-
description: Height of the LED circle (default =
|
753
|
+
description: Height of the LED circle (default = 20)
|
754
754
|
values: .*
|
755
755
|
example: |
|
756
756
|
LED INST PARAMS VALUE5 RAW 25 20 # Ellipse
|
data/lib/openc3/accessors.rb
CHANGED
@@ -31,6 +31,7 @@ rescue LoadError
|
|
31
31
|
end
|
32
32
|
require 'openc3/models/setting_model'
|
33
33
|
require 'openc3/models/news_model'
|
34
|
+
require 'openc3/models/plugin_store_model'
|
34
35
|
|
35
36
|
module OpenC3
|
36
37
|
module Api
|
@@ -43,6 +44,7 @@ module OpenC3
|
|
43
44
|
'set_setting',
|
44
45
|
'save_setting', # DEPRECATED
|
45
46
|
'update_news',
|
47
|
+
'update_plugin_store',
|
46
48
|
])
|
47
49
|
|
48
50
|
def list_settings(manual: false, scope: $openc3_scope, token: $openc3_token)
|
@@ -103,5 +105,11 @@ module OpenC3
|
|
103
105
|
rescue Exception => e
|
104
106
|
NewsModel.news_error("Error contacting OpenC3 news feed. #{e.message})")
|
105
107
|
end
|
108
|
+
|
109
|
+
# Update the local copy of the plugin store data
|
110
|
+
def update_plugin_store(manual: false, scope: $openc3_scope, token: $openc3_token)
|
111
|
+
authorize(permission: 'admin', manual: manual, scope: scope, token: token)
|
112
|
+
PluginStoreModel.update()
|
113
|
+
end
|
106
114
|
end
|
107
115
|
end
|
data/lib/openc3/api/stash_api.rb
CHANGED
data/lib/openc3/api/tlm_api.rb
CHANGED
@@ -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 2025, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -22,7 +22,7 @@
|
|
22
22
|
#
|
23
23
|
# A portion of this file was funded by Blue Origin Enterprises, L.P.
|
24
24
|
# See https://github.com/OpenC3/cosmos/pull/1963
|
25
|
-
|
25
|
+
#
|
26
26
|
# A portion of this file was funded by Blue Origin Enterprises, L.P.
|
27
27
|
# See https://github.com/OpenC3/cosmos/pull/1957
|
28
28
|
|
@@ -49,6 +49,7 @@ module OpenC3
|
|
49
49
|
'normalize_tlm',
|
50
50
|
'get_tlm_buffer',
|
51
51
|
'get_tlm_packet',
|
52
|
+
'get_tlm_available',
|
52
53
|
'get_tlm_values',
|
53
54
|
'get_all_tlm',
|
54
55
|
'get_all_telemetry', # DEPRECATED
|
@@ -254,6 +255,78 @@ module OpenC3
|
|
254
255
|
items.zip(current_values).map { | item , values | [item, values[0], values[1]]}
|
255
256
|
end
|
256
257
|
|
258
|
+
# Returns the available items from a list of requested screen items
|
259
|
+
# This does the packet introspection to determine what is actually available
|
260
|
+
# Like if you ask for WITH_UNITS but only RAW is available
|
261
|
+
def get_tlm_available(items, manual: false, scope: $openc3_scope, token: $openc3_token)
|
262
|
+
results = []
|
263
|
+
items.each do |item|
|
264
|
+
item_upcase = item.to_s.upcase
|
265
|
+
target_name, orig_packet_name, item_name, value_type = item_upcase.split('__')
|
266
|
+
packet_name = orig_packet_name
|
267
|
+
raise ArgumentError, "items must be formatted as TGT__PKT__ITEM__TYPE" if target_name.nil? || packet_name.nil? || item_name.nil? || value_type.nil?
|
268
|
+
if orig_packet_name == 'LATEST'
|
269
|
+
# TODO: Do we need to lookup ALL the possible packets for this item?
|
270
|
+
# We can have a large cache_timeout of 1 because all we're trying to do is lookup a packet
|
271
|
+
packet_name = CvtModel.determine_latest_packet_for_item(target_name, item_name, cache_timeout: 1, scope: scope)
|
272
|
+
end
|
273
|
+
authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
|
274
|
+
begin
|
275
|
+
item = TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
|
276
|
+
if Packet::RESERVED_ITEM_NAMES.include?(item_name)
|
277
|
+
value_type = 'RAW' # Must request the raw value when dealing with the reserved items
|
278
|
+
end
|
279
|
+
|
280
|
+
# QuestDB 9.0.0 only supports DOUBLE arrays: https://questdb.com/docs/concept/array/
|
281
|
+
if item['array_size']
|
282
|
+
# TODO: This needs work ... we're JSON encoding non numeric array values
|
283
|
+
if item['data_type'] == 'STRING' or item['data_type'] == 'BLOCK'
|
284
|
+
results << nil
|
285
|
+
next
|
286
|
+
end
|
287
|
+
value_type = 'RAW'
|
288
|
+
end
|
289
|
+
|
290
|
+
case value_type
|
291
|
+
when 'WITH_UNITS'
|
292
|
+
if item['units']
|
293
|
+
results << [target_name, orig_packet_name, item_name, 'WITH_UNITS'].join('__')
|
294
|
+
elsif item['format_string']
|
295
|
+
results << [target_name, orig_packet_name, item_name, 'FORMATTED'].join('__')
|
296
|
+
elsif item['read_conversion'] or item['states']
|
297
|
+
results << [target_name, orig_packet_name, item_name, 'CONVERTED'].join('__')
|
298
|
+
else
|
299
|
+
results << [target_name, orig_packet_name, item_name, 'RAW'].join('__')
|
300
|
+
end
|
301
|
+
when 'FORMATTED'
|
302
|
+
if item['format_string']
|
303
|
+
results << [target_name, orig_packet_name, item_name, 'FORMATTED'].join('__')
|
304
|
+
elsif item['read_conversion'] or item['states']
|
305
|
+
results << [target_name, orig_packet_name, item_name, 'CONVERTED'].join('__')
|
306
|
+
else
|
307
|
+
results << [target_name, orig_packet_name, item_name, 'RAW'].join('__')
|
308
|
+
end
|
309
|
+
when 'CONVERTED'
|
310
|
+
if item['read_conversion'] or item['states']
|
311
|
+
results << [target_name, orig_packet_name, item_name, 'CONVERTED'].join('__')
|
312
|
+
else
|
313
|
+
results << [target_name, orig_packet_name, item_name, 'RAW'].join('__')
|
314
|
+
end
|
315
|
+
else # RAW or unknown
|
316
|
+
results << [target_name, orig_packet_name, item_name, 'RAW'].join('__')
|
317
|
+
end
|
318
|
+
|
319
|
+
# Tack on __LIMITS to notify that we have an available limits value
|
320
|
+
if item['limits']['DEFAULT']
|
321
|
+
results[-1] += '__LIMITS'
|
322
|
+
end
|
323
|
+
rescue RuntimeError => e
|
324
|
+
results << nil
|
325
|
+
end
|
326
|
+
end
|
327
|
+
results
|
328
|
+
end
|
329
|
+
|
257
330
|
# Returns all the item values (along with their limits state). The items
|
258
331
|
# can be from any target and packet and thus must be fully qualified with
|
259
332
|
# their target and packet names.
|
@@ -264,26 +337,32 @@ module OpenC3
|
|
264
337
|
# @return [Array<Object, Symbol>]
|
265
338
|
# Array consisting of the item value and limits state
|
266
339
|
# given as symbols such as :RED, :YELLOW, :STALE
|
267
|
-
def get_tlm_values(items, stale_time: 30, cache_timeout: nil, manual: false, scope: $openc3_scope, token: $openc3_token)
|
268
|
-
if !items.is_a?(Array)
|
340
|
+
def get_tlm_values(items, stale_time: 30, cache_timeout: nil, manual: false, start_time: nil, end_time: nil, scope: $openc3_scope, token: $openc3_token)
|
341
|
+
if !items.is_a?(Array)
|
269
342
|
raise ArgumentError, "items must be array of strings: ['TGT__PKT__ITEM__TYPE', ...]"
|
270
343
|
end
|
271
344
|
packets = []
|
272
345
|
cvt_items = []
|
273
346
|
items.each_with_index do |item, index|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
347
|
+
if item.nil?
|
348
|
+
# null items mean that it doesn't exist
|
349
|
+
cvt_items[index] = nil
|
350
|
+
else
|
351
|
+
item_upcase = item.to_s.upcase
|
352
|
+
target_name, packet_name, item_name, value_type, limits = item_upcase.split('__')
|
353
|
+
raise ArgumentError, "items must be formatted as TGT__PKT__ITEM__TYPE" if target_name.nil? || packet_name.nil? || item_name.nil? || value_type.nil?
|
354
|
+
if packet_name == 'LATEST' # Lookup packet_name in case of LATEST
|
355
|
+
packet_name = CvtModel.determine_latest_packet_for_item(target_name, item_name, cache_timeout: cache_timeout, scope: scope)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
cvt_items[index] = [target_name, packet_name, item_name, value_type, limits]
|
280
359
|
packets << [target_name, packet_name]
|
281
360
|
end
|
282
361
|
packets.uniq!
|
283
362
|
packets.each do |target_name, packet_name|
|
284
363
|
authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, manual: manual, scope: scope, token: token)
|
285
364
|
end
|
286
|
-
CvtModel.get_tlm_values(cvt_items, stale_time: stale_time, cache_timeout: cache_timeout, scope: scope)
|
365
|
+
CvtModel.get_tlm_values(cvt_items, stale_time: stale_time, cache_timeout: cache_timeout, start_time: start_time, end_time: end_time, scope: scope)
|
287
366
|
end
|
288
367
|
|
289
368
|
# Returns an array of all the telemetry packet hashes
|
@@ -374,16 +453,16 @@ module OpenC3
|
|
374
453
|
raise ArgumentError, "packets must be nested array: [['TGT','PKT'],...]"
|
375
454
|
end
|
376
455
|
|
377
|
-
|
456
|
+
results = {}
|
378
457
|
packets.each do |target_name, packet_name|
|
379
458
|
target_name = target_name.upcase
|
380
459
|
packet_name = packet_name.upcase
|
381
460
|
authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, manual: manual, scope: scope, token: token)
|
382
461
|
topic = "#{scope}__DECOM__{#{target_name}}__#{packet_name}"
|
383
462
|
id, _ = Topic.get_newest_message(topic)
|
384
|
-
|
463
|
+
results[topic] = id ? id : '0-0'
|
385
464
|
end
|
386
|
-
|
465
|
+
results.to_a.join(SUBSCRIPTION_DELIMITER)
|
387
466
|
end
|
388
467
|
# Alias the singular as well since that matches COSMOS 4
|
389
468
|
alias subscribe_packet subscribe_packets
|
@@ -14,10 +14,10 @@
|
|
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 2025, 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
|
# OpenC3 specific additions to the Ruby Kernel module
|
@@ -40,6 +40,6 @@ module Kernel
|
|
40
40
|
# @param start [Integer] The number of stack entries to skip
|
41
41
|
# @return [Symbol] The name of the calling method
|
42
42
|
def calling_method(start = 1)
|
43
|
-
caller[start][
|
43
|
+
caller[start][/[`']([^']*)'/, 1].intern
|
44
44
|
end
|
45
45
|
end
|
@@ -319,19 +319,23 @@ module OpenC3
|
|
319
319
|
begin
|
320
320
|
@file.close unless @file.closed?
|
321
321
|
Logger.debug "Log File Closed : #{@filename}"
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
322
|
+
# Only try to moce the file if we've written data to it
|
323
|
+
# This is indicated by the first and last timestamps being set
|
324
|
+
if @first_time and @last_time
|
325
|
+
date = first_timestamp[0..7] # YYYYMMDD
|
326
|
+
bucket_key = File.join(@remote_log_directory, date, bucket_filename())
|
327
|
+
# Cleanup timestamps here so they are unset for the next file
|
328
|
+
@first_time = nil
|
329
|
+
@last_time = nil
|
330
|
+
threads << BucketUtilities.move_log_file_to_bucket(@filename, bucket_key)
|
331
|
+
# Now that the file is in storage, trim the Redis stream after a delay
|
332
|
+
@cleanup_offsets << {}
|
333
|
+
@last_offsets.each do |redis_topic, last_offset|
|
334
|
+
@cleanup_offsets[-1][redis_topic] = last_offset
|
335
|
+
end
|
336
|
+
@cleanup_times << (Time.now + CLEANUP_DELAY)
|
337
|
+
@last_offsets.clear
|
332
338
|
end
|
333
|
-
@cleanup_times << (Time.now + CLEANUP_DELAY)
|
334
|
-
@last_offsets.clear
|
335
339
|
rescue Exception => e
|
336
340
|
Logger.error "Error closing #{@filename} : #{e.formatted}"
|
337
341
|
end
|
@@ -43,8 +43,8 @@ module OpenC3
|
|
43
43
|
# Fortify: Process Control
|
44
44
|
# This is dangerous! However, plugins need to be able to run whatever they want.
|
45
45
|
# Only admins can install plugins and they need to be vetted for content.
|
46
|
-
# NOTE: In
|
47
|
-
# footprint is much smaller. In
|
46
|
+
# NOTE: In Enterprise each microservice gets its own container so the potential
|
47
|
+
# footprint is much smaller. In Core you're in the same container
|
48
48
|
# as all the other plugins.
|
49
49
|
exec(*@config["cmd"])
|
50
50
|
end
|