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
@@ -24,6 +24,7 @@ require 'openc3/io/stdout'
24
24
  require 'openc3/io/stderr'
25
25
  require 'childprocess'
26
26
  require 'openc3/script/suite_runner'
27
+ require 'openc3/utilities/script'
27
28
  require 'openc3/utilities/store'
28
29
  require 'openc3/utilities/store_queued'
29
30
  require 'openc3/utilities/bucket_require'
@@ -31,6 +32,7 @@ require 'openc3/models/offline_access_model'
31
32
  require 'openc3/models/environment_model'
32
33
  require 'openc3/models/script_engine_model'
33
34
  require 'openc3/models/script_status_model'
35
+ require 'fileutils'
34
36
 
35
37
  if not defined? RAILS_ROOT
36
38
  if ENV['RAILS_ROOT']
@@ -60,8 +62,8 @@ module OpenC3
60
62
  private
61
63
  # Define all the user input methods used in scripting which we need to broadcast to the frontend
62
64
  # Note: This list matches the list in run_script.rb:116
63
- SCRIPT_METHODS = %i[ask ask_string message_box vertical_message_box combo_box prompt prompt_for_hazardous
64
- prompt_for_critical_cmd metadata_input open_file_dialog open_files_dialog]
65
+ SCRIPT_METHODS = %i[ask ask_string message_box vertical_message_box combo_box check_box prompt prompt_for_hazardous
66
+ prompt_for_critical_cmd metadata_input open_file_dialog open_files_dialog open_bucket_dialog]
65
67
  SCRIPT_METHODS.each do |method|
66
68
  define_method(method) do |*args, **kwargs|
67
69
  while true
@@ -72,10 +74,21 @@ module OpenC3
72
74
  input = RunningScript.instance.user_input
73
75
  # All ask and prompt dialogs should include a 'Cancel' button
74
76
  # If they cancel we wait so they can potentially stop
75
- if input == 'Cancel'
77
+ if input == 'COSMOS__CANCEL'
76
78
  RunningScript.instance.perform_pause
77
79
  else
