openc3 5.5.2 → 5.6.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.

Potentially problematic release.


This version of openc3 might be problematic. Click here for more details.

Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +167 -69
  3. data/data/config/_interfaces.yaml +1 -6
  4. data/data/config/interface_modifiers.yaml +55 -4
  5. data/data/config/microservice.yaml +30 -3
  6. data/ext/openc3/ext/crc/crc.c +82 -1
  7. data/lib/openc3/api/cmd_api.rb +19 -7
  8. data/lib/openc3/api/tlm_api.rb +13 -12
  9. data/lib/openc3/bridge/bridge_config.rb +4 -4
  10. data/lib/openc3/config/config_parser.rb +1 -0
  11. data/lib/openc3/conversions/unix_time_conversion.rb +3 -1
  12. data/lib/openc3/ext/.keep +0 -0
  13. data/lib/openc3/interfaces/interface.rb +54 -26
  14. data/lib/openc3/interfaces/serial_interface.rb +4 -5
  15. data/lib/openc3/interfaces/simulated_target_interface.rb +4 -4
  16. data/lib/openc3/interfaces/stream_interface.rb +2 -2
  17. data/lib/openc3/interfaces/tcpip_client_interface.rb +4 -3
  18. data/lib/openc3/interfaces/tcpip_server_interface.rb +18 -19
  19. data/lib/openc3/interfaces/udp_interface.rb +10 -4
  20. data/lib/openc3/io/json_api.rb +72 -0
  21. data/lib/openc3/io/serial_driver.rb +4 -5
  22. data/lib/openc3/logs/buffered_packet_log_writer.rb +2 -4
  23. data/lib/openc3/logs/log_writer.rb +9 -8
  24. data/lib/openc3/logs/packet_log_reader.rb +8 -1
  25. data/lib/openc3/logs/packet_log_writer.rb +3 -4
  26. data/lib/openc3/logs/stream_log.rb +116 -0
  27. data/lib/openc3/logs/stream_log_pair.rb +70 -0
  28. data/lib/openc3/microservices/cleanup_microservice.rb +1 -1
  29. data/lib/openc3/microservices/decom_microservice.rb +17 -2
  30. data/lib/openc3/microservices/interface_decom_common.rb +42 -0
  31. data/lib/openc3/microservices/interface_microservice.rb +24 -17
  32. data/lib/openc3/microservices/router_microservice.rb +46 -4
  33. data/lib/openc3/migrations/20221202214600_add_target_names.rb +1 -1
  34. data/lib/openc3/migrations/20230319154100_log_stream.rb +40 -0
  35. data/lib/openc3/migrations/20230413101100_remove_log.rb +30 -0
  36. data/lib/openc3/models/gem_model.rb +2 -2
  37. data/lib/openc3/models/interface_model.rb +13 -14
  38. data/lib/openc3/models/metadata_model.rb +1 -1
  39. data/lib/openc3/models/note_model.rb +1 -1
  40. data/lib/openc3/models/plugin_model.rb +3 -2
  41. data/lib/openc3/operators/operator.rb +2 -0
  42. data/lib/openc3/packets/commands.rb +2 -0
  43. data/lib/openc3/packets/packet_config.rb +3 -2
  44. data/lib/openc3/packets/parsers/xtce_converter.rb +2 -1
  45. data/lib/openc3/script/gems.rb +125 -0
  46. data/lib/openc3/script/plugins.rb +186 -0
  47. data/lib/openc3/script/screen.rb +119 -0
  48. data/lib/openc3/script/script.rb +3 -0
  49. data/lib/openc3/script/script_runner.rb +19 -8
  50. data/lib/openc3/script/suite_results.rb +2 -2
  51. data/lib/openc3/script/web_socket_api.rb +5 -1
  52. data/lib/openc3/streams/serial_stream.rb +14 -11
  53. data/lib/openc3/streams/tcpip_client_stream.rb +5 -2
  54. data/lib/openc3/streams/tcpip_socket_stream.rb +37 -71
  55. data/lib/openc3/streams/web_socket_client_stream.rb +5 -3
  56. data/lib/openc3/system/system.rb +2 -0
  57. data/lib/openc3/topics/interface_topic.rb +13 -4
  58. data/lib/openc3/topics/router_topic.rb +6 -6
  59. data/lib/openc3/topics/telemetry_decom_topic.rb +10 -1
  60. data/lib/openc3/utilities/bucket_utilities.rb +12 -5
  61. data/lib/openc3/utilities/cli_generator.rb +56 -4
  62. data/lib/openc3/utilities/crc.rb +42 -7
  63. data/lib/openc3/utilities/process_manager.rb +3 -1
  64. data/lib/openc3/utilities/ruby_lex_utils.rb +265 -504
  65. data/lib/openc3/version.rb +6 -6
  66. data/templates/conversion/conversion.rb +10 -2
  67. data/templates/microservice/microservices/TEMPLATE/microservice.rb +1 -1
  68. data/templates/plugin/Rakefile +8 -1
  69. data/templates/widget/.browserslistrc +16 -0
  70. data/templates/widget/.eslintrc.js +43 -0
  71. data/templates/widget/.nycrc +3 -0
  72. data/templates/widget/.prettierrc.js +5 -0
  73. data/templates/widget/LICENSE.txt +20 -0
  74. data/templates/widget/Rakefile +24 -0
  75. data/templates/widget/babel.config.json +11 -0
  76. data/templates/widget/package.json +35 -0
  77. data/templates/widget/src/Widget.vue +46 -0
  78. data/templates/widget/vue.config.js +25 -0
  79. data/templates/widget/yarn.lock +8938 -0
  80. metadata +23 -4
  81. data/lib/openc3/io/raw_logger.rb +0 -170
  82. data/lib/openc3/io/raw_logger_pair.rb +0 -80
