openc3 7.0.0.pre.rc3 → 7.0.1
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 +58 -10
- data/bin/pipinstall +38 -6
- data/data/config/command_modifiers.yaml +1 -0
- data/data/config/interface_modifiers.yaml +1 -1
- data/data/config/item_modifiers.yaml +20 -7
- data/data/config/table_parameter_modifiers.yaml +3 -1
- data/data/config/telemetry.yaml +1 -1
- data/lib/openc3/accessors/json_accessor.rb +1 -1
- data/lib/openc3/accessors/template_accessor.rb +9 -0
- data/lib/openc3/api/tlm_api.rb +3 -3
- data/lib/openc3/config/config_parser.rb +4 -4
- data/lib/openc3/conversions/conversion.rb +3 -3
- data/lib/openc3/core_ext/faraday.rb +4 -0
- data/lib/openc3/interfaces/interface.rb +1 -6
- data/lib/openc3/logs/log_writer.rb +24 -6
- data/lib/openc3/logs/packet_log_writer.rb +1 -4
- data/lib/openc3/logs/stream_log_pair.rb +11 -4
- data/lib/openc3/logs/text_log_writer.rb +1 -4
- data/lib/openc3/microservices/decom_microservice.rb +1 -1
- data/lib/openc3/microservices/interface_decom_common.rb +22 -8
- data/lib/openc3/microservices/interface_microservice.rb +14 -3
- data/lib/openc3/microservices/log_microservice.rb +7 -2
- data/lib/openc3/microservices/microservice.rb +10 -4
- data/lib/openc3/microservices/queue_microservice.rb +3 -0
- data/lib/openc3/microservices/scope_cleanup_microservice.rb +116 -1
- data/lib/openc3/microservices/text_log_microservice.rb +4 -1
- data/lib/openc3/migrations/20260204000000_remove_decom_reducer.rb +2 -0
- data/lib/openc3/models/activity_model.rb +15 -3
- data/lib/openc3/models/cvt_model.rb +2 -247
- data/lib/openc3/models/plugin_model.rb +9 -1
- data/lib/openc3/models/plugin_store_model.rb +1 -1
- data/lib/openc3/models/python_package_model.rb +1 -1
- data/lib/openc3/models/reaction_model.rb +27 -9
- data/lib/openc3/models/script_engine_model.rb +1 -1
- data/lib/openc3/models/target_model.rb +32 -34
- data/lib/openc3/models/tool_model.rb +18 -5
- data/lib/openc3/models/trigger_model.rb +25 -8
- data/lib/openc3/models/widget_model.rb +1 -2
- data/lib/openc3/operators/operator.rb +9 -7
- data/lib/openc3/packets/json_packet.rb +2 -0
- data/lib/openc3/packets/packet.rb +1 -0
- data/lib/openc3/packets/packet_config.rb +28 -12
- data/lib/openc3/script/api_shared.rb +39 -2
- data/lib/openc3/script/calendar.rb +40 -10
- data/lib/openc3/script/extract.rb +46 -13
- data/lib/openc3/script/script.rb +19 -0
- data/lib/openc3/script/storage.rb +6 -6
- data/lib/openc3/system/system.rb +6 -6
- data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +0 -2
- data/lib/openc3/top_level.rb +15 -63
- data/lib/openc3/topics/decom_interface_topic.rb +19 -4
- data/lib/openc3/topics/interface_topic.rb +21 -2
- data/lib/openc3/topics/limits_event_topic.rb +1 -1
- data/lib/openc3/utilities/bucket_utilities.rb +3 -1
- data/lib/openc3/utilities/cli_generator.rb +7 -0
- data/lib/openc3/utilities/cmd_log.rb +1 -1
- data/lib/openc3/utilities/ctrf.rb +231 -0
- data/lib/openc3/utilities/local_mode.rb +3 -0
- data/lib/openc3/utilities/process_manager.rb +1 -1
- data/lib/openc3/utilities/python_proxy.rb +11 -4
- data/lib/openc3/utilities/questdb_client.rb +739 -22
- data/lib/openc3/utilities/running_script.rb +25 -7
- data/lib/openc3/utilities/script.rb +452 -0
- data/lib/openc3/utilities/secrets.rb +1 -1
- data/lib/openc3/version.rb +6 -6
- data/templates/conversion/conversion.py +0 -8
- data/templates/conversion/conversion.rb +0 -11
- 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 -4
- data/templates/widget/package.json +2 -2
- metadata +17 -2
- data/lib/openc3/migrations/20251022000000_remove_unique_id.rb +0 -23
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 36f12f777f56b7d64a19cfd89b8886352b6f0fb06bbda59d95d5643b8d4c71f6
|
|
4
|
+
data.tar.gz: 1ebffbb1399279e391a47dde42e6f8b9492be10b389c8e63d148aab402ee614c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ee971acf5bfd378f5a238263b467385286997900bea7fe549b8458c707568fb3a2265b261ee8794601bd42db7d0c2e8a983cf3d81a52c0c5069b9be0b41c4966
|
|
7
|
+
data.tar.gz: 5ba0ea7fb684a6e3e029864289612975db9555d1754f30f65baf4cf3193208516ea74029bb889b4575014b7953c45990c7e515dcf8f43d292bf8fc844de15a6b
|
data/bin/openc3cli
CHANGED
|
@@ -75,6 +75,7 @@ def print_usage
|
|
|
75
75
|
puts " cli pkguninstall PKGFILENAME SCOPE # Uninstall loaded package (Ruby gem or python package)"
|
|
76
76
|
puts " cli xtce_converter # Convert to and from the XTCE format. Run with --help for more info."
|
|
77
77
|
puts " cli cstol_converter # Converts CSTOL files (.prc) to COSMOS. Run with --help for more info."
|
|
78
|
+
puts " cli setpassword # Set the initial password from OPENC3_API_PASSWORD env var"
|
|
78
79
|
puts ""
|
|
79
80
|
end
|
|
80
81
|
|
|
@@ -588,35 +589,44 @@ def run_bridge(filename, params)
|
|
|
588
589
|
end
|
|
589
590
|
end
|
|
590
591
|
|
|
591
|
-
def cli_script_monitor(script_id)
|
|
592
|
+
def cli_script_monitor(script_id, format: 'text')
|
|
592
593
|
ret_code = ERROR_CODE
|
|
593
594
|
require 'openc3/script'
|
|
595
|
+
require 'openc3/utilities/ctrf'
|
|
594
596
|
begin
|
|
595
597
|
OpenC3::RunningScriptWebSocketApi.new(id: script_id) do |api|
|
|
596
598
|
while (resp = api.read) do
|
|
597
599
|
# see ScriptRunner.vue for types and states
|
|
598
600
|
case resp['type']
|
|
599
601
|
when 'file'
|
|
600
|
-
puts "Filename #{resp['filename']} scope #{resp['scope']}"
|
|
602
|
+
puts "Filename #{resp['filename']} scope #{resp['scope']}" unless format == 'ctrf'
|
|
601
603
|
when 'line'
|
|
602
604
|
fn = resp['filename'].nil? ? '<no file>' : resp['filename']
|
|
603
|
-
puts "At [#{fn}:#{resp['line_no']}] state [#{resp['state']}]"
|
|
605
|
+
puts "At [#{fn}:#{resp['line_no']}] state [#{resp['state']}]" unless format == 'ctrf'
|
|
604
606
|
if resp['state'] == 'error' or resp['state'] == 'crashed'
|
|
605
607
|
$script_interrupt_text = ''
|
|
606
|
-
puts 'script failed'
|
|
608
|
+
puts 'script failed' unless format == 'ctrf'
|
|
607
609
|
break
|
|
608
610
|
end
|
|
609
611
|
when 'output'
|
|
610
|
-
puts resp['line']
|
|
612
|
+
puts resp['line'] unless format == 'ctrf'
|
|
611
613
|
when 'complete'
|
|
612
614
|
$script_interrupt_text = ''
|
|
613
|
-
|
|
615
|
+
if resp['report']
|
|
616
|
+
if format == 'ctrf'
|
|
617
|
+
ctrf_data = OpenC3::Ctrf.convert_report(resp['report'])
|
|
618
|
+
puts JSON.pretty_generate(ctrf_data)
|
|
619
|
+
else
|
|
620
|
+
puts resp['report']
|
|
621
|
+
end
|
|
622
|
+
end
|
|
623
|
+
puts 'script complete' unless format == 'ctrf'
|
|
614
624
|
ret_code = 0
|
|
615
625
|
break
|
|
616
626
|
# These conditions are all handled by the else
|
|
617
627
|
# when 'running', 'breakpoint', 'waiting', 'time'
|
|
618
628
|
else
|
|
619
|
-
puts resp.pretty_inspect
|
|
629
|
+
puts resp.pretty_inspect unless format == 'ctrf'
|
|
620
630
|
end
|
|
621
631
|
end
|
|
622
632
|
end
|
|
@@ -692,10 +702,10 @@ def cli_script_run(args, options)
|
|
|
692
702
|
puts id
|
|
693
703
|
$script_interrupt_text = " Script #{args[1]} still running remotely.\n" # for Ctrl-C
|
|
694
704
|
if (options[:wait] < 1) then
|
|
695
|
-
ret_code = cli_script_monitor(id)
|
|
705
|
+
ret_code = cli_script_monitor(id, format: options[:format])
|
|
696
706
|
else
|
|
697
707
|
Timeout::timeout(options[:wait], nil, "--wait #{options[:wait]} exceeded") do
|
|
698
|
-
ret_code = cli_script_monitor(id)
|
|
708
|
+
ret_code = cli_script_monitor(id, format: options[:format])
|
|
699
709
|
rescue Timeout::ExitException, Timeout::Error => e
|
|
700
710
|
# Timeout exceptions are also raised by the Websocket API, so we check
|
|
701
711
|
if e.message =~ /^--wait /
|
|
@@ -795,7 +805,7 @@ rescue => e
|
|
|
795
805
|
end
|
|
796
806
|
|
|
797
807
|
def cli_script(args=[])
|
|
798
|
-
options = {scope: 'DEFAULT', disconnect: false, wait: 0, verbose: false}
|
|
808
|
+
options = {scope: 'DEFAULT', disconnect: false, wait: 0, verbose: false, format: 'text'}
|
|
799
809
|
option_parser = OptionParser.new do |opts|
|
|
800
810
|
opts.banner = "Usage: script --scope SCOPE [init | list | spawn | run]\n" +
|
|
801
811
|
" init Initialize running scripts (Enterprise Only)\n" +
|
|
@@ -812,6 +822,9 @@ def cli_script(args=[])
|
|
|
812
822
|
opts.on("--scope SCOPE", "Run with specified scope (default = DEFAULT)") do |arg|
|
|
813
823
|
options[:scope] = arg
|
|
814
824
|
end
|
|
825
|
+
opts.on("--format FORMAT", "Output format: text or ctrf (default = text)") do |arg|
|
|
826
|
+
options[:format] = arg
|
|
827
|
+
end
|
|
815
828
|
opts.on("--suite SUITE", "Run with specified suite") do |arg|
|
|
816
829
|
options[:suite] = arg
|
|
817
830
|
end
|
|
@@ -892,6 +905,25 @@ def cli_script(args=[])
|
|
|
892
905
|
exit(ret_code)
|
|
893
906
|
end
|
|
894
907
|
|
|
908
|
+
def set_password
|
|
909
|
+
password = ENV['OPENC3_API_PASSWORD']
|
|
910
|
+
argon2_profile = ENV["OPENC3_ARGON2_PROFILE"]&.to_sym || :rfc_9106_low_memory
|
|
911
|
+
if password.nil? or password.empty?
|
|
912
|
+
abort "OPENC3_API_PASSWORD environment variable is required"
|
|
913
|
+
end
|
|
914
|
+
if password.length < 8
|
|
915
|
+
abort "Password must be at least 8 characters"
|
|
916
|
+
end
|
|
917
|
+
redis = Redis.new(url: $redis_url, username: ENV['OPENC3_REDIS_USERNAME'], password: ENV['OPENC3_REDIS_PASSWORD'])
|
|
918
|
+
if redis.exists('OPENC3__TOKEN') == 1
|
|
919
|
+
abort "Password has already been set. Use the web interface to change the password."
|
|
920
|
+
end
|
|
921
|
+
pw_hash = Argon2::Password.create(password, profile: argon2_profile)
|
|
922
|
+
redis.set('OPENC3__TOKEN', pw_hash)
|
|
923
|
+
puts "Password set successfully."
|
|
924
|
+
exit 0
|
|
925
|
+
end
|
|
926
|
+
|
|
895
927
|
def migrate_password_hash
|
|
896
928
|
password = ENV['OPENC3_API_PASSWORD']
|
|
897
929
|
argon2_profile = ENV["OPENC3_ARGON2_PROFILE"]&.to_sym || :rfc_9106_low_memory
|
|
@@ -1377,6 +1409,22 @@ if not ARGV[0].nil? # argument(s) given
|
|
|
1377
1409
|
end
|
|
1378
1410
|
run_migrations(ARGV[1])
|
|
1379
1411
|
|
|
1412
|
+
when 'setpassword'
|
|
1413
|
+
if ARGV[1] == '--help' || ARGV[1] == '-h'
|
|
1414
|
+
puts "Usage: cli setpassword"
|
|
1415
|
+
puts ""
|
|
1416
|
+
puts "Set the initial COSMOS password from the OPENC3_API_PASSWORD environment variable."
|
|
1417
|
+
puts "This allows you to skip the password creation screen in the web interface."
|
|
1418
|
+
puts ""
|
|
1419
|
+
puts "The password must be at least 8 characters. This command will fail if a"
|
|
1420
|
+
puts "password has already been set."
|
|
1421
|
+
puts ""
|
|
1422
|
+
puts "Options:"
|
|
1423
|
+
puts " -h, --help Show this help message"
|
|
1424
|
+
exit 0
|
|
1425
|
+
end
|
|
1426
|
+
set_password()
|
|
1427
|
+
|
|
1380
1428
|
when 'migratepassword'
|
|
1381
1429
|
migrate_password_hash()
|
|
1382
1430
|
|
data/bin/pipinstall
CHANGED
|
@@ -1,13 +1,45 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
|
-
uv venv "$PYTHONUSERBASE"
|
|
2
|
+
uv venv "$PYTHONUSERBASE" --allow-existing
|
|
3
3
|
echo "uv pip install $@"
|
|
4
4
|
uv pip install --python "$PYTHONUSERBASE" "$@"
|
|
5
5
|
if [ $? -eq 0 ]; then
|
|
6
6
|
echo "Command succeeded"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
exit 0
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
# Collect the last arg and all preceding args
|
|
11
|
+
LAST_ARG=""
|
|
12
|
+
OPTS=""
|
|
13
|
+
PREV=""
|
|
14
|
+
for ARG in "$@"; do
|
|
15
|
+
if [ -n "$PREV" ]; then
|
|
16
|
+
OPTS="${OPTS} ${PREV}"
|
|
17
|
+
fi
|
|
18
|
+
PREV="$ARG"
|
|
19
|
+
done
|
|
20
|
+
LAST_ARG="$PREV"
|
|
21
|
+
|
|
22
|
+
# If last arg is a directory with pyproject.toml, the build may have failed
|
|
23
|
+
# (e.g. Poetry package-mode = false). Try compiling and installing declared
|
|
24
|
+
# dependencies only, without attempting to build the package itself.
|
|
25
|
+
if [ -d "$LAST_ARG" ] && [ -f "$LAST_ARG/pyproject.toml" ]; then
|
|
26
|
+
echo "Warning: Failed to build Python package, attempting to install declared dependencies from pyproject.toml"
|
|
27
|
+
TMPFILE=$(mktemp)
|
|
28
|
+
uv pip compile ${OPTS} "${LAST_ARG}/pyproject.toml" > "$TMPFILE"
|
|
29
|
+
if [ $? -eq 0 ] && [ -s "$TMPFILE" ]; then
|
|
30
|
+
uv pip install --python "$PYTHONUSERBASE" ${OPTS} -r "$TMPFILE"
|
|
31
|
+
if [ $? -eq 0 ]; then
|
|
32
|
+
echo "Dependencies installed successfully"
|
|
33
|
+
rm -f "$TMPFILE"
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
12
36
|
fi
|
|
37
|
+
rm -f "$TMPFILE"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
echo "Warning: Install failed - retrying with --no-index"
|
|
41
|
+
uv pip install --python "$PYTHONUSERBASE" --no-index "$@"
|
|
42
|
+
if [ $? -ne 0 ]; then
|
|
43
|
+
echo "ERROR: uv pip install failed"
|
|
44
|
+
exit 1
|
|
13
45
|
fi
|
|
@@ -199,6 +199,7 @@ HIDDEN:
|
|
|
199
199
|
summary: Hides this command from all OpenC3 tools such as Command Sender and Handbook Creator
|
|
200
200
|
description: Hidden commands do not appear in the Script Runner popup helper when writing scripts.
|
|
201
201
|
The command still exists in the system and can be sent by scripts.
|
|
202
|
+
since: 6.10.1
|
|
202
203
|
DISABLED:
|
|
203
204
|
summary: Disables this command from being sent
|
|
204
205
|
description: Hides the command and also disables it from being sent by scripts.
|
|
@@ -53,7 +53,7 @@ RECONNECT_DELAY:
|
|
|
53
53
|
parameters:
|
|
54
54
|
- name: Delay
|
|
55
55
|
required: true
|
|
56
|
-
description: Delay in seconds between reconnect attempts. The default is
|
|
56
|
+
description: Delay in seconds between reconnect attempts. The default is 5 seconds.
|
|
57
57
|
values: ([0-9]*[.])?[0-9]+
|
|
58
58
|
DISABLE_DISCONNECT:
|
|
59
59
|
summary: Disable the Disconnect button on the Interfaces tab in the Server
|
|
@@ -72,7 +72,8 @@ GENERIC_READ_CONVERSION_START:
|
|
|
72
72
|
class (Note, referencing the packet as 'myself' is still supported for backwards
|
|
73
73
|
compatibility). The last line of code should return the converted
|
|
74
74
|
value. The GENERIC_READ_CONVERSION_END keyword specifies that all lines of
|
|
75
|
-
code for the conversion have been given.
|
|
75
|
+
code for the conversion have been given. To specify the bit size, type, and array size of the converted data,
|
|
76
|
+
use the CONVERTED_DATA keyword.
|
|
76
77
|
warning: Generic conversions are not a good long term solution. Consider creating
|
|
77
78
|
a conversion class and using READ_CONVERSION instead. READ_CONVERSION is easier
|
|
78
79
|
to debug and has higher performance.
|
|
@@ -81,22 +82,33 @@ GENERIC_READ_CONVERSION_START:
|
|
|
81
82
|
GENERIC_READ_CONVERSION_START
|
|
82
83
|
(value * 1.5).to_i # Convert the value by a scale factor
|
|
83
84
|
GENERIC_READ_CONVERSION_END
|
|
85
|
+
CONVERTED_DATA 32 UINT
|
|
84
86
|
python_example: |
|
|
85
87
|
APPEND_ITEM ITEM1 32 UINT
|
|
86
88
|
GENERIC_READ_CONVERSION_START
|
|
87
89
|
int(value * 1.5) # Convert the value by a scale factor
|
|
88
90
|
GENERIC_READ_CONVERSION_END
|
|
91
|
+
CONVERTED_DATA 32 UINT
|
|
92
|
+
GENERIC_READ_CONVERSION_END:
|
|
93
|
+
summary: Complete a generic read conversion
|
|
94
|
+
CONVERTED_DATA:
|
|
95
|
+
summary: Defines the bit size, type, and array size of the converted data for a read conversion
|
|
96
|
+
description: This keyword is used in conjunction with DERIVED items to specify the bit size, type, and array size of the converted data.
|
|
97
|
+
If this keyword is not used, DERIVED items are stored as strings in the decommutated data.
|
|
98
|
+
since: 7.0.0
|
|
89
99
|
parameters:
|
|
100
|
+
- name: Converted Bit Size
|
|
101
|
+
required: true
|
|
102
|
+
description: Bit size of converted value
|
|
103
|
+
values: \d+
|
|
90
104
|
- name: Converted Type
|
|
91
|
-
required:
|
|
105
|
+
required: true
|
|
92
106
|
description: Type of the converted value
|
|
93
107
|
values: <%= %w(INT UINT FLOAT STRING BLOCK) %>
|
|
94
|
-
- name: Converted
|
|
108
|
+
- name: Converted Array Size
|
|
95
109
|
required: false
|
|
96
|
-
description: Bit size of converted value
|
|
110
|
+
description: Bit size of the total array if the converted value is an array. Only specified if the converted type is an array.
|
|
97
111
|
values: \d+
|
|
98
|
-
GENERIC_READ_CONVERSION_END:
|
|
99
|
-
summary: Complete a generic read conversion
|
|
100
112
|
LIMITS:
|
|
101
113
|
summary: Defines a set of limits for a telemetry item
|
|
102
114
|
description: If limits are violated a message is printed in the Command and Telemetry Server
|
|
@@ -180,4 +192,5 @@ HIDDEN:
|
|
|
180
192
|
summary: Hides this item from all the OpenC3 tools
|
|
181
193
|
description: This item will not appear in PacketViewer or Item Choosers.
|
|
182
194
|
It also hides this item from appearing in the Script Runner popup helper
|
|
183
|
-
when writing scripts. The item will also not be included in decom data.
|
|
195
|
+
when writing scripts. The item will also not be included in decom data.
|
|
196
|
+
since: 6.10.1
|
|
@@ -3,7 +3,9 @@ HIDDEN:
|
|
|
3
3
|
summary: Indicates that the parameter should not be shown to the user in the Table Manager GUI
|
|
4
4
|
description: Hidden parameters still exist and will be saved to the resulting
|
|
5
5
|
binary. This is useful for padding and other essential but non-user editable fields.
|
|
6
|
+
since: 6.10.1
|
|
6
7
|
UNEDITABLE:
|
|
7
8
|
summary: Indicates that the parameter should be shown to the user but not editable.
|
|
8
|
-
description:
|
|
9
|
+
description:
|
|
10
|
+
Uneditable parameters are useful for control fields which the user
|
|
9
11
|
may be interested in but should not be able to edit.
|
data/data/config/telemetry.yaml
CHANGED
|
@@ -20,7 +20,7 @@ require 'openc3/accessors/accessor'
|
|
|
20
20
|
OpenC3.disable_warnings do
|
|
21
21
|
class JsonPath
|
|
22
22
|
def self.process_object(obj_or_str, opts = {})
|
|
23
|
-
obj_or_str.is_a?(String) ?
|
|
23
|
+
obj_or_str.is_a?(String) ? JSON.parse(obj_or_str, max_nesting: opts[:max_nesting], create_additions: true, allow_nan: true) : obj_or_str
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
end
|
|
@@ -53,6 +53,9 @@ module OpenC3
|
|
|
53
53
|
return nil if item.data_type == :DERIVED
|
|
54
54
|
configure()
|
|
55
55
|
|
|
56
|
+
# No template items to read (e.g. command with fixed template string)
|
|
57
|
+
return nil if @item_keys.empty?
|
|
58
|
+
|
|
56
59
|
# Scan the response for all the variables in brackets <VARIABLE>
|
|
57
60
|
values = buffer.scan(@read_regexp)[0]
|
|
58
61
|
if !values || (values.length != @item_keys.length)
|
|
@@ -73,6 +76,12 @@ module OpenC3
|
|
|
73
76
|
result = {}
|
|
74
77
|
configure()
|
|
75
78
|
|
|
79
|
+
# No template items to read (e.g. command with fixed template string)
|
|
80
|
+
if @item_keys.empty?
|
|
81
|
+
items.each { |item| result[item.name] = nil }
|
|
82
|
+
return result
|
|
83
|
+
end
|
|
84
|
+
|
|
76
85
|
# Scan the response for all the variables in brackets <VARIABLE>
|
|
77
86
|
values = buffer.scan(@read_regexp)[0]
|
|
78
87
|
if !values || (values.length != @item_keys.length)
|
data/lib/openc3/api/tlm_api.rb
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
require 'openc3/models/target_model'
|
|
25
25
|
require 'openc3/models/cvt_model'
|
|
26
|
-
require 'openc3/packets/packet'
|
|
26
|
+
# require 'openc3/packets/packet' # Circular require
|
|
27
27
|
require 'openc3/topics/telemetry_topic'
|
|
28
28
|
require 'openc3/topics/interface_topic'
|
|
29
29
|
require 'openc3/topics/decom_interface_topic'
|
|
@@ -281,7 +281,7 @@ module OpenC3
|
|
|
281
281
|
|
|
282
282
|
case value_type
|
|
283
283
|
when 'FORMATTED', 'WITH_UNITS'
|
|
284
|
-
if item['format_string']
|
|
284
|
+
if item['format_string'] or item['units']
|
|
285
285
|
results << [target_name, orig_packet_name, item_name, 'FORMATTED'].join('__')
|
|
286
286
|
# This logic must match the logic in Packet#decom
|
|
287
287
|
elsif item['states'] or (item['read_conversion'] and item['data_type'] != 'DERIVED')
|
|
@@ -304,7 +304,7 @@ module OpenC3
|
|
|
304
304
|
if item['limits']['DEFAULT']
|
|
305
305
|
results[-1] += '__LIMITS'
|
|
306
306
|
end
|
|
307
|
-
rescue RuntimeError
|
|
307
|
+
rescue RuntimeError
|
|
308
308
|
results << nil
|
|
309
309
|
end
|
|
310
310
|
end
|
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
# This file may also be used under the terms of a commercial license
|
|
16
16
|
# if purchased from OpenC3, Inc.
|
|
17
17
|
|
|
18
|
-
require 'openc3/top_level'
|
|
18
|
+
# require 'openc3/top_level' # Circular require
|
|
19
19
|
require 'openc3/ext/config_parser' if RUBY_ENGINE == 'ruby' and !ENV['OPENC3_NO_EXT']
|
|
20
20
|
require 'erb'
|
|
21
21
|
require 'fileutils'
|
|
22
|
+
require 'tempfile'
|
|
22
23
|
|
|
23
24
|
module OpenC3
|
|
24
25
|
# Reads OpenC3 style configuration data which consists of keywords followed
|
|
@@ -218,6 +219,7 @@ module OpenC3
|
|
|
218
219
|
&)
|
|
219
220
|
ensure
|
|
220
221
|
file.close unless file.closed?
|
|
222
|
+
file.unlink
|
|
221
223
|
end
|
|
222
224
|
end
|
|
223
225
|
|
|
@@ -417,9 +419,7 @@ module OpenC3
|
|
|
417
419
|
elsif copy.include?(':') # Check for Windows drive letter
|
|
418
420
|
copy = copy.split(':')[1]
|
|
419
421
|
end
|
|
420
|
-
|
|
421
|
-
FileUtils.mkdir_p(File.dirname(parsed_filename)) # Create the path
|
|
422
|
-
file = File.open(parsed_filename, 'w+')
|
|
422
|
+
file = Tempfile.new(copy)
|
|
423
423
|
file.puts output
|
|
424
424
|
file.rewind # Rewind so the file is ready to read
|
|
425
425
|
file
|
|
@@ -20,11 +20,11 @@ module OpenC3
|
|
|
20
20
|
class Conversion
|
|
21
21
|
# @return [Symbol] The converted data type. Must be one of
|
|
22
22
|
# {OpenC3::StructureItem#data_type}
|
|
23
|
-
|
|
23
|
+
attr_accessor :converted_type
|
|
24
24
|
# @return [Integer] The size in bits of the converted value
|
|
25
|
-
|
|
25
|
+
attr_accessor :converted_bit_size
|
|
26
26
|
# @return [Integer] The size in bits of the converted array value
|
|
27
|
-
|
|
27
|
+
attr_accessor :converted_array_size
|
|
28
28
|
# @return [Array] The arguments passed to the conversion
|
|
29
29
|
attr_reader :params
|
|
30
30
|
|
|
@@ -295,17 +295,12 @@ module OpenC3
|
|
|
295
295
|
else
|
|
296
296
|
data, extra = protocol.read_data(data)
|
|
297
297
|
end
|
|
298
|
-
protocol.read_protocol_output_base(data, extra) unless blank_test
|
|
298
|
+
protocol.read_protocol_output_base(data, extra) unless blank_test or data == :STOP or data == :DISCONNECT
|
|
299
299
|
if data == :DISCONNECT
|
|
300
300
|
Logger.info("#{@name}: Protocol #{protocol.class} read_data requested disconnect")
|
|
301
301
|
return nil
|
|
302
302
|
end
|
|
303
303
|
break if data == :STOP
|
|
304
|
-
if blank_test
|
|
305
|
-
# This means the blank test returned something so we can log
|
|
306
|
-
protocol.read_protocol_input_base('', nil)
|
|
307
|
-
protocol.read_protocol_output_base(data, extra)
|
|
308
|
-
end
|
|
309
304
|
end
|
|
310
305
|
next if data == :STOP
|
|
311
306
|
|
|
@@ -126,6 +126,7 @@ module OpenC3
|
|
|
126
126
|
@cleanup_times = []
|
|
127
127
|
@previous_time_nsec_since_epoch = nil
|
|
128
128
|
@tmp_dir = Dir.mktmpdir
|
|
129
|
+
@wait_threads = []
|
|
129
130
|
|
|
130
131
|
# This is an optimization to avoid creating a new entry object
|
|
131
132
|
# each time we create an entry which we do a LOT!
|
|
@@ -154,9 +155,8 @@ module OpenC3
|
|
|
154
155
|
|
|
155
156
|
# Stops all logging and closes the current log file.
|
|
156
157
|
def stop
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
return threads
|
|
158
|
+
@mutex.synchronize { close_file(false); @logging_enabled = false; }
|
|
159
|
+
return @wait_threads
|
|
160
160
|
end
|
|
161
161
|
|
|
162
162
|
# Stop all logging, close the current log file, and kill the logging threads.
|
|
@@ -173,6 +173,13 @@ module OpenC3
|
|
|
173
173
|
return threads
|
|
174
174
|
end
|
|
175
175
|
|
|
176
|
+
def cleanup
|
|
177
|
+
if @tmp_dir
|
|
178
|
+
FileUtils.remove_entry_secure(@tmp_dir, true)
|
|
179
|
+
@tmp_dir = nil
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
176
183
|
def graceful_kill
|
|
177
184
|
@cancel_threads = true
|
|
178
185
|
end
|
|
@@ -307,8 +314,19 @@ module OpenC3
|
|
|
307
314
|
# to keep a full file's worth of data in the stream. This is what prevents continuous stream growth.
|
|
308
315
|
# Returns thread that moves log to bucket
|
|
309
316
|
def close_file(take_mutex = true)
|
|
310
|
-
threads = []
|
|
311
317
|
@mutex.lock if take_mutex
|
|
318
|
+
|
|
319
|
+
# Remove old wait_threads
|
|
320
|
+
to_remove = []
|
|
321
|
+
@wait_threads.each do |thread|
|
|
322
|
+
unless thread.alive?
|
|
323
|
+
to_remove << thread
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
to_remove.each do |thread|
|
|
327
|
+
@wait_threads.delete(thread)
|
|
328
|
+
end
|
|
329
|
+
|
|
312
330
|
begin
|
|
313
331
|
if @file
|
|
314
332
|
begin
|
|
@@ -322,7 +340,7 @@ module OpenC3
|
|
|
322
340
|
# Cleanup timestamps here so they are unset for the next file
|
|
323
341
|
@first_time = nil
|
|
324
342
|
@last_time = nil
|
|
325
|
-
|
|
343
|
+
@wait_threads << BucketUtilities.move_log_file_to_bucket(@filename, bucket_key)
|
|
326
344
|
# Now that the file is in storage, trim the Redis stream after a delay
|
|
327
345
|
@cleanup_offsets << {}
|
|
328
346
|
@last_offsets.each do |redis_topic, last_offset|
|
|
@@ -342,7 +360,7 @@ module OpenC3
|
|
|
342
360
|
ensure
|
|
343
361
|
@mutex.unlock if take_mutex
|
|
344
362
|
end
|
|
345
|
-
return
|
|
363
|
+
return @wait_threads
|
|
346
364
|
end
|
|
347
365
|
|
|
348
366
|
def bucket_filename
|
|
@@ -137,7 +137,6 @@ module OpenC3
|
|
|
137
137
|
# Closing a log file isn't critical so we just log an error
|
|
138
138
|
# Returns threads that moves log to bucket
|
|
139
139
|
def close_file(take_mutex = true)
|
|
140
|
-
threads = []
|
|
141
140
|
@mutex.lock if take_mutex
|
|
142
141
|
begin
|
|
143
142
|
# Need to write the OFFSET_MARKER for each packet
|
|
@@ -145,12 +144,10 @@ module OpenC3
|
|
|
145
144
|
write_entry(:OFFSET_MARKER, nil, nil, nil, nil, nil, last_offset + ',' + redis_topic, nil) if @file
|
|
146
145
|
end
|
|
147
146
|
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
return super(false)
|
|
150
148
|
ensure
|
|
151
149
|
@mutex.unlock if take_mutex
|
|
152
150
|
end
|
|
153
|
-
return threads
|
|
154
151
|
end
|
|
155
152
|
|
|
156
153
|
def get_packet_index(cmd_or_tlm, target_name, packet_name, entry_type, data)
|
|
@@ -43,13 +43,20 @@ module OpenC3
|
|
|
43
43
|
|
|
44
44
|
# Close any open stream log files
|
|
45
45
|
def stop
|
|
46
|
-
@read_log.stop
|
|
47
|
-
@write_log.stop
|
|
46
|
+
threads = @read_log.stop
|
|
47
|
+
threads.concat(@write_log.stop)
|
|
48
|
+
return threads
|
|
48
49
|
end
|
|
49
50
|
|
|
50
51
|
def shutdown
|
|
51
|
-
@read_log.shutdown
|
|
52
|
-
@write_log.shutdown
|
|
52
|
+
threads = @read_log.shutdown
|
|
53
|
+
threads.concat(@write_log.shutdown)
|
|
54
|
+
return threads
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def cleanup
|
|
58
|
+
@read_log.cleanup
|
|
59
|
+
@write_log.cleanup
|
|
53
60
|
end
|
|
54
61
|
|
|
55
62
|
# Clone the stream log pair
|
|
@@ -68,7 +68,6 @@ module OpenC3
|
|
|
68
68
|
# Closing a log file isn't critical so we just log an error
|
|
69
69
|
# Returns threads that moves log to bucket
|
|
70
70
|
def close_file(take_mutex = true)
|
|
71
|
-
threads = []
|
|
72
71
|
@mutex.lock if take_mutex
|
|
73
72
|
begin
|
|
74
73
|
# Need to write the OFFSET_MARKER for each packet
|
|
@@ -79,12 +78,10 @@ module OpenC3
|
|
|
79
78
|
write_entry(time.to_nsec_from_epoch, data.as_json(allow_nan: true).to_json(allow_nan: true)) if @file
|
|
80
79
|
end
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
return super(false)
|
|
84
82
|
ensure
|
|
85
83
|
@mutex.unlock if take_mutex
|
|
86
84
|
end
|
|
87
|
-
return threads
|
|
88
85
|
end
|
|
89
86
|
|
|
90
87
|
def extension
|
|
@@ -116,7 +116,7 @@ module OpenC3
|
|
|
116
116
|
microservice_cmd(topic, msg_id, msg_hash, redis)
|
|
117
117
|
elsif topic =~ /__DECOMINTERFACE/
|
|
118
118
|
if msg_hash.key?('inject_tlm')
|
|
119
|
-
|
|
119
|
+
handle_inject_tlm_with_ack(msg_hash['inject_tlm'], msg_id)
|
|
120
120
|
next
|
|
121
121
|
end
|
|
122
122
|
if msg_hash.key?('build_cmd')
|
|
@@ -34,10 +34,26 @@ module OpenC3
|
|
|
34
34
|
packet.received_time = Time.now.sys
|
|
35
35
|
packet.received_count = TargetModel.increment_telemetry_count(packet.target_name, packet.packet_name, 1, scope: @scope)
|
|
36
36
|
TelemetryTopic.write_packet(packet, scope: @scope)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def handle_inject_tlm_with_ack(inject_tlm_json, msg_id)
|
|
40
|
+
inject_tlm_hash = JSON.parse(inject_tlm_json, allow_nan: true, create_additions: true)
|
|
41
|
+
target_name = inject_tlm_hash['target_name']
|
|
42
|
+
ack_topic = "{#{@scope}__ACKCMD}TARGET__#{target_name}"
|
|
43
|
+
begin
|
|
44
|
+
handle_inject_tlm(inject_tlm_json)
|
|
45
|
+
msg_hash = {
|
|
46
|
+
id: msg_id,
|
|
47
|
+
result: 'SUCCESS'
|
|
48
|
+
}
|
|
49
|
+
rescue => error
|
|
50
|
+
@logger.error "inject_tlm error due to #{error.message}"
|
|
51
|
+
msg_hash = {
|
|
52
|
+
id: msg_id,
|
|
53
|
+
result: error.message
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
Topic.write_topic(ack_topic, msg_hash)
|
|
41
57
|
end
|
|
42
58
|
|
|
43
59
|
def handle_build_cmd(build_cmd_json, msg_id)
|
|
@@ -65,8 +81,7 @@ module OpenC3
|
|
|
65
81
|
rescue => error
|
|
66
82
|
msg_hash = {
|
|
67
83
|
id: msg_id,
|
|
68
|
-
result:
|
|
69
|
-
message: error.message
|
|
84
|
+
result: error.message
|
|
70
85
|
}
|
|
71
86
|
end
|
|
72
87
|
Topic.write_topic(ack_topic, msg_hash)
|
|
@@ -97,8 +112,7 @@ module OpenC3
|
|
|
97
112
|
rescue => error
|
|
98
113
|
msg_hash = {
|
|
99
114
|
id: msg_id,
|
|
100
|
-
result:
|
|
101
|
-
message: error.message
|
|
115
|
+
result: error.message
|
|
102
116
|
}
|
|
103
117
|
end
|
|
104
118
|
Topic.write_topic(ack_topic, msg_hash)
|