78
- if (method.to_s.include?('open_file'))
80
+ if method.to_s == 'open_bucket_dialog'
81
+ bucket = input['bucket']
82
+ path = input['path']
83
+ filename = input['filename']
84
+ # Path from bucket dialog already includes scope prefix (e.g. DEFAULT/targets/...)
85
+ # _get_storage_file prepends scope, so strip it from the path to avoid doubling
86
+ scope = RunningScript.instance.scope
87
+ path = path.sub(/\A#{Regexp.escape(scope)}\//, '')
88
+ file = _get_storage_file(path, bucket: bucket, scope: scope)
89
+ file.filename = filename
90
+ return file
91
+ elsif (method.to_s.include?('open_file'))
79
92
  files = input.map do |filename|
80
93
  file = _get_storage_file("tmp/#{filename}", scope: RunningScript.instance.scope)
81
94
  # Set filename method we added to Tempfile in the core_ext
@@ -377,7 +390,9 @@ class RunningScript
377
390
  scope = $openc3_scope
378
391
  tags = []
379
392
  end
380
- @@message_log = OpenC3::MessageLog.new("sr", File.join(RAILS_ROOT, 'log'), tags: tags, scope: scope)
393
+ log_dir = File.join(RAILS_ROOT, 'tmp', 'script_logs')
394
+ FileUtils.mkdir_p(log_dir)
395
+ @@message_log = OpenC3::MessageLog.new("sr", log_dir, tags: tags, scope: scope)
381
396
  end
382
397
 
383
398
  def message_log
@@ -422,7 +437,9 @@ class RunningScript
422
437
 
423
438
  process = ChildProcess.build(process_name, runner_path.to_s, running_script_id.to_s, scope)
424
439
  process.io.inherit! # Helps with debugging
425
- process.cwd = File.join(RAILS_ROOT, 'scripts')
440
+ script_cwd = File.join(RAILS_ROOT, 'tmp', "script_#{running_script_id}")
441
+ FileUtils.mkdir_p(script_cwd)
442
+ process.cwd = script_cwd
426
443
 
427
444
  # Check for offline access token
428
445
  model = nil
@@ -1213,7 +1230,8 @@ class RunningScript
1213
1230
  end
1214
1231
  @suite_report = OpenC3::SuiteRunner.suite_results.report
1215
1232
  # Write out the report to a local file
1216
- log_dir = File.join(RAILS_ROOT, 'log')
1233
+ log_dir = File.join(RAILS_ROOT, 'tmp', 'script_logs')
1234
+ FileUtils.mkdir_p(log_dir)
1217
1235
  filename = File.join(log_dir, File.build_timestamped_filename(['sr', parts.join('__')]))
1218
1236
  File.open(filename, 'wb') do |file|
1219
1237
  file.write(OpenC3::SuiteRunner.suite_results.report)
@@ -0,0 +1,452 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 Ball Aerospace & Technologies Corp.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is distributed in the hope that it will be useful,
7
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
+ # See LICENSE.md for more details.
10
+
11
+ # Modified by OpenC3, Inc.
12
+ # All changes Copyright 2026, OpenC3, Inc.
13
+ # All Rights Reserved
14
+ #
15
+ # This file may also be used under the terms of a commercial license
16
+ # if purchased from OpenC3, Inc.
17
+
18
+ require 'tempfile'
19
+ require 'openc3/utilities/target_file'
20
+ require 'openc3/utilities/running_script'
21
+ require 'openc3/script/suite'
22
+ require 'openc3/script/suite_runner'
23
+ require 'openc3/tools/test_runner/test'
24
+
25
+ OpenC3.require_file 'openc3/utilities/store'
26
+
27
+ class Script < OpenC3::TargetFile
28
+ def self.all(scope, target = nil)
29
+ super(scope, nil, target: target) # No path matchers
30
+ end
31
+
32
+ def self.lock(scope, name, username)
33
+ name = name.split('*')[0] # Split '*' that indicates modified
34
+ OpenC3::Store.hset("#{scope}__script-locks", name, username)
35
+ end
36
+
37
+ def self.unlock(scope, name)
38
+ name = name.split('*')[0] # Split '*' that indicates modified
39
+ OpenC3::Store.hdel("#{scope}__script-locks", name)
40
+ end
41
+
42
+ def self.locked?(scope, name)
43
+ name = name.split('*')[0] # Split '*' that indicates modified
44
+ locked_by = OpenC3::Store.hget("#{scope}__script-locks", name)
45
+ locked_by ||= false
46
+ locked_by
47
+ end
48
+
49
+ def self.get_breakpoints(scope, name)
50
+ breakpoints = OpenC3::Store.hget("#{scope}__script-breakpoints", name.split('*')[0]) # Split '*' that indicates modified
51
+ return JSON.parse(breakpoints, allow_nan: true, create_additions: true) if breakpoints
52
+ []
53
+ end
54
+
55
+ def self.process_suite(name, contents, new_process: true, username: nil, scope:)
56
+ python = false
57
+ python = true if File.extname(name) == '.py'
58
+
59
+ start = Time.now
60
+
61
+ if python
62
+ temp = Tempfile.new(%w[suite .py])
63
+ else
64
+ temp = Tempfile.new(%w[suite .rb])
65
+ end
66
+
67
+ # Remove any carriage returns which ruby doesn't like
68
+ temp.write(contents.gsub(/\r/, ' '))
69
+ temp.close
70
+
71
+ # We open a new process so as to not pollute the API with require
72
+ success = true
73
+ if new_process or python
74
+ if python
75
+ runner_path = File.join(RAILS_ROOT, 'scripts', 'run_suite_analysis.py')
76
+ process_name = ENV['OPENC3_PYTHON_BIN'] || '/openc3/python/.venv/bin/python'
77
+ process = ChildProcess.build(process_name, runner_path.to_s, scope, temp.path)
78
+ else
79
+ runner_path = File.join(RAILS_ROOT, 'scripts', 'run_suite_analysis.rb')
80
+ process = ChildProcess.build('ruby', runner_path.to_s, scope, temp.path)
81
+ end
82
+ process.cwd = File.join(RAILS_ROOT, 'scripts')
83
+
84
+ # Check for offline access token
85
+ model = nil
86
+ model = OpenC3::OfflineAccessModel.get_model(name: username, scope: scope) if username and username != ''
87
+
88
+ # Set proper secrets for running script
89
+ process.environment['SECRET_KEY_BASE'] = nil
90
+ process.environment['OPENC3_REDIS_USERNAME'] = ENV['OPENC3_SR_REDIS_USERNAME'] || 'OPENC3_SR_REDIS_USERNAME not set'
91
+ process.environment['OPENC3_REDIS_PASSWORD'] = ENV['OPENC3_SR_REDIS_PASSWORD'] || 'OPENC3_SR_REDIS_PASSWORD not set'
92
+ process.environment['OPENC3_BUCKET_USERNAME'] = ENV['OPENC3_SR_BUCKET_USERNAME'] || 'OPENC3_SR_BUCKET_USERNAME not set'
93
+ process.environment['OPENC3_BUCKET_PASSWORD'] = ENV['OPENC3_SR_BUCKET_PASSWORD'] || 'OPENC3_SR_BUCKET_PASSWORD not set'
94
+ process.environment['OPENC3_SR_REDIS_USERNAME'] = nil
95
+ process.environment['OPENC3_SR_REDIS_PASSWORD'] = nil
96
+ process.environment['OPENC3_SR_BUCKET_USERNAME'] = nil
97
+ process.environment['OPENC3_SR_BUCKET_PASSWORD'] = nil
98
+ process.environment['OPENC3_API_CLIENT'] = ENV['OPENC3_API_CLIENT'] || 'api'
99
+ if model and model.offline_access_token
100
+ auth = OpenC3::OpenC3KeycloakAuthentication.new(ENV['OPENC3_KEYCLOAK_URL'])
101
+ valid_token = auth.get_token_from_refresh_token(model.offline_access_token)
102
+ if valid_token
103
+ process.environment['OPENC3_API_TOKEN'] = model.offline_access_token
104
+ else
105
+ model.offline_access_token = nil
106
+ model.update
107
+ raise "offline_access token invalid for script"
108
+ end
109
+ else
110
+ process.environment['OPENC3_API_USER'] = ENV['OPENC3_API_USER'] || nil
111
+ if ENV['OPENC3_SERVICE_PASSWORD']
112
+ process.environment['OPENC3_API_PASSWORD'] = ENV['OPENC3_SERVICE_PASSWORD']
113
+ else
114
+ # The viewer user doesn't have an offline access token (because they can't run scripts)
115
+ # but they still want to be able to view suite files
116
+ # Since processing a suite file requires running it they won't get the Suite chrome
117
+ # so return nothing here and allow Script Runner to simply view the suite file
118
+ return '', '', false
119
+ end
120
+ end
121
+ process.environment['GEM_HOME'] = ENV['GEM_HOME'] || '/gems'
122
+ process.environment['PYTHONUSERBASE'] = ENV['PYTHONUSERBASE'] || '/gems/python_packages'
123
+ # Preserve PYTHONPATH to ensure Python can find both UV venv and user packages
124
+ process.environment['PYTHONPATH'] = ENV['PYTHONPATH'] || '.'
125
+
126
+ # Spawned process should not be controlled by same Bundler constraints as spawning process
127
+ ENV.each do |key, _value|
128
+ if key =~ /^BUNDLE/
129
+ process.environment[key] = nil
130
+ end
131
+ end
132
+ process.environment['RUBYOPT'] = nil # Removes loading bundler setup
133
+ process.environment['OPENC3_SCOPE'] = scope
134
+
135
+ stdout = Tempfile.new("child-stdout")
136
+ stdout.sync = true
137
+ stderr = Tempfile.new("child-stderr")
138
+ stderr.sync = true
139
+ process.io.stdout = stdout
140
+ process.io.stderr = stderr
141
+ process.start
142
+ process.wait
143
+ stdout.rewind
144
+ stdout_results = stdout.read
145
+ stdout.close
146
+ stdout.unlink
147
+ stderr.rewind
148
+ stderr_results = stderr.read
149
+ stderr.close
150
+ stderr.unlink
151
+ success = process.exit_code == 0
152
+ else
153
+ require temp.path
154
+ stdout_results = OpenC3::SuiteRunner.build_suites.as_json().to_json(allow_nan: true)
155
+ end
156
+ temp.delete
157
+ puts "Processed #{name} in #{Time.now - start} seconds"
158
+ # Make sure we're getting the last line which should be the suite
159
+ puts "Stdout Results:#{stdout_results}:"
160
+ puts "Stderr Results:#{stderr_results}:"
161
+ stdout_results = stdout_results.split("\n")[-1] if stdout_results
162
+ return stdout_results, stderr_results, success
163
+ end
164
+
165
+ def self.create(params)
166
+ existing = body(params[:scope], params[:name])
167
+ # Commit if there is no existing or something has changed
168
+ if existing.nil? or existing != params[:text]
169
+ super(params[:scope], params[:name], params[:text])
170
+ end
171
+ breakpoints = params[:breakpoints]
172
+ if breakpoints
173
+ if breakpoints.empty?
174
+ OpenC3::Store.hdel("#{params[:scope]}__script-breakpoints", params[:name])
175
+ else
176
+ OpenC3::Store.hset("#{params[:scope]}__script-breakpoints", params[:name],
177
+ breakpoints.as_json().to_json(allow_nan: true))
178
+ end
179
+ end
180
+ end
181
+
182
+ def self.delete_temp(scope)
183
+ files = super(scope)
184
+ files.each do |name|
185
+ # Remove any breakpoints associated with the temp files
186
+ OpenC3::Store.hdel("#{scope}__script-breakpoints", "#{TEMP_FOLDER}/#{File.basename(name)}")
187
+ end
188
+ end
189
+
190
+ def self.destroy(scope, name)
191
+ super(scope, name)
192
+ OpenC3::Store.hdel("#{scope}__script-breakpoints", name)
193
+ end
194
+
195
+ def self.run(
196
+ scope,
197
+ name,
198
+ suite_runner = nil,
199
+ disconnect = false,
200
+ environment = nil,
201
+ user_full_name = nil,
202
+ username = nil,
203
+ line_no = nil,
204
+ end_line_no = nil
205
+ )
206
+ RunningScript.spawn(scope, name, suite_runner, disconnect, environment, user_full_name, username, line_no, end_line_no)
207
+ end
208
+
209
+ def self.instrumented(filename, text)
210
+ language = detect_language(text, filename)
211
+ if language == 'ruby'
212
+ return {
213
+ 'title' => 'Instrumented Script',
214
+ 'description' =>
215
+ RunningScript.instrument_script(
216
+ text,
217
+ filename,
218
+ true,
219
+ ).split("\n").as_json().to_json(allow_nan: true),
220
+ }
221
+ elsif language == 'python'
222
+ start = Time.now
223
+ temp = Tempfile.new(%w[instrument .py])
224
+ temp.write(text)
225
+ temp.close
226
+
227
+ runner_path = File.join(RAILS_ROOT, 'scripts', 'run_instrument.py')
228
+ process_name = ENV['OPENC3_PYTHON_BIN'] || '/openc3/python/.venv/bin/python'
229
+ process = ChildProcess.build(process_name, runner_path.to_s, temp.path)
230
+ process.cwd = File.join(RAILS_ROOT, 'scripts')
231
+
232
+ stdout = Tempfile.new("child-stdout")
233
+ stdout.sync = true
234
+ stderr = Tempfile.new("child-stderr")
235
+ stderr.sync = true
236
+ process.io.stdout = stdout
237
+ process.io.stderr = stderr
238
+ process.start
239
+ process.wait
240
+ stdout.rewind
241
+ stdout_results = stdout.read
242
+ stdout.close
243
+ stdout.unlink
244
+ stderr.rewind
245
+ stderr_results = stderr.read
246
+ stderr.close
247
+ stderr.unlink
248
+ success = process.exit_code == 0
249
+ puts "Processed Instrumenting #{filename} in #{Time.now - start} seconds"
250
+ # Make sure we're getting the last line which should be the suite
251
+ puts "Stdout Results:#{stdout_results}:"
252
+ puts "Stderr Results:#{stderr_results}:"
253
+ # stdout_results = stdout_results.split("\n")[-1] if stdout_results
254
+
255
+ if success
256
+ return {
257
+ 'title' => 'Instrumented Script',
258
+ 'description' =>
259
+ stdout_results.to_s.split("\n").as_json().to_json(allow_nan: true),
260
+ }
261
+ else
262
+ return {
263
+ 'title' => 'Error Instrumenting Script',
264
+ 'description' =>
265
+ (stdout_results.to_s + stderr_results.to_s).split("\n").as_json().to_json(allow_nan: true),
266
+ }
267
+ end
268
+ else
269
+ return {
270
+ 'title' => 'Instrumenting Not Supported',
271
+ 'description' => ['Only Ruby and Python Support Viewing Instrumentation'].as_json.to_json,
272
+ }
273
+ end
274
+ end
275
+
276
+ def self.detect_language(text, filename = nil)
277
+ if filename
278
+ extension = File.extname(filename)
279
+ if extension == '.rb'
280
+ return 'ruby'
281
+ elsif extension == '.py'
282
+ return 'python'
283
+ elsif extension.length > 0
284
+ return 'other'
285
+ end
286
+ end
287
+
288
+ return 'ruby' if text =~ /^\s*(require|load|puts) /
289
+ return 'python' if text =~ /^\s*(import|from) /
290
+ return 'ruby' if text =~ /^\s*end\s*$/
291
+ return 'python' if text =~ /^\s*(if|def|while|else|elif|class).*:\s*$/
292
+ return 'ruby' # otherwise guess Ruby
293
+ end
294
+
295
+ def self.mnemonics(filename, text)
296
+ # Ruby and Python are currently handled in-browser
297
+ # Script Engine Possibly
298
+ extension = File.extname(filename).to_s.downcase
299
+ script_engine_model = OpenC3::ScriptEngineModel.get_model(name: extension, scope: 'DEFAULT')
300
+ if script_engine_model
301
+ script_engine = script_engine_model.filename
302
+ if File.extname(script_engine).to_s.downcase == '.py'
303
+ process_name = ENV['OPENC3_PYTHON_BIN'] || '/openc3/python/.venv/bin/python'
304
+ runner_path = File.join(RAILS_ROOT, 'scripts', 'script_engine_cmd.py')
305
+ else
306
+ process_name = 'ruby'
307
+ runner_path = File.join(RAILS_ROOT, 'scripts', 'script_engine_cmd.rb')
308
+ end
309
+
310
+ tf = nil
311
+ begin
312
+ tf = Tempfile.new((['mnemonics', extension]))
313
+ tf.write(text)
314
+ tf.close()
315
+ results, status = Open3.capture2e("#{process_name} \"#{runner_path}\" mnemonic_check \"#{tf.path}\"")
316
+ lines = []
317
+ if results and results.length > 0
318
+ results.each_line do |line|
319
+ lines << line
320
+ end
321
+ return(
322
+ { 'title' => 'Mnemonics Check Failed', 'description' => lines.as_json().to_json(allow_nan: true) }
323
+ )
324
+ else
325
+ return(
326
+ {
327
+ 'title' => 'Mnemonics Check Successful',
328
+ 'description' => ["Mnemonics OK"].as_json().to_json(allow_nan: true),
329
+ }
330
+ )
331
+ end
332
+ ensure
333
+ tf.unlink if tf
334
+ end
335
+ else
336
+ raise "Unsupported script file type: #{extension}"
337
+ end
338
+ end
339
+
340
+ def self.syntax(filename, text)
341
+ if text.nil?
342
+ return(
343
+ { 'title' => 'Syntax Check Failed', 'description' => 'no text passed' }
344
+ )
345
+ end
346
+ language = detect_language(text, filename)
347
+ if language == 'ruby'
348
+ check_process = IO.popen('ruby -c -rubygems 2>&1', 'r+')
349
+ check_process.write("require 'openc3'; require 'openc3/script'; " + text)
350
+ check_process.close_write
351
+ results = check_process.readlines
352
+ check_process.close
353
+ if results
354
+ if results.any?(/Syntax OK/)
355
+ return(
356
+ {
357
+ 'title' => 'Syntax Check Successful',
358
+ 'description' => results.as_json().to_json(allow_nan: true),
359
+ }
360
+ )
361
+ else
362
+ # Results is an array of strings like this: ":2: syntax error ..."
363
+ # Normally the procedure comes before the first colon but since we
364
+ # are writing to the process this is blank so we throw it away
365
+ results.map! { |result| result.split(':')[1..-1].join(':') }
366
+ return(
367
+ { 'title' => 'Syntax Check Failed', 'description' => results.as_json().to_json(allow_nan: true) }
368
+ )
369
+ end
370
+ else
371
+ return(
372
+ {
373
+ 'title' => 'Syntax Check Exception',
374
+ 'description' => 'Ruby syntax check unexpectedly returned nil',
375
+ }
376
+ )
377
+ end
378
+ elsif language == 'python'
379
+ # Python
380
+ tf = nil
381
+ begin
382
+ tf = Tempfile.new((['syntax', '.py']))
383
+ tf.write(text)
384
+ tf.close()
385
+ process_name = ENV['OPENC3_PYTHON_BIN'] || '/openc3/python/.venv/bin/python'
386
+ results, status = Open3.capture2e("#{process_name} -m py_compile #{tf.path}")
387
+ lines = []
388
+ success = status.respond_to?(:success?) ? status.success? : status == 0
389
+ if !success || (results and results.length > 0)
390
+ results.each_line do |line|
391
+ lines << line
392
+ end
393
+ lines << "Syntax Error" if lines.empty?
394
+ return(
395
+ { 'title' => 'Syntax Check Failed', 'description' => lines.as_json().to_json(allow_nan: true) }
396
+ )
397
+ else
398
+ return(
399
+ {
400
+ 'title' => 'Syntax Check Successful',
401
+ 'description' => ["Syntax OK"].as_json().to_json(allow_nan: true),
402
+ }
403
+ )
404
+ end
405
+ ensure
406
+ tf.unlink if tf
407
+ end
408
+ else
409
+ # Script Engine Possibly
410
+ extension = File.extname(filename).to_s.downcase
411
+ script_engine_model = OpenC3::ScriptEngineModel.get_model(name: extension, scope: 'DEFAULT')
412
+ if script_engine_model
413
+ script_engine = script_engine_model.filename
414
+ if File.extname(script_engine).to_s.downcase == '.py'
415
+ process_name = ENV['OPENC3_PYTHON_BIN'] || '/openc3/python/.venv/bin/python'
416
+ runner_path = File.join(RAILS_ROOT, 'scripts', 'script_engine_cmd.py')
417
+ else
418
+ process_name = 'ruby'
419
+ runner_path = File.join(RAILS_ROOT, 'scripts', 'script_engine_cmd.rb')
420
+ end
421
+
422
+ tf = nil
423
+ begin
424
+ tf = Tempfile.new((['syntax', extension]))
425
+ tf.write(text)
426
+ tf.close()
427
+ results, status = Open3.capture2e("#{process_name} \"#{runner_path}\" syntax_check \"#{tf.path}\"")
428
+ lines = []
429
+ if results and results.length > 0
430
+ results.each_line do |line|
431
+ lines << line
432
+ end
433
+ return(
434
+ { 'title' => 'Syntax Check Failed', 'description' => lines.as_json().to_json(allow_nan: true) }
435
+ )
436
+ else
437
+ return(
438
+ {
439
+ 'title' => 'Syntax Check Successful',
440
+ 'description' => ["Syntax OK"].as_json().to_json(allow_nan: true),
441
+ }
442
+ )
443
+ end
444
+ ensure
445
+ tf.unlink if tf
446
+ end
447
+ else
448
+ raise "Unsupported script file type: #{extension}"
449
+ end
450
+ end
451
+ end
452
+ end
@@ -47,7 +47,7 @@ module OpenC3
47
47
  if secret.length < 3
48
48
  raise ArgumentError, "Secret must have at least 3 items (type, key, data), got #{secret.length}"
49
49
  end
50
- type, key, data, secret_store = secret
50
+ type, key, data, _secret_store = secret
51
51
  case type
52
52
  when 'ENV'
53
53
  @local_secrets[key] = ENV[data]
@@ -1,14 +1,14 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- OPENC3_VERSION = '7.0.0-rc3'
3
+ OPENC3_VERSION = '7.0.1'
4
4
  module OpenC3
5
5
  module Version
6
6
  MAJOR = '7'
7
7
  MINOR = '0'
8
- PATCH = '0'
9
- OTHER = 'pre.rc3'
10
- BUILD = '3c2c659a79585f3881e8878d8013b8a1b9fd0bb9'
8
+ PATCH = '1'
9
+ OTHER = ''
10
+ BUILD = '665de79cef884b8d470b08817fd4a1fb538fb187'
11
11
  end
12
- VERSION = '7.0.0-rc3'
13
- GEM_VERSION = '7.0.0.pre.rc3'
12
+ VERSION = '7.0.1'
13
+ GEM_VERSION = '7.0.1'
14
14
  end
@@ -5,14 +5,6 @@ from openc3.conversions.conversion import Conversion
5
5
  # Custom conversion class
6
6
  # See https://docs.openc3.com/docs/configuration/conversions
7
7
  class <%= conversion_class %>(Conversion):
8
- def __init__(self):
9
- super().__init__()
10
- # Should be one of 'INT', 'UINT', 'FLOAT', 'STRING', 'BLOCK'
11
- self.converted_type = 'STRING'
12
- # Size of the converted type in bits
13
- # Use 0 for 'STRING' or 'BLOCK' where the size can be variable
14
- self.converted_bit_size = 0
15
-
16
8
  # @param value [Object] Value based on the item definition. This could be
17
9
  # a string, integer, float, or array of values.
18
10
  # @param packet [Packet] The packet object where the conversion is defined
@@ -5,17 +5,6 @@ module OpenC3
5
5
  # Custom conversion class
6
6
  # See https://docs.openc3.com/docs/configuration/conversions
7
7
  class <%= conversion_class %> < Conversion
8
- def initialize
9
- super()
10
- # Should be one of :INT, :UINT, :FLOAT, :STRING, :BLOCK
11
- @converted_type = :STRING
12
- # Size of the converted type in bits
13
- # Use 0 for :STRING or :BLOCK where the size can be variable
14
- @converted_bit_size = 0
15
- # return the arguments used
16
- @params = nil
17
- end
18
-
19
8
  # @param value [Object] Value based on the item definition. This could be
20
9
  # a string, integer, float, or array of values.
21
10
  # @param packet [Packet] The packet object where the conversion is defined
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "<%= tool_name %>",
3
- "version": "7.0.0-rc3",
3
+ "version": "7.0.1",
4
4
  "scripts": {
5
5
  "ng": "ng",
6
6
  "start": "ng serve",
@@ -23,7 +23,7 @@
23
23
  "@angular/platform-browser-dynamic": "^18.2.6",
24
24
  "@angular/router": "^18.2.6",
25
25
  "@astrouxds/astro-web-components": "^7.24.0",
26
- "@openc3/js-common": "6.10.4",
26
+ "@openc3/js-common": "7.0.1",
27
27
  "rxjs": "~7.8.0",
28
28
  "single-spa": "^5.9.5",
29
29
  "single-spa-angular": "^9.2.0",
@@ -16,7 +16,7 @@
16
16
  "@emotion/react": "^11.13.3",
17
17
  "@emotion/styled": "^11.11.0",
18
18
  "@mui/material": "^6.1.1",
19
- "@openc3/js-common": "6.10.4",
19
+ "@openc3/js-common": "7.0.1",
20
20
  "react": "^18.2.0",
21
21
  "react-dom": "^18.2.0",
22
22
  "single-spa-react": "^5.1.4"
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@astrouxds/astro-web-components": "^7.24.0",
15
- "@openc3/js-common": "6.10.4",
15
+ "@openc3/js-common": "7.0.1",
16
16
  "@smui/button": "^7.0.0",
17
17
  "@smui/common": "^7.0.0",
18
18
  "@smui/card": "^7.0.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "<%= tool_name %>",
3
- "version": "7.0.0-rc3",
3
+ "version": "7.0.1",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -11,8 +11,8 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "@astrouxds/astro-web-components": "^7.24.0",
14
- "@openc3/js-common": "6.10.4",
15
- "@openc3/vue-common": "6.10.4",
14
+ "@openc3/js-common": "7.0.1",
15
+ "@openc3/vue-common": "7.0.1",
16
16
  "axios": "^1.7.7",
17
17
  "date-fns": "^4.1.0",
18
18
  "lodash": "^4.17.21",
@@ -22,7 +22,6 @@
22
22
  "devDependencies": {
23
23
  "@vitejs/plugin-vue": "^6.0.1",
24
24
  "@vue/eslint-config-prettier": "^9.0.0",
25
- "@vue/test-utils": "^2.4.6",
26
25
  "eslint": "^9.16.0",
27
26
  "eslint-config-prettier": "^9.1.0",
28
27
  "eslint-plugin-prettier": "^5.2.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "<%= widget_name %>",
3
- "version": "7.0.0-rc3",
3
+ "version": "7.0.1",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "@astrouxds/astro-web-components": "^7.24.0",
11
- "@openc3/vue-common": "6.10.4",
11
+ "@openc3/vue-common": "7.0.1",
12
12
  "vuetify": "^3.7.1"
13
13
  },
14
14
  "devDependencies": {