@@ -40,7 +40,7 @@ module OpenC3
40
40
 
41
41
  def script_syntax_check(script, scope: $openc3_scope)
42
42
  endpoint = "/script-api/scripts/syntax"
43
- response = $script_runner_api_server.request('post', endpoint, :data => script, scope: scope)
43
+ response = $script_runner_api_server.request('post', endpoint, json: true, data: script, scope: scope)
44
44
  if response.nil? || response.code != 200
45
45
  _script_response_error(response, "Script syntax check request failed", scope: scope)
46
46
  else
@@ -64,13 +64,24 @@ module OpenC3
64
64
  end
65
65
  end
66
66
 
67
- def script_run(filename, disconnect: false, scope: $openc3_scope)
67
+ def script_run(filename, disconnect: false, environment: nil, scope: $openc3_scope)
68
68
  if disconnect
69
69
  endpoint = "/script-api/scripts/#{filename}/run/disconnect"
70
70
  else
71
71
  endpoint = "/script-api/scripts/#{filename}/run"
72
72
  end
73
- response = $script_runner_api_server.request('post', endpoint, scope: scope)
73
+
74
+ # Encode the environment hash into an array of key values
75
+ if environment && !environment.empty?
76
+ env_data = []
77
+ environment.map do |key, value|
78
+ env_data << { "key" => key, "value" => value }
79
+ end
80
+ else
81
+ env_data = []
82
+ end
83
+ # NOTE: json: true causes json_api_object to JSON generate and set the Content-Type to json
84
+ response = $script_runner_api_server.request('post', endpoint, json: true, data: { environment: env_data }, scope: scope)
74
85
  if response.nil? || response.code != 200
75
86
  _script_response_error(response, "Failed to run #{filename}", scope: scope)
76
87
  else
@@ -111,7 +122,7 @@ module OpenC3
111
122
 
112
123
  def script_instrumented(filename, script, scope: $openc3_scope)
113
124
  endpoint = "/script-api/scripts/#{filename}/instrumented"
114
- response = $script_runner_api_server.request('post', endpoint, :data => script, scope: scope)
125
+ response = $script_runner_api_server.request('post', endpoint, json: true, data: script, scope: scope)
115
126
  if response.nil? || response.code != 200
116
127
  _script_response_error(response, "Script instrumented request failed", scope: scope)
117
128
  else
@@ -127,7 +138,7 @@ module OpenC3
127
138
 
128
139
  def script_create(filename, script, breakpoints = [], scope: $openc3_scope)
129
140
  endpoint = "/script-api/scripts/#{filename}"
130
- response = $script_runner_api_server.request('post', endpoint, :data => {text: script, breakpoints: breakpoints}, scope: scope)
141
+ response = $script_runner_api_server.request('post', endpoint, json: true, data: { text: script, breakpoints: breakpoints }, scope: scope)
131
142
  if response.nil? || response.code != 200
132
143
  _script_response_error(response, "Script create request failed", scope: scope)
133
144
  else
@@ -205,7 +216,7 @@ module OpenC3
205
216
 
206
217
  def running_script_debug(id, debug_code, scope: $openc3_scope)
207
218
  endpoint = "/script-api/running-script/#{id}/debug"
