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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +58 -10
  3. data/bin/pipinstall +38 -6
  4. data/data/config/command_modifiers.yaml +1 -0
  5. data/data/config/interface_modifiers.yaml +1 -1
  6. data/data/config/item_modifiers.yaml +20 -7
  7. data/data/config/table_parameter_modifiers.yaml +3 -1
  8. data/data/config/telemetry.yaml +1 -1
  9. data/lib/openc3/accessors/json_accessor.rb +1 -1
  10. data/lib/openc3/accessors/template_accessor.rb +9 -0
  11. data/lib/openc3/api/tlm_api.rb +3 -3
  12. data/lib/openc3/config/config_parser.rb +4 -4
  13. data/lib/openc3/conversions/conversion.rb +3 -3
  14. data/lib/openc3/core_ext/faraday.rb +4 -0
  15. data/lib/openc3/interfaces/interface.rb +1 -6
  16. data/lib/openc3/logs/log_writer.rb +24 -6
  17. data/lib/openc3/logs/packet_log_writer.rb +1 -4
  18. data/lib/openc3/logs/stream_log_pair.rb +11 -4
  19. data/lib/openc3/logs/text_log_writer.rb +1 -4
  20. data/lib/openc3/microservices/decom_microservice.rb +1 -1
  21. data/lib/openc3/microservices/interface_decom_common.rb +22 -8
  22. data/lib/openc3/microservices/interface_microservice.rb +14 -3
  23. data/lib/openc3/microservices/log_microservice.rb +7 -2
  24. data/lib/openc3/microservices/microservice.rb +10 -4
  25. data/lib/openc3/microservices/queue_microservice.rb +3 -0
  26. data/lib/openc3/microservices/scope_cleanup_microservice.rb +116 -1
  27. data/lib/openc3/microservices/text_log_microservice.rb +4 -1
  28. data/lib/openc3/migrations/20260204000000_remove_decom_reducer.rb +2 -0
  29. data/lib/openc3/models/activity_model.rb +15 -3
  30. data/lib/openc3/models/cvt_model.rb +2 -247
  31. data/lib/openc3/models/plugin_model.rb +9 -1
  32. data/lib/openc3/models/plugin_store_model.rb +1 -1
  33. data/lib/openc3/models/python_package_model.rb +1 -1
  34. data/lib/openc3/models/reaction_model.rb +27 -9
  35. data/lib/openc3/models/script_engine_model.rb +1 -1
  36. data/lib/openc3/models/target_model.rb +32 -34
  37. data/lib/openc3/models/tool_model.rb +18 -5
  38. data/lib/openc3/models/trigger_model.rb +25 -8
  39. data/lib/openc3/models/widget_model.rb +1 -2
  40. data/lib/openc3/operators/operator.rb +9 -7
  41. data/lib/openc3/packets/json_packet.rb +2 -0
  42. data/lib/openc3/packets/packet.rb +1 -0
  43. data/lib/openc3/packets/packet_config.rb +28 -12
  44. data/lib/openc3/script/api_shared.rb +39 -2
  45. data/lib/openc3/script/calendar.rb +40 -10
  46. data/lib/openc3/script/extract.rb +46 -13
  47. data/lib/openc3/script/script.rb +19 -0
  48. data/lib/openc3/script/storage.rb +6 -6
  49. data/lib/openc3/system/system.rb +6 -6
  50. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +0 -2
  51. data/lib/openc3/top_level.rb +15 -63
  52. data/lib/openc3/topics/decom_interface_topic.rb +19 -4
  53. data/lib/openc3/topics/interface_topic.rb +21 -2
  54. data/lib/openc3/topics/limits_event_topic.rb +1 -1
  55. data/lib/openc3/utilities/bucket_utilities.rb +3 -1
  56. data/lib/openc3/utilities/cli_generator.rb +7 -0
  57. data/lib/openc3/utilities/cmd_log.rb +1 -1
  58. data/lib/openc3/utilities/ctrf.rb +231 -0
  59. data/lib/openc3/utilities/local_mode.rb +3 -0
  60. data/lib/openc3/utilities/process_manager.rb +1 -1
  61. data/lib/openc3/utilities/python_proxy.rb +11 -4
  62. data/lib/openc3/utilities/questdb_client.rb +739 -22
  63. data/lib/openc3/utilities/running_script.rb +25 -7
  64. data/lib/openc3/utilities/script.rb +452 -0
  65. data/lib/openc3/utilities/secrets.rb +1 -1
  66. data/lib/openc3/version.rb +6 -6
  67. data/templates/conversion/conversion.py +0 -8
  68. data/templates/conversion/conversion.rb +0 -11
  69. data/templates/tool_angular/package.json +2 -2
  70. data/templates/tool_react/package.json +1 -1
  71. data/templates/tool_svelte/package.json +1 -1
  72. data/templates/tool_vue/package.json +3 -4
  73. data/templates/widget/package.json +2 -2
  74. metadata +17 -2
  75. 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: 920a681652723ab469d3311e6f28332ea202068300c3a506d3314c552f5c0f30
