openc3 6.6.0 → 6.8.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 +77 -16
- 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/cmd_api.rb +15 -4
- 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 +96 -14
- data/lib/openc3/core_ext/kernel.rb +3 -3
- data/lib/openc3/logs/log_writer.rb +16 -12
- data/lib/openc3/microservices/interface_microservice.rb +14 -1
- data/lib/openc3/microservices/plugin_microservice.rb +2 -2
- data/lib/openc3/microservices/queue_microservice.rb +166 -0
- 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/queue_model.rb +232 -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 +4 -4
- data/lib/openc3/script/queue.rb +80 -0
- data/lib/openc3/script/script.rb +1 -0
- data/lib/openc3/script/script_runner.rb +7 -2
- data/lib/openc3/script/tables.rb +3 -3
- data/lib/openc3/script/web_socket_api.rb +11 -0
- data/lib/openc3/topics/queue_topic.rb +29 -0
- data/lib/openc3/utilities/authorization.rb +1 -1
- data/lib/openc3/utilities/cosmos_rails_formatter.rb +1 -1
- data/lib/openc3/utilities/local_mode.rb +2 -0
- 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 +83 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b3a2017659bdea9209911793ce6d8f53c28320df77e9c938e1a3ec6d0e88527
|
4
|
+
data.tar.gz: 950e4c86f740f303186262aa4c808f063219b7097e4b1a0c343aa83da9a71b48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6bcbc2924b4b7b0e7d961a0892f5e74fbb527998527f3c1ae1d7ba4c3213cbda5088864383c25bbebdeca01b09571efb596248d892fb513dc30181bbf376b1c
|
7
|
+
data.tar.gz: 0f42338482eb4069675bee41a29b0396ced047520a7300f7a627dcca2ff0fc483536448860f82e6e99a44a42b09ce64b261604fc35dd586b53ac764352ac9118
|
data/bin/openc3cli
CHANGED
@@ -24,17 +24,18 @@
|
|
24
24
|
# This file will handle OpenC3 tasks such as instantiating a new project
|
25
25
|
|
26
26
|
require 'openc3'
|
27
|
-
require 'openc3/
|
28
|
-
require 'openc3/utilities/bucket'
|
29
|
-
require 'openc3/utilities/cli_generator'
|
30
|
-
require 'openc3/models/scope_model'
|
31
|
-
require 'openc3/models/plugin_model'
|
27
|
+
require 'openc3/bridge/bridge'
|
32
28
|
require 'openc3/models/gem_model'
|
33
29
|
require 'openc3/models/migration_model'
|
30
|
+
require 'openc3/models/plugin_model'
|
34
31
|
require 'openc3/models/python_package_model'
|
32
|
+
require 'openc3/models/queue_model'
|
33
|
+
require 'openc3/models/scope_model'
|
35
34
|
require 'openc3/models/tool_model'
|
36
35
|
require 'openc3/packets/packet_config'
|
37
|
-
require 'openc3/
|
36
|
+
require 'openc3/utilities/bucket'
|
37
|
+
require 'openc3/utilities/cli_generator'
|
38
|
+
require 'openc3/utilities/local_mode'
|
38
39
|
require 'ostruct'
|
39
40
|
require 'optparse'
|
40
41
|
require 'openc3/utilities/zip'
|
@@ -63,8 +64,9 @@ def print_usage
|
|
63
64
|
puts " cli rake # Runs rake in the local directory"
|
64
65
|
puts " cli irb # Runs irb in the local directory"
|
65
66
|
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
|
67
|
+
puts " cli validate /PATH/FILENAME.gem SCOPE variables.json # Validate a COSMOS plugin gem file"
|
68
|
+
puts " cli load /PATH/FILENAME.gem SCOPE plugin_hash.json # Loads a COSMOS plugin gem file"
|
69
|
+
puts " OPTIONS: --variables lets you pass a path to a JSON file containing your plugin's variables"
|
68
70
|
puts " cli list <SCOPE> # Lists installed plugins, SCOPE is DEFAULT if not given"
|
69
71
|
puts " cli generate TYPE OPTIONS # Generate various COSMOS entities"
|
70
72
|
puts " OPTIONS: --ruby or --python is required to specify the language in the generated code unless OPENC3_LANGUAGE is set"
|
@@ -333,7 +335,7 @@ end
|
|
333
335
|
# Pass true as the last argument to force install even if a plugin with
|
334
336
|
# the same version number exists
|
335
337
|
#
|
336
|
-
def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil, force: false)
|
338
|
+
def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil, force: false, variables_file: nil)
|
337
339
|
scope ||= 'DEFAULT'
|
338
340
|
check_environment()
|
339
341
|
if $openc3_in_cluster
|
@@ -352,9 +354,10 @@ def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil, force: false)
|
|
352
354
|
end
|
353
355
|
|
354
356
|
begin
|
357
|
+
existing_variables = JSON.parse(File.read(variables_file)) if variables_file
|
355
358
|
if plugin_hash_file
|
356
359
|
# Admin Create / Edit / or Upgrade Plugin
|
357
|
-
OpenC3::PluginModel.install_phase1(plugin_file_path, scope: scope)
|
360
|
+
OpenC3::PluginModel.install_phase1(plugin_file_path, existing_variables: existing_variables, scope: scope)
|
358
361
|
plugin_hash = JSON.parse(File.read(plugin_hash_file), :allow_nan => true, :create_additions => true)
|
359
362
|
else
|
360
363
|
# Init or Command Line openc3cli load with no plugin_hash_file
|
@@ -378,7 +381,7 @@ def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil, force: false)
|
|
378
381
|
end
|
379
382
|
return if found
|
380
383
|
|
381
|
-
plugin_hash = OpenC3::PluginModel.install_phase1(plugin_file_path, scope: scope)
|
384
|
+
plugin_hash = OpenC3::PluginModel.install_phase1(plugin_file_path, existing_variables: existing_variables, scope: scope)
|
382
385
|
end
|
383
386
|
|
384
387
|
# Determine if plugin named in plugin_hash exists
|
@@ -640,12 +643,39 @@ def cli_script_list(args, options)
|
|
640
643
|
return 0
|
641
644
|
end
|
642
645
|
|
646
|
+
def parse_suite_runner_options(options)
|
647
|
+
suite_runner = nil
|
648
|
+
# suite must be given to enable Suite Runner execution
|
649
|
+
if options[:suite]
|
650
|
+
suite_runner = {}
|
651
|
+
suite_runner['suite'] = options[:suite]
|
652
|
+
if options[:group]
|
653
|
+
suite_runner['group'] = options[:group]
|
654
|
+
# script requires group to be set
|
655
|
+
if options[:script]
|
656
|
+
suite_runner['script'] = options[:script]
|
657
|
+
end
|
658
|
+
end
|
659
|
+
if options[:method]
|
660
|
+
suite_runner['method'] = options[:method]
|
661
|
+
else
|
662
|
+
suite_runner['method'] = 'start'
|
663
|
+
end
|
664
|
+
if options[:options]
|
665
|
+
suite_runner['options'] = options[:options].split(',')
|
666
|
+
else
|
667
|
+
suite_runner['options'] = ["continueAfterError"]
|
668
|
+
end
|
669
|
+
end
|
670
|
+
return suite_runner
|
671
|
+
end
|
672
|
+
|
643
673
|
def cli_script_run(args, options)
|
644
674
|
environment = get_env_from_args(args)
|
645
|
-
|
675
|
+
suite_runner = parse_suite_runner_options(options)
|
646
676
|
ret_code = ERROR_CODE
|
647
677
|
require 'openc3/script'
|
648
|
-
if (id = script_run(args[1], disconnect: options[:disconnect], environment: environment, scope: options[:scope]))
|
678
|
+
if (id = script_run(args[1], disconnect: options[:disconnect], environment: environment, suite_runner: suite_runner, scope: options[:scope]))
|
649
679
|
puts id
|
650
680
|
$script_interrupt_text = " Script #{args[1]} still running remotely.\n" # for Ctrl-C
|
651
681
|
if (options[:wait] < 1) then
|
@@ -673,10 +703,10 @@ end
|
|
673
703
|
|
674
704
|
def cli_script_spawn(args, options)
|
675
705
|
environment = get_env_from_args(args)
|
676
|
-
|
706
|
+
suite_runner = parse_suite_runner_options(options)
|
677
707
|
ret_code = ERROR_CODE
|
678
708
|
require 'openc3/script'
|
679
|
-
if (id = script_run(args[1], disconnect: options[:disconnect], environment: environment, scope: options[:scope]))
|
709
|
+
if (id = script_run(args[1], disconnect: options[:disconnect], environment: environment, suite_runner: suite_runner, scope: options[:scope]))
|
680
710
|
puts id
|
681
711
|
ret_code = 0
|
682
712
|
end
|
@@ -769,6 +799,22 @@ def cli_script(args=[])
|
|
769
799
|
opts.on("--scope SCOPE", "Run with specified scope (default = DEFAULT)") do |arg|
|
770
800
|
options[:scope] = arg
|
771
801
|
end
|
802
|
+
opts.on("--suite SUITE", "Run with specified suite") do |arg|
|
803
|
+
options[:suite] = arg
|
804
|
+
end
|
805
|
+
opts.on("--group GROUP", "Run with specified group") do |arg|
|
806
|
+
options[:group] = arg
|
807
|
+
end
|
808
|
+
opts.on("--script SCRIPT", "Run with specified script") do |arg|
|
809
|
+
options[:script] = arg
|
810
|
+
end
|
811
|
+
opts.on("--method start", "Method must be start, setup, or teardown. Default is start.") do |arg|
|
812
|
+
options[:method] = arg
|
813
|
+
end
|
814
|
+
# TODO 7.0: Should these all be snake case?
|
815
|
+
opts.on("--options options", "Options is a comma separated list consisting of continueAfterError,pauseOnError,abortAfterError,manual,loop,breakLoopOnError. Default is continueAfterError.") do |arg|
|
816
|
+
options[:options] = arg
|
817
|
+
end
|
772
818
|
opts.on("-d", "--disconnect", "Run a script in disconnect mode (default = false)") do |arg|
|
773
819
|
options[:disconnect] = arg
|
774
820
|
end
|
@@ -848,7 +894,18 @@ if not ARGV[0].nil? # argument(s) given
|
|
848
894
|
when 'load'
|
849
895
|
# force is a boolean so if they pass 'force' it is true
|
850
896
|
# See plugins_controller.rb install for usage
|
851
|
-
|
897
|
+
variables_option = ARGV.find_index('--variables')
|
898
|
+
if variables_option.nil?
|
899
|
+
scope = ARGV[2]
|
900
|
+
plugin_hash_file = ARGV[3]
|
901
|
+
force = ARGV[4] == 'force'
|
902
|
+
else
|
903
|
+
scope = ARGV[2] unless variables_option <= 2
|
904
|
+
plugin_hash_file = ARGV[3] unless variables_option <= 3
|
905
|
+
force = ARGV[4] == 'force' unless variables_option <= 4
|
906
|
+
variables_file = ARGV[variables_option + 1]
|
907
|
+
end
|
908
|
+
load_plugin(ARGV[1], scope: scope, plugin_hash_file: plugin_hash_file, force: force, variables_file: variables_file)
|
852
909
|
|
853
910
|
when 'list'
|
854
911
|
list_plugins(scope: ARGV[1])
|
@@ -959,6 +1016,10 @@ if not ARGV[0].nil? # argument(s) given
|
|
959
1016
|
end
|
960
1017
|
end
|
961
1018
|
|
1019
|
+
when 'createqueue'
|
1020
|
+
queue = OpenC3::QueueModel.new(name: ARGV[1], state: 'RELEASE', scope: ARGV[2])
|
1021
|
+
queue.create
|
1022
|
+
|
962
1023
|
when 'destroyscope'
|
963
1024
|
scope = OpenC3::ScopeModel.get_model(name: ARGV[1])
|
964
1025
|
scope.destroy
|
@@ -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
data/lib/openc3/api/cmd_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
|
@@ -24,6 +24,7 @@
|
|
24
24
|
# See https://github.com/OpenC3/cosmos/pull/1963
|
25
25
|
|
26
26
|
require 'openc3/api/interface_api'
|
27
|
+
require 'openc3/models/queue_model'
|
27
28
|
require 'openc3/models/target_model'
|
28
29
|
require 'openc3/topics/command_topic'
|
29
30
|
require 'openc3/topics/command_decom_topic'
|
@@ -452,7 +453,7 @@ module OpenC3
|
|
452
453
|
end
|
453
454
|
|
454
455
|
# NOTE: When adding new keywords to this method, make sure to update script/commands.rb
|
455
|
-
def _cmd_implementation(method_name, *args, range_check:, hazardous_check:, raw:, timeout: nil, log_message: nil, manual: false, validate: true,
|
456
|
+
def _cmd_implementation(method_name, *args, range_check:, hazardous_check:, raw:, timeout: nil, log_message: nil, manual: false, validate: true, queue: nil,
|
456
457
|
scope: $openc3_scope, token: $openc3_token, **kwargs)
|
457
458
|
extract_string_kwargs_to_args(args, kwargs)
|
458
459
|
unless [nil, true, false].include?(log_message)
|
@@ -538,9 +539,19 @@ module OpenC3
|
|
538
539
|
'log_message' => log_message.to_s,
|
539
540
|
'obfuscated_items' => packet['obfuscated_items'].to_s
|
540
541
|
}
|
541
|
-
|
542
|
+
# Users have to explicitly opt into a default queue by setting the OPENC3_DEFAULT_QUEUE
|
543
|
+
# At which point ALL commands will go to that queue unless they specifically opt out with queue: false
|
544
|
+
if ENV['OPENC3_DEFAULT_QUEUE'] && queue.nil?
|
545
|
+
queue = ENV['OPENC3_DEFAULT_QUEUE']
|
546
|
+
end
|
547
|
+
if queue
|
548
|
+
# Pull the command out of the script string, e.g. cmd("INST ABORT")
|
549
|
+
queued = cmd_string.split('("')[1].split('")')[0]
|
550
|
+
QueueModel.queue_command(queue, command: queued, username: username, scope: scope)
|
551
|
+
else
|
552
|
+
CommandTopic.send_command(command, timeout: timeout, scope: scope)
|
553
|
+
end
|
542
554
|
return command
|
543
555
|
end
|
544
|
-
|
545
556
|
end
|
546
557
|
end
|
@@ -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,81 @@ 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
|
+
# This logic must match the logic in Packet#decom
|
297
|
+
elsif item['states'] or (item['read_conversion'] and item['data_type'] != 'DERIVED')
|
298
|
+
results << [target_name, orig_packet_name, item_name, 'CONVERTED'].join('__')
|
299
|
+
else
|
300
|
+
results << [target_name, orig_packet_name, item_name, 'RAW'].join('__')
|
301
|
+
end
|
302
|
+
when 'FORMATTED'
|
303
|
+
if item['format_string']
|
304
|
+
results << [target_name, orig_packet_name, item_name, 'FORMATTED'].join('__')
|
305
|
+
# This logic must match the logic in Packet#decom
|
306
|
+
elsif item['states'] or (item['read_conversion'] and item['data_type'] != 'DERIVED')
|
307
|
+
results << [target_name, orig_packet_name, item_name, 'CONVERTED'].join('__')
|
308
|
+
else
|
309
|
+
results << [target_name, orig_packet_name, item_name, 'RAW'].join('__')
|
310
|
+
end
|
311
|
+
when 'CONVERTED'
|
312
|
+
# This logic must match the logic in Packet#decom
|
313
|
+
if item['states'] or (item['read_conversion'] and item['data_type'] != 'DERIVED')
|
314
|
+
results << [target_name, orig_packet_name, item_name, 'CONVERTED'].join('__')
|
315
|
+
else
|
316
|
+
results << [target_name, orig_packet_name, item_name, 'RAW'].join('__')
|
317
|
+
end
|
318
|
+
else # RAW or unknown
|
319
|
+
results << [target_name, orig_packet_name, item_name, 'RAW'].join('__')
|
320
|
+
end
|
321
|
+
|
322
|
+
# Tack on __LIMITS to notify that we have an available limits value
|
323
|
+
if item['limits']['DEFAULT']
|
324
|
+
results[-1] += '__LIMITS'
|
325
|
+
end
|
326
|
+
rescue RuntimeError => e
|
327
|
+
results << nil
|
328
|
+
end
|
329
|
+
end
|
330
|
+
results
|
331
|
+
end
|
332
|
+
|
257
333
|
# Returns all the item values (along with their limits state). The items
|
258
334
|
# can be from any target and packet and thus must be fully qualified with
|
259
335
|
# their target and packet names.
|
@@ -264,26 +340,32 @@ module OpenC3
|
|
264
340
|
# @return [Array<Object, Symbol>]
|
265
341
|
# Array consisting of the item value and limits state
|
266
342
|
# 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)
|
343
|
+
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)
|
344
|
+
if !items.is_a?(Array)
|
269
345
|
raise ArgumentError, "items must be array of strings: ['TGT__PKT__ITEM__TYPE', ...]"
|
270
346
|
end
|
271
347
|
packets = []
|
272
348
|
cvt_items = []
|
273
349
|
items.each_with_index do |item, index|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
350
|
+
if item.nil?
|
351
|
+
# null items mean that it doesn't exist
|
352
|
+
cvt_items[index] = nil
|
353
|
+
else
|
354
|
+
item_upcase = item.to_s.upcase
|
355
|
+
target_name, packet_name, item_name, value_type, limits = item_upcase.split('__')
|
356
|
+
raise ArgumentError, "items must be formatted as TGT__PKT__ITEM__TYPE" if target_name.nil? || packet_name.nil? || item_name.nil? || value_type.nil?
|
357
|
+
if packet_name == 'LATEST' # Lookup packet_name in case of LATEST
|
358
|
+
packet_name = CvtModel.determine_latest_packet_for_item(target_name, item_name, cache_timeout: cache_timeout, scope: scope)
|
359
|
+
end
|
360
|
+
end
|
361
|
+
cvt_items[index] = [target_name, packet_name, item_name, value_type, limits]
|
280
362
|
packets << [target_name, packet_name]
|
281
363
|
end
|
282
364
|
packets.uniq!
|
283
365
|
packets.each do |target_name, packet_name|
|
284
366
|
authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, manual: manual, scope: scope, token: token)
|
285
367
|
end
|
286
|
-
CvtModel.get_tlm_values(cvt_items, stale_time: stale_time, cache_timeout: cache_timeout, scope: scope)
|
368
|
+
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
369
|
end
|
288
370
|
|
289
371
|
# Returns an array of all the telemetry packet hashes
|
@@ -374,16 +456,16 @@ module OpenC3
|
|
374
456
|
raise ArgumentError, "packets must be nested array: [['TGT','PKT'],...]"
|
375
457
|
end
|
376
458
|
|
377
|
-
|
459
|
+
results = {}
|
378
460
|
packets.each do |target_name, packet_name|
|
379
461
|
target_name = target_name.upcase
|
380
462
|
packet_name = packet_name.upcase
|
381
463
|
authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, manual: manual, scope: scope, token: token)
|
382
464
|
topic = "#{scope}__DECOM__{#{target_name}}__#{packet_name}"
|
383
465
|
id, _ = Topic.get_newest_message(topic)
|
384
|
-
|
466
|
+
results[topic] = id ? id : '0-0'
|
385
467
|
end
|
386
|
-
|
468
|
+
results.to_a.join(SUBSCRIPTION_DELIMITER)
|
387
469
|
end
|
388
470
|
# Alias the singular as well since that matches COSMOS 4
|
389
471
|
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
|
@@ -298,7 +298,7 @@ module OpenC3
|
|
298
298
|
@interface.write(command)
|
299
299
|
|
300
300
|
command.obfuscate
|
301
|
-
|
301
|
+
|
302
302
|
if command.validator and validate
|
303
303
|
begin
|
304
304
|
result, reason = command.validator.post_check(command)
|
@@ -559,6 +559,19 @@ module OpenC3
|
|
559
559
|
target.interface = new_interface
|
560
560
|
end
|
561
561
|
@interface = new_interface
|
562
|
+
|
563
|
+
# Update the model
|
564
|
+
if @interface_or_router == 'INTERFACE'
|
565
|
+
interface_model = InterfaceModel.get(name: @interface.name, scope: @scope)
|
566
|
+
# config_params[0] is the filename so set the rest
|
567
|
+
interface_model['config_params'][1..-1] = *params
|
568
|
+
InterfaceModel.set(interface_model, scope: @scope)
|
569
|
+
else
|
570
|
+
router_model = RouterModel.get(name: @interface.name, scope: @scope)
|
571
|
+
# config_params[0] is the filename so set the rest
|
572
|
+
router_model['config_params'][1..-1] = *params
|
573
|
+
RouterModel.set(router_model, scope: @scope)
|
574
|
+
end
|
562
575
|
end
|
563
576
|
|
564
577
|
@interface.state = 'ATTEMPTING'
|
@@ -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
|