208
- response = $script_runner_api_server.request('post', endpoint, data: {'args' => debug_code}, scope: scope)
219
+ response = $script_runner_api_server.request('post', endpoint, json: true, data: {'args' => debug_code}, scope: scope)
209
220
  if response.nil? || response.code != 200
210
221
  _script_response_error(response, "Running script debug request failed", scope: scope)
211
222
  else
@@ -216,9 +227,9 @@ module OpenC3
216
227
  def running_script_prompt(id, method_name, answer, prompt_id, password: nil, scope: $openc3_scope)
217
228
  endpoint = "/script-api/running-script/#{id}/prompt"
218
229
  if password
219
- response = $script_runner_api_server.request('post', endpoint, data: {'method' => method_name, 'answer' => answer, 'prompt_id' => prompt_id, 'password' => password}, scope: scope)
230
+ response = $script_runner_api_server.request('post', endpoint, json: true, data: {'method' => method_name, 'answer' => answer, 'prompt_id' => prompt_id, 'password' => password}, scope: scope)
220
231
  else
221
- response = $script_runner_api_server.request('post', endpoint, data: {'method' => method_name, 'answer' => answer, 'prompt_id' => prompt_id}, scope: scope)
232
+ response = $script_runner_api_server.request('post', endpoint, json: true, data: {'method' => method_name, 'answer' => answer, 'prompt_id' => prompt_id}, scope: scope)
222
233
  end
223
234
  if response.nil? || response.code != 200
224
235
  _script_response_error(response, "Running script prompt request failed", scope: scope)
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, 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
  module OpenC3
@@ -108,7 +108,7 @@ module OpenC3
108
108
  end
109
109
 
110
110
  def header
111
- @report << "--- Test Report ---"
111
+ @report << "--- Script Report ---"
112
112
  # @report << ''
113
113
  # if @metadata
114
114
  # begin
@@ -401,12 +401,16 @@ module OpenC3
401
401
  end
402
402
 
403
403
  # # Example Use
404
+ # # The following lines are only for outside of the COSMOS Docker or Kubernetes Cluster
405
+ # # Environment variables are already set inside of our containers
406
+ # # START OUTSIDE OF DOCKER ONLY
404
407
  # $openc3_scope = 'DEFAULT'
405
408
  # ENV['OPENC3_API_HOSTNAME'] = 'localhost'
406
409
  # ENV['OPENC3_API_PORT'] = '2900'
407
410
  # ENV['OPENC3_SCRIPT_API_HOSTNAME'] = 'localhost'
408
411
  # ENV['OPENC3_SCRIPT_API_PORT'] = '2900'
409
412
  # ENV['OPENC3_API_PASSWORD'] = 'password'
413
+ # # END OUTSIDE OF DOCKER ONLY
410
414
  #
411
415
  # OpenC3::StreamingWebSocketApi.new do |api|
412
416
  # api.add(items: ['DECOM__TLM__INST__HEALTH_STATUS__TEMP1__CONVERTED', 'DECOM__TLM__INST__HEALTH_STATUS__TEMP2__CONVERTED'])
@@ -420,4 +424,4 @@ end
420
424
  # end
421
425
  #
422
426
  # # Warning this saves all data to RAM. Do not use for large queries
423
- # data = OpenC3::StreamingWebSocketApi.read_all(items: ['DECOM__TLM__INST__HEALTH_STATUS__TEMP1__CONVERTED', 'DECOM__TLM__INST__HEALTH_STATUS__TEMP2__CONVERTED'], end_time: Time.now + 30)
427
+ # data = OpenC3::StreamingWebSocketApi.read_all(items: ['DECOM__TLM__INST__HEALTH_STATUS__TEMP1__CONVERTED', 'DECOM__TLM__INST__HEALTH_STATUS__TEMP2__CONVERTED'], start_time: Time.now - 30, end_time: Time.now + 30)
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, 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
  require 'thread' # For Mutex
@@ -38,15 +38,13 @@ module OpenC3
38
38
  # name is typically a device such as '/dev/ttyS0'.
39
39
  # @param baud_rate [Integer] The serial port baud rate
40
40
  # @param parity [Symbol] Must be :NONE, :EVEN, or :ODD