4
- data.tar.gz: 2f35277fa1e25eb30646a27e8f45365cb2fd39d7d3b24758baabf004e9fe54b5
3
+ metadata.gz: 36f12f777f56b7d64a19cfd89b8886352b6f0fb06bbda59d95d5643b8d4c71f6
4
+ data.tar.gz: 1ebffbb1399279e391a47dde42e6f8b9492be10b389c8e63d148aab402ee614c
5
5
  SHA512:
6
- metadata.gz: 1cd5cb567a743e90742e76db3b4ab8498f34d8b6ba6dc0032e351bb6e3fdfbc4f8db826cc907d500f4670ef461bd49193be74e8264648b6a0f5908d090b7e6cf
7
- data.tar.gz: 9af5921a88413af21113b00c0f99dcc023e70c21b4dfc9e7f0f8840fbe226765f86d80e82a7b874458468c45cea590a3e6b0ab2d702d29613818e19e48b473be
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
- puts 'script complete'
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
- else
8
- echo "Command failed - retrying with --no-index"
9
- uv pip install --python "$PYTHONUSERBASE" --no-index "$@"
10
- if [ $? -ne 0 ]; then
11
- echo "ERROR: uv pip install failed"
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 15 seconds.
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: false
105
+ required: true
92
106
  description: Type of the converted value
93
107
  values: <%= %w(INT UINT FLOAT STRING BLOCK) %>
94
- - name: Converted Bit Size
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: Uneditable parameters are useful for control fields which the user
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.
@@ -10,7 +10,7 @@ TELEMETRY:
10
10
  required: true
11
11
  description: Name of the target this telemetry packet is associated with
12
12
  values: .+
13
- - name: Command
13
+ - name: Packet
14
14
  required: true
15
15
  description:
16
16
  Name of this telemetry packet. Also referred to as its mnemonic.
@@ -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) ? MultiJson.load(obj_or_str, max_nesting: opts[:max_nesting], create_additions: true, allow_nan: true) : obj_or_str
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)
@@ -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 => e
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
- parsed_filename = File.join(Dir.tmpdir, 'openc3', 'tmp', copy)
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
- attr_reader :converted_type
23
+ attr_accessor :converted_type
24
24
  # @return [Integer] The size in bits of the converted value
25
- attr_reader :converted_bit_size
25
+ attr_accessor :converted_bit_size
26
26
  # @return [Integer] The size in bits of the converted array value
27
- attr_reader :converted_array_size
27
+ attr_accessor :converted_array_size
28
28
  # @return [Array] The arguments passed to the conversion
29
29
  attr_reader :params
30
30
 
@@ -1,4 +1,8 @@
1
+ # Remove warnings in CGI
2
+ saved_verbose = $VERBOSE
3
+ $VERBOSE = false
1
4
  require 'faraday'
5
+ $VERBOSE = saved_verbose
2
6
 
3
7
  module Faraday
4
8
  class Response
@@ -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
- threads = nil
158
- @mutex.synchronize { threads = close_file(false); @logging_enabled = false; }
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
- threads << BucketUtilities.move_log_file_to_bucket(@filename, bucket_key)
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 threads
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
- threads.concat(super(false))
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
- threads.concat(super(false))
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
- handle_inject_tlm(msg_hash['inject_tlm'])
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
- # If the inject_tlm parameters are bad we rescue so
38
- # interface_microservice and decom_microservice can continue
39
- rescue => e
40
- @logger.error "inject_tlm error due to #{e.message}"
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: 'ERROR',
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: 'ERROR',
101
- message: error.message
115
+ result: error.message
102
116
  }
103
117
  end
104
118
  Topic.write_topic(ack_topic, msg_hash)