41
- # @param stop_bits [Integer] Number of stop bits. Must be 1 or 2.
42
- # @param write_timeout [Integer] Number of seconds to wait for the write to
43
- # complete. Pass nil to create no timeout. The {SerialDriver} will
44
- # continously try to send the data until it has been sent or an error
45
- # occurs.
46
- # @param read_timeout [Integer] Number of seconds to wait for the read to
47
- # complete. Pass nil to create no timeout. The {SerialDriver} will
48
- # continously try to read data until it has received data or an error
49
- # occurs.
41
+ # @param stop_bits [Integer] Stop bits. Must be 1 or 2.
42
+ # @param write_timeout [Integer] Seconds to wait for the write to complete.
43
+ # The {SerialDriver} will continously try to send the data until
44
+ # it has been sent or an error occurs.
45
+ # @param read_timeout [Integer] Seconds to wait for the read to complete.
46
+ # Pass nil to block until the read is complete. The {SerialDriver} will
47
+ # continously try to read data until it has received data or an error occurs.
50
48
  # @param flow_control [Symbol] Currently supported :NONE and :RTSCTS (default :NONE)
51
49
  # @param data_bits [Integer] Number of data bits (default 8)
52
50
  def initialize(write_port_name,
@@ -67,7 +65,12 @@ module OpenC3
67
65
  @parity = parity
68
66
  @stop_bits = stop_bits.to_i
69
67
  @write_timeout = ConfigParser.handle_nil(write_timeout)
70
- @write_timeout = @write_timeout.to_f if @write_timeout
68
+ if @write_timeout
69
+ @write_timeout = @write_timeout.to_f
70
+ else
71
+ Logger.instance.warn("Warning: To avoid interface lock, write_timeout can not be nil. Setting to 10 seconds.")
72
+ @write_timeout = 10.0
73
+ end
71
74
  @read_timeout = ConfigParser.handle_nil(read_timeout)
72
75
  @read_timeout = @read_timeout.to_f if @read_timeout
73
76
  @flow_control = flow_control.to_s.intern
@@ -36,8 +36,11 @@ module OpenC3
36
36
  # a read only stream.
37
37
  # @param read_port [Integer|nil] The port to read. Pass nil to make this
38
38
  # a write only stream.
39
- # @param write_timeout (see TcpipSocketStream#initialize)
40
- # @param read_timeout (see TcpipSocketStream#initialize)
39
+ # @param write_timeout [Float] Seconds to wait before aborting writes
40
+ # @param read_timeout [Float|nil] Seconds to wait before aborting reads.
41
+ # Pass nil to block until the read is complete.
42
+ # @param connect_timeout [Float|nil] Seconds to wait before aborting connect.
43
+ # Pass nil to block until the connection is complete.
41
44
  def initialize(hostname, write_port, read_port, write_timeout, read_timeout, connect_timeout = 5.0)
42
45
  @hostname = hostname
43
46
  if @hostname.to_s.upcase == 'LOCALHOST'
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, 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
  require 'socket'
@@ -31,21 +31,23 @@ module OpenC3
31
31
  class TcpipSocketStream < Stream
32
32
  attr_reader :write_socket
33
33
 
34
- FAST_READ = (RUBY_VERSION > "2.1")
35
-
36
34
  # @param write_socket [Socket] Socket to write
37
35
  # @param read_socket [Socket] Socket to read
38
- # @param write_timeout [Float|nil] Number of seconds to wait for the write
39
- # to complete or nil to block until the socket is ready to write.
40
- # @param read_timeout [Float|nil] Number of seconds to wait for the read
41
- # to complete or nil to block until the socket is ready to read.
36
+ # @param write_timeout [Float] Seconds to wait before aborting writes
37
+ # @param read_timeout [Float|nil] Seconds to wait before aborting reads.
38
+ # Pass nil to block until the read is complete.
42
39
  def initialize(write_socket, read_socket, write_timeout, read_timeout)
43
40
  super()
44
41
 
45
42
  @write_socket = write_socket
46
43
  @read_socket = read_socket
47
44
  @write_timeout = ConfigParser.handle_nil(write_timeout)
48
- @write_timeout = @write_timeout.to_f if @write_timeout
45
+ if @write_timeout
46
+ @write_timeout = @write_timeout.to_f
47
+ else
48
+ Logger.warn("Warning: To avoid interface lock, write_timeout can not be nil. Setting to 10 seconds.")
49
+ @write_timeout = 10.0
50
+ end
49
51
  @read_timeout = ConfigParser.handle_nil(read_timeout)
50
52
  @read_timeout = @read_timeout.to_f if @read_timeout
51
53
 
@@ -61,67 +63,37 @@ module OpenC3
61
63
  raise "Attempt to read from write only stream" unless @read_socket
62
64
 
63
65
  # No read mutex is needed because reads happen serially
64
- if FAST_READ
65
- begin
66
- while true # Loop until we get some data
67
- data = @read_socket.read_nonblock(65535, exception: false)
68
- raise EOFError, 'end of file reached' unless data
69
-
70
- if data == :wait_readable
71
- # Wait for the socket to be ready for reading or for the timeout
72
- begin
73
- result = IO.fast_select([@read_socket, @pipe_reader], nil, nil, @read_timeout)
74
- # If select returns something it means the socket is now available for
75
- # reading so retry the read. If it returns nil it means we timed out.
76
- # If the pipe is present that means we closed the socket
77
- if result
78
- if result.include?(@pipe_reader)
79
- raise IOError
80
- else
81
- next
82
- end
66
+ begin
67
+ while true # Loop until we get some data
68
+ data = @read_socket.read_nonblock(65535, exception: false)
69
+ raise EOFError, 'end of file reached' unless data
70
+
71
+ if data == :wait_readable
72
+ # Wait for the socket to be ready for reading or for the timeout
73
+ begin
74
+ result = IO.fast_select([@read_socket, @pipe_reader], nil, nil, @read_timeout)
75
+ # If select returns something it means the socket is now available for
76
+ # reading so retry the read. If it returns nil it means we timed out.
77
+ # If the pipe is present that means we closed the socket
78
+ if result
79
+ if result.include?(@pipe_reader)
80
+ raise IOError
83
81
  else
84
- raise Timeout::Error, "Read Timeout"
82
+ next
85
83
  end
86
- rescue IOError, Errno::ENOTSOCK
87
- # These can happen with the socket being closed while waiting on select
88
- data = ''
89
- end
90
- end
91
- break
92
- end
93
- rescue Errno::ECONNRESET, Errno::ECONNABORTED, IOError, Errno::ENOTSOCK
94
- data = ''
95
- end
96
- else
97
- begin
98
- data = @read_socket.read_nonblock(65535)
99
- rescue IO::WaitReadable
100
- # Wait for the socket to be ready for reading or for the timeout
101
- begin
102
- result = IO.fast_select([@read_socket, @pipe_reader], nil, nil, @read_timeout)
103
-
104
- # If select returns something it means the socket is now available for
105
- # reading so retry the read. If it returns nil it means we timed out.
106
- # If the pipe is present that means we closed the socket
107
- if result
108
- if result.include?(@pipe_reader)
109
- raise IOError
110
84
  else
111
- retry
85
+ raise Timeout::Error, "Read Timeout"
112
86
  end
113
- else
114
- raise Timeout::Error, "Read Timeout"
87
+ rescue IOError, Errno::ENOTSOCK
88
+ # These can happen with the socket being closed while waiting on select
89
+ data = ''
115
90
  end
116
- rescue IOError, Errno::ENOTSOCK
117
- # These can happen with the socket being closed while waiting on select
118
- data = ''
119
91
  end
120
- rescue Errno::ECONNRESET, Errno::ECONNABORTED, IOError, Errno::ENOTSOCK
121
- data = ''
92
+ break
122
93
  end
94
+ rescue Errno::ECONNRESET, Errno::ECONNABORTED, IOError, Errno::ENOTSOCK
95
+ data = ''
123
96
  end
124
-
125
97
  data
126
98
  end
127
99
 
@@ -129,18 +101,12 @@ module OpenC3
129
101
  def read_nonblock
130
102
  # No read mutex is needed because reads happen serially
131
103
  begin
132
- if FAST_READ
133
- data = @read_socket.read_nonblock(65535, exception: false)
134
- raise EOFError, 'end of file reached' unless data
135
-
136
- data = '' if data == :wait_readable
137
- else
138
- data = @read_socket.read_nonblock(65535)
139
- end
104
+ data = @read_socket.read_nonblock(65535, exception: false)
105
+ raise EOFError, 'end of file reached' unless data
106
+ data = '' if data == :wait_readable
140
107
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNRESET, Errno::ECONNABORTED, IOError
141
108
  data = ''
142
109
  end
143
-
144
110
  data
145
111
  end
146
112
 
@@ -194,5 +160,5 @@ module OpenC3
194
160
  @pipe_writer.write('.')
195
161
  @connected = false
196
162
  end
197
- end # class TcpipSocketStream
163
+ end
198
164
  end
@@ -26,9 +26,11 @@ module OpenC3
26
26
  attr_accessor :headers
27
27
 
28
28
  # @param url [String] The host to connect to
29
- # @param write_timeout (see TcpipSocketStream#initialize)
30
- # @param read_timeout (see TcpipSocketStream#initialize)
31
- # @param connect_timeout (see TcpipClientStream#initialize)
29
+ # @param write_timeout [Float] Seconds to wait before aborting writes
30
+ # @param read_timeout [Float|nil] Seconds to wait before aborting reads.
31
+ # Pass nil to block until the read is complete.
32
+ # @param connect_timeout [Float|nil] Seconds to wait before aborting connect.
33
+ # Pass nil to block until the connection is complete.
32
34
  def initialize(url, write_timeout, read_timeout, connect_timeout = 5.0)
33
35
  @url = url
34
36
  @uri = URI.parse @url
@@ -20,6 +20,7 @@
20
20
  # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
+ require 'openc3/top_level'
23
24
  require 'openc3/config/config_parser'
24
25
  require 'openc3/packets/packet_config'
25
26
  require 'openc3/packets/commands'
@@ -116,6 +117,7 @@ module OpenC3
116
117
  # @param target_names [Array of target names]
117
118
  # @param target_config_dir Directory where target config folders are
118
119
  def initialize(target_names, target_config_dir)
120
+ OpenC3.add_to_search_path(target_config_dir, true)
119
121
  @targets = {}
120
122
  @packet_config = PacketConfig.new
121
123
  @commands = Commands.new(@packet_config)
@@ -64,11 +64,11 @@ module OpenC3
64
64
  end
65
65
 
66
66
  def self.start_raw_logging(interface_name, scope:)
67
- Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'log_raw' => 'true' }, '*', 100)
67
+ Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'log_stream' => 'true' }, '*', 100)
68
68
  end
69
69
 
70
70
  def self.stop_raw_logging(interface_name, scope:)
71
- Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'log_raw' => 'false' }, '*', 100)
71
+ Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'log_stream' => 'false' }, '*', 100)
72
72
  end
73
73
 
74
74
  def self.shutdown(interface, scope:)
@@ -77,14 +77,14 @@ module OpenC3
77
77
  InterfaceTopic.clear_topics(InterfaceTopic.topics(interface, scope: scope))
78
78
  end
79
79
 
80
- def interface_cmd(interface_name, cmd_name, *cmd_params, scope:)
80
+ def self.interface_cmd(interface_name, cmd_name, *cmd_params, scope:)
81
81
  data = {}
82
82
  data['cmd_name'] = cmd_name
83
83
  data['cmd_params'] = cmd_params
84
84
  Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'interface_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
85
85
  end
86
86
 
87
- def protocol_cmd(interface_name, cmd_name, *cmd_params, read_write: :READ_WRITE, index: -1, scope:)
87
+ def self.protocol_cmd(interface_name, cmd_name, *cmd_params, read_write: :READ_WRITE, index: -1, scope:)
88
88
  data = {}
89
89
  data['cmd_name'] = cmd_name
90
90
  data['cmd_params'] = cmd_params
@@ -92,5 +92,14 @@ module OpenC3
92
92
  data['index'] = index
93
93
  Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'protocol_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
94
94
  end
95
+
96
+ def self.inject_tlm(interface_name, target_name, packet_name, item_hash = nil, type: :CONVERTED, scope:)
97
+ data = {}
98
+ data['target_name'] = target_name.to_s.upcase
99
+ data['packet_name'] = packet_name.to_s.upcase
100
+ data['item_hash'] = item_hash
101
+ data['type'] = type
102
+ Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'inject_tlm' => JSON.generate(data, allow_nan: true) }, '*', 100)
103
+ end
95
104
  end
96
105
  end
@@ -57,9 +57,9 @@ module OpenC3
57
57
  Topic.write_topic(topic, { 'target_name' => packet.target_name, 'cmd_name' => packet.packet_name, 'cmd_buffer' => packet.buffer(false) }, '*', 100)
58
58
  elsif target_names.length == 1
59
59
  topic = "{#{scope}__CMD}TARGET__#{target_names[0]}"
60
- Topic.write_topic(topic, { 'target_name' => packet.target_name, 'cmd_name' => 'UNKNOWN', 'cmd_buffer' => packet.buffer(false) }, '*', 100)
60
+ Topic.write_topic(topic, { 'target_name' => packet.target_name ? packet.target_name : 'UNKNOWN', 'cmd_name' => 'UNKNOWN', 'cmd_buffer' => packet.buffer(false) }, '*', 100)
61
61
  else
62
- raise "No route for command: #{packet.target_name} #{packet.packet_name}"
62
+ raise "No route for command: #{packet.target_name ? packet.target_name : 'UNKNOWN'} #{packet.packet_name ? packet.packet_name : 'UNKNOWN'}"
63
63
  end
64
64
  end
65
65
 
@@ -76,11 +76,11 @@ module OpenC3
76
76
  end
77
77
 
78
78
  def self.start_raw_logging(router_name, scope:)
79
- Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'log_raw' => 'true' }, '*', 100)
79
+ Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'log_stream' => 'true' }, '*', 100)
80
80
  end
81
81
 
82
82
  def self.stop_raw_logging(router_name, scope:)
83
- Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'log_raw' => 'false' }, '*', 100)
83
+ Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'log_stream' => 'false' }, '*', 100)
84
84
  end
85
85
 
86
86
  def self.shutdown(router, scope:)
@@ -89,14 +89,14 @@ module OpenC3
89
89
  RouterTopic.clear_topics(RouterTopic.topics(router, scope: scope))
90
90
  end
91
91
 
92
- def router_cmd(router_name, cmd_name, *cmd_params, scope:)
92
+ def self.router_cmd(router_name, cmd_name, *cmd_params, scope:)
93
93
  data = {}
94
94
  data['cmd_name'] = cmd_name
95
95
  data['cmd_params'] = cmd_params
96
96
  Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'router_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
97
97
  end
98
98
 
99
- def protocol_cmd(router_name, cmd_name, *cmd_params, read_write: :READ_WRITE, index: -1, scope:)
99
+ def self.protocol_cmd(router_name, cmd_name, *cmd_params, read_write: :READ_WRITE, index: -1, scope:)
100
100
  data = {}
101
101
  data['cmd_name'] = cmd_name
102
102
  data['cmd_params'] = cmd_params
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, 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
  require 'openc3/topics/topic'
@@ -56,5 +56,14 @@ module OpenC3
56
56
  end
57
57
  end
58
58
  end
59
+
60
+ def self.inject_tlm(target_name, packet_name, item_hash = nil, type: :CONVERTED, scope:)
61
+ data = {}
62
+ data['target_name'] = target_name.to_s.upcase
63
+ data['packet_name'] = packet_name.to_s.upcase
64
+ data['item_hash'] = item_hash
65
+ data['type'] = type
66
+ Topic.write_topic("#{scope}__DECOMINTERFACE__{#{target_name}}", { 'inject_tlm' => JSON.generate(data, allow_nan: true) }, '*', 100)
67
+ end
59
68
  end
60
69
  end
@@ -60,15 +60,22 @@ module OpenC3
60
60
  Thread.new do
61
61
  client = Bucket.getClient()
62
62
 
63
- zipped = compress_file(filename)
64
- bucket_key = bucket_key + '.gz'
65
- File.open(zipped, 'rb') do |read_file|
66
- client.put_object(bucket: ENV['OPENC3_LOGS_BUCKET'], key: bucket_key, body: read_file, metadata: metadata)
63
+ orig_filename = nil
64
+ if File.extname(filename) != '.txt'
65
+ orig_filename = filename
66
+ filename = compress_file(filename)
67
+ bucket_key += '.gz'
68
+ end
69
+ # We want to open this as a file and pass that to put_object to allow
70
+ # this to work with really large files. Otherwise the entire file has
71
+ # to be held in memory!
72
+ File.open(filename, 'rb') do |file|
73
+ client.put_object(bucket: ENV['OPENC3_LOGS_BUCKET'], key: bucket_key, body: file, metadata: metadata)
67
74
  end
68
75
  Logger.debug "wrote #{ENV['OPENC3_LOGS_BUCKET']}/#{bucket_key}"
69
76
  ReducerModel.add_file(bucket_key) # Record the new file for data reduction
70
77
 
71
- File.delete(zipped)
78
+ File.delete(orig_filename) if orig_filename
72
79
  File.delete(filename)
73
80
  rescue => err
74
81
  Logger.error("Error saving log file to bucket: #{filename}\n#{err.formatted}")
@@ -18,7 +18,7 @@
18
18
 
19
19
  module OpenC3
20
20
  class CliGenerator
21
- GENERATORS = %w(plugin target microservice conversion limits_response)
21
+ GENERATORS = %w(plugin target microservice widget conversion limits_response)
22
22
  TEMPLATES_DIR = "#{File.dirname(__FILE__)}/../../../templates"
23
23
 
24
24
  # Called by openc3cli with ARGV[1..-1]
@@ -43,11 +43,12 @@ module OpenC3
43
43
  end
44
44
 
45
45
  def self.process_template(template_dir, the_binding)
46
- Dir.glob("#{template_dir}/**/*").each do |file|
46
+ Dir.glob("#{template_dir}/**/*", File::FNM_DOTMATCH).each do |file|
47
+ next if File.basename(file) == '.'
47
48
  base_name = file.sub("#{template_dir}/", '')
48
- yield base_name
49
+ next if yield base_name
49
50
  if File.directory?(file)
50
- FileUtils.mkdir(base_name)
51
+ FileUtils.mkdir(base_name) unless File.exist?(base_name)
51
52
  next
52
53
  end
53
54
  output = ERB.new(File.read(file), trim_mode: "-").result(the_binding)
@@ -73,6 +74,7 @@ module OpenC3
73
74
 
74
75
  process_template("#{TEMPLATES_DIR}/plugin", binding) do |filename|
75
76
  filename.sub!("plugin.gemspec", "#{plugin_name}.gemspec")
77
+ false
76
78
  end
77
79
 
78
80
  puts "Plugin #{plugin_name} successfully generated!"
@@ -98,6 +100,7 @@ module OpenC3
98
100
  # Rename the template TARGET to our actual target named after the plugin
99
101
  filename.sub!("targets/TARGET", "targets/#{target_name}")
100
102
  filename.sub!("target.rb", target_lib_filename)
103
+ false
101
104
  end
102
105
 
103
106
  # Add this target to plugin.txt
@@ -134,6 +137,7 @@ module OpenC3
134
137
  # Rename the template MICROSERVICE to our actual microservice name
135
138
  filename.sub!("microservices/TEMPLATE", "microservices/#{microservice_name}")
136
139
  filename.sub!("microservice.rb", microservice_filename)
140
+ false
137
141
  end
138
142
 
139
143
  # Add this microservice to plugin.txt
@@ -149,6 +153,52 @@ module OpenC3
149
153
  return microservice_name
150
154
  end
151
155
 
156
+ def self.generate_widget(args)
157
+ if args.length != 2
158
+ abort("Usage: cli generate #{args[0]} <SuperdataWidget>")
159
+ end
160
+ # Per https://stackoverflow.com/a/47591707/453280
161
+ if args[1] !~ /.*Widget$/ or args[1][0...-6] != args[1][0...-6].capitalize
162
+ abort("Widget name should be Uppercase followed by Widget, e.g. SuperdataWidget. Found '#{args[1]}'.")
163
+ end
164
+
165
+ # Create the local variables
166
+ widget_name = args[1]
167
+ widget_filename = "#{widget_name}.vue"
168
+ widget_path = "src/#{widget_filename}"
169
+ if File.exist?(widget_path)
170
+ abort("Widget #{widget_path} already exists!")
171
+ end
172
+ skip_package = false
173
+ if File.exist?('package.json')
174
+ puts "package.json already exists ... you'll have to manually add this widget to the end of the \"build\" script."
175
+ skip_package = true
176
+ end
177
+
178
+ process_template("#{TEMPLATES_DIR}/widget", binding) do |filename|
179
+ if skip_package && filename == 'package.json'
180
+ true # causes the block to skip processing this file
181
+ elsif filename.include?('node_modules')
182
+ true
183
+ else
184
+ filename.sub!("Widget.vue", widget_filename)
185
+ false
186
+ end
187
+ end
188
+
189
+ # Add this widget to plugin.txt but remove Widget from the name
190
+ File.open("plugin.txt", 'a') do |file|
191
+ file.puts <<~DOC
192
+
193
+ WIDGET #{widget_name[0...-6]}
194
+ DOC
195
+ end
196
+
197
+ puts "Widget #{widget_name} successfully generated!"
198
+ puts "Please be sure #{widget_name} does not overlap an existing widget: https://openc3.com/docs/v5/telemetry-screens"
199
+ return widget_name
200
+ end
201
+
152
202
  def self.generate_conversion(args)
153
203
  if args.length != 3
154
204
  abort("Usage: cli generate conversion <TARGET> <NAME>")
@@ -170,6 +220,7 @@ module OpenC3
170
220
 
171
221
  process_template("#{TEMPLATES_DIR}/conversion", binding) do |filename|
172
222
  filename.sub!("conversion.rb", conversion_filename)
223
+ false
173
224
  end
174
225
 
175
226
  puts "Conversion #{conversion_filename} successfully generated!"
@@ -199,6 +250,7 @@ module OpenC3
199
250
 
200
251
  process_template("#{TEMPLATES_DIR}/limits_response", binding) do |filename|
201
252
  filename.sub!("response.rb", response_filename)
253
+ false
202
254
  end
203
255
 
204
256
  puts "Limits response #{response_filename} successfully generated!"