openc3 6.10.3 → 6.10.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c1459e2b7d8160f09840d33a7fc6aa6314eb34367d68f41d45902a0203b37f57
4
- data.tar.gz: 6de6d85032257c4e19f9470846056d5cf4aa8acc0498e4f9691543598a12856f
3
+ metadata.gz: e6cc1e03ca8cd4ac75025fc7cd4758099c513957b012291e7b332e8c61c513cf
4
+ data.tar.gz: '058a71978241f50cc97a1ebefe70a7a6ecc69a9f1fb51e11690d50fd167ae60f'
5
5
  SHA512:
6
- metadata.gz: 556297be7a37dac84357e9a4c63c0c69a8c2fae6b0f8478317c9eb5b95506880f2b4acb2c99a56e4d7d2b0e4c4f22f4cf78a244046f027b68aad6b73a87a7845
7
- data.tar.gz: e36738a22fcdcfbc136a899a051f691c29b8a0701b6c6a6f79d36c3f4689d4ea8018c01b90a5908b082a07507d14f2fce3ab252fcd5abc3a04312daf935f1cfc
6
+ metadata.gz: a96529177ee05a9f04257e4668f245c7346692b039e000f970166fb311c80992401f19def70cbe63844510aa97b2a90ab89bafd835b377aecd14ef0505cb2feb
7
+ data.tar.gz: 3a1c5152b36c733009daaefb07786087b428bbf172dd3ecbee2cfb4bb82de88c84ac0af727c25287cd4af5d1038812e1f4a3d2566c935cf0c519b984163013f5
data/bin/openc3cli CHANGED
@@ -381,9 +381,10 @@ def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil, force: false, v
381
381
  gem_name = full_name.split('-')[0..-2].join('-')
382
382
  if file_gem_name == gem_name
383
383
  found = true
384
- # Upgrade if version changed else do nothing
385
- if file_full_name != full_name
386
- update_plugin(plugin_file_path, plugin_name, scope: scope, existing_plugin_name: plugin_name, force: force)
384
+ force_install = force || ENV['OPENC3_FORCE_INSTALL']
385
+ # Upgrade if version changed or force install is set
386
+ if file_full_name != full_name || force_install
387
+ update_plugin(plugin_file_path, plugin_name, scope: scope, existing_plugin_name: plugin_name, force: force_install)
387
388
  else
388
389
  puts "No version change detected for: #{plugin_name}"
389
390
  end
@@ -13,6 +13,44 @@ VARIABLE:
13
13
  required: true
14
14
  description: Default value of the variable
15
15
  values: .+
16
+ VARIABLE_DESCRIPTION:
17
+ summary: Add a description to a plugin variable
18
+ description: The VARIABLE_DESCRIPTION keyword adds a human-readable description to a previously defined VARIABLE. This description appears as hint text below the variable input field during plugin installation. Must follow a VARIABLE definition.
19
+ since: 7.0.0
20
+ example: |
21
+ VARIABLE port 8080
22
+ VARIABLE_DESCRIPTION port "TCP port for the target connection"
23
+ parameters:
24
+ - name: Variable Name
25
+ required: true
26
+ description: Name of the variable to describe. Must match a previously defined VARIABLE name.
27
+ values: .+
28
+ - name: Description
29
+ required: true
30
+ description: Human-readable description of the variable's purpose
31
+ values: .+
32
+ VARIABLE_STATE:
33
+ summary: Add a selectable state for a plugin variable
34
+ description: The VARIABLE_STATE keyword defines a selectable state for a previously defined VARIABLE. When states are defined for a variable, it renders as a dropdown/combobox in the plugin installation dialog instead of a text field. Users can still type custom values if needed. Multiple VARIABLE_STATE keywords can be used to define multiple states. Must follow a VARIABLE definition.
35
+ since: 7.0.0
36
+ example: |
37
+ VARIABLE target_name INST
38
+ VARIABLE_DESCRIPTION target_name "Select the target name"
39
+ VARIABLE_STATE target_name INST "Primary instrument"
40
+ VARIABLE_STATE target_name INST2 "Secondary instrument"
41
+ parameters:
42
+ - name: Variable Name
43
+ required: true
44
+ description: Name of the variable this state belongs to. Must match a previously defined VARIABLE name.
45
+ values: .+
46
+ - name: State Value
47
+ required: true
48
+ description: The value that will be used when this state is selected
49
+ values: .+
50
+ - name: State Description
51
+ required: false
52
+ description: Human-readable description of what this state represents. Appears as subtitle in the dropdown.
53
+ values: .+
16
54
  NEEDS_DEPENDENCIES:
17
55
  summary: Indicates the plugin needs dependencies and sets the GEM_HOME environment variable
18
56
  description: If the plugin has a top level lib folder or lists runtime dependencies in the gemspec,
@@ -15,7 +15,7 @@
15
15
 
16
16
  /*
17
17
  # Modified by OpenC3, Inc.
18
- # All changes Copyright 2022, OpenC3, Inc.
18
+ # All changes Copyright 2026, OpenC3, Inc.
19
19
  # All Rights Reserved
20
20
  #
21
21
  # This file may also be used under the terms of a commercial license
@@ -33,6 +33,7 @@ static ID id_ivar_line_number = 0;
33
33
  static ID id_ivar_keyword = 0;
34
34
  static ID id_ivar_parameters = 0;
35
35
  static ID id_ivar_line = 0;
36
+ static ID id_ivar_preserve_lines = 0;
36
37
  static ID id_method_readline = 0;
37
38
  static ID id_method_close = 0;
38
39
  static ID id_method_pos = 0;
@@ -42,6 +43,7 @@ static ID id_method_strip = 0;
42
43
  static ID id_method_to_s = 0;
43
44
  static ID id_method_upcase = 0;
44
45
  static ID id_method_parse_errors = 0;
46
+ static ID id_method_chomp_exclamation = 0;
45
47
 
46
48
  /*
47
49
  * Removes quotes from the given string if present.
@@ -131,53 +133,61 @@ static VALUE parse_loop(VALUE self, VALUE io, VALUE yield_non_keyword_lines, VAL
131
133
  rb_set_errinfo(Qnil);
132
134
  break;
133
135
  }
134
- line = rb_funcall(line, id_method_strip, 0);
135
- // Ensure the line length is not 0
136
- if (RSTRING_LEN(line) == 0) {
137
- continue;
138
- }
136
+ line = rb_funcall(line, id_method_chomp_exclamation, 0);
139
137
 
140
- if (RTEST(string_concat))
141
- {
142
- /* Skip comment lines after a string concat */
143
- if (RSTRING_PTR(line)[0] == '#')
144
- {
138
+ if (!RTEST(rb_ivar_get(self, id_ivar_preserve_lines))) {
139
+ line = rb_funcall(line, id_method_strip, 0);
140
+
141
+ // Ensure the line length is not 0
142
+ if (RSTRING_LEN(line) == 0) {
145
143
  continue;
146
144
  }
147
- /* Remove the opening quote if we're continuing the line */
148
- line = rb_str_new(RSTRING_PTR(line) + 1, RSTRING_LEN(line) - 1);
149
- }
150
145
 
151
- /* Check for string continuation */
152
- if ((RSTRING_PTR(line)[RSTRING_LEN(line) - 1] == '+') ||
153
- (RSTRING_PTR(line)[RSTRING_LEN(line) - 1] == '\\'))
154
- {
155
- int newline = 0;
156
- if (RSTRING_PTR(line)[RSTRING_LEN(line) - 1] == '+')
146
+ if (RTEST(string_concat))
157
147
  {
158
- newline = 1;
148
+ /* Skip comment lines after a string concat */
149
+ if (RSTRING_PTR(line)[0] == '#')
150
+ {
151
+ continue;
152
+ }
153
+ /* Remove the opening quote if we're continuing the line */
154
+ line = rb_str_new(RSTRING_PTR(line) + 1, RSTRING_LEN(line) - 1);
159
155
  }
160
- rb_str_resize(line, RSTRING_LEN(line) - 1);
161
- line = rb_funcall(line, id_method_strip, 0);
162
- rb_str_append(ivar_line, line);
163
- rb_str_resize(ivar_line, RSTRING_LEN(ivar_line) - 1);
164
- if (newline == 1)
156
+
157
+ /* Check for string continuation */
158
+ if ((RSTRING_PTR(line)[RSTRING_LEN(line) - 1] == '+') ||
159
+ (RSTRING_PTR(line)[RSTRING_LEN(line) - 1] == '\\'))
160
+ {
161
+ int newline = 0;
162
+ if (RSTRING_PTR(line)[RSTRING_LEN(line) - 1] == '+')
163
+ {
164
+ newline = 1;
165
+ }
166
+ rb_str_resize(line, RSTRING_LEN(line) - 1);
167
+ line = rb_funcall(line, id_method_strip, 0);
168
+ rb_str_append(ivar_line, line);
169
+ rb_str_resize(ivar_line, RSTRING_LEN(ivar_line) - 1);
170
+ if (newline == 1)
171
+ {
172
+ rb_str_cat2(ivar_line, "\n");
173
+ }
174
+ rb_ivar_set(self, id_ivar_line, ivar_line);
175
+ string_concat = Qtrue;
176
+ continue;
177
+ }
178
+ if (RSTRING_PTR(line)[RSTRING_LEN(line) - 1] == '&')
165
179
  {
166
- rb_str_cat2(ivar_line, "\n");
180
+ rb_str_append(ivar_line, line);
181
+ rb_str_resize(ivar_line, RSTRING_LEN(ivar_line) - 1);
182
+ rb_ivar_set(self, id_ivar_line, ivar_line);
183
+ continue;
167
184
  }
168
- rb_ivar_set(self, id_ivar_line, ivar_line);
169
- string_concat = Qtrue;
170
- continue;
171
- }
172
- if (RSTRING_PTR(line)[RSTRING_LEN(line) - 1] == '&')
173
- {
174
185
  rb_str_append(ivar_line, line);
175
- rb_str_resize(ivar_line, RSTRING_LEN(ivar_line) - 1);
176
186
  rb_ivar_set(self, id_ivar_line, ivar_line);
177
- continue;
187
+ } else {
188
+ ivar_line = line;
189
+ rb_ivar_set(self, id_ivar_line, ivar_line);
178
190
  }
179
- rb_str_append(ivar_line, line);
180
- rb_ivar_set(self, id_ivar_line, ivar_line);
181
191
  string_concat = Qfalse;
182
192
 
183
193
  data = rb_funcall(ivar_line, id_method_scan, 1, rx);
@@ -284,6 +294,7 @@ void Init_config_parser(void)
284
294
  id_ivar_keyword = rb_intern("@keyword");
285
295
  id_ivar_parameters = rb_intern("@parameters");
286
296
  id_ivar_line = rb_intern("@line");
297
+ id_ivar_preserve_lines = rb_intern("@preserve_lines");
287
298
  id_method_readline = rb_intern("readline");
288
299
  id_method_close = rb_intern("close");
289
300
  id_method_pos = rb_intern("pos");
@@ -293,6 +304,7 @@ void Init_config_parser(void)
293
304
  id_method_to_s = rb_intern("to_s");
294
305
  id_method_upcase = rb_intern("upcase");
295
306
  id_method_parse_errors = rb_intern("parse_errors");
307
+ id_method_chomp_exclamation = rb_intern("chomp!");
296
308
 
297
309
  mOpenC3 = rb_define_module("OpenC3");
298
310
 
@@ -1,6 +1,6 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- # Copyright 2023 OpenC3, Inc.
3
+ # Copyright 2026 OpenC3, Inc.
4
4
  # All Rights Reserved.
5
5
  #
6
6
  # This program is free software; you can modify and/or redistribute it
@@ -135,7 +135,7 @@ module OpenC3
135
135
  when :STRING, :BLOCK
136
136
  if item.array_size
137
137
  value = JSON.parse(value) if value.is_a? String
138
- value = value.map { |v| v.to_s }
138
+ value = value.map { |v| v.to_s }
139
139
  else
140
140
  value = value.to_s
141
141
  end
@@ -14,7 +14,7 @@
14
14
  # GNU Affero General Public License for more details.
15
15
 
16
16
  # Modified by OpenC3, Inc.
17
- # All changes Copyright 2024, OpenC3, Inc.
17
+ # All changes Copyright 2025, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -139,6 +139,7 @@ module OpenC3
139
139
  @line_number = config_parser.line_number
140
140
  @usage = usage
141
141
  @url = url
142
+ @preserve_lines = false
142
143
  end
143
144
  end
144
145
 
@@ -389,6 +390,10 @@ module OpenC3
389
390
  return value
390
391
  end
391
392
 
393
+ def set_preserve_lines(state)
394
+ @preserve_lines = state
395
+ end
396
+
392
397
  protected
393
398
 
394
399
  # Writes the ERB parsed results
@@ -500,39 +505,54 @@ module OpenC3
500
505
 
501
506
  begin
502
507
  line = io.readline
508
+ line.chomp!
503
509
  rescue Exception
504
510
  break
505
511
  end
506
512
 
507
- line.strip!
508
- # Ensure the line length is not 0
509
- next if line.length == 0
513
+ if not @preserve_lines
514
+ line.strip!
510
515
 
511
- if string_concat
512
- # Skip comment lines after a string concatenation
513
- if (line[0] == '#')
516
+ # Ensure the line length is not 0
517
+ if line.length == 0
518
+ if yield_non_keyword_lines
519
+ begin
520
+ yield(nil, [])
521
+ rescue => e
522
+ errors << e
523
+ end
524
+ end
514
525
  next
515
526
  end
516
- # Remove the opening quote if we're continuing the line
517
- line = line[1..-1]
518
- end
519
527
 
520
- # Check for string continuation
521
- case line[-1]
522
- when '+', '\\' # String concatenation
523
- newline = line[-1] == '+'
524
- # Trim off the concat character plus any spaces, e.g. "line" \
525
- trim = line[0..-2].strip()
526
- # Now trim off the last quote so it will flow into the next line
527
- @line += trim[0..-2]
528
- @line += "\n" if newline
529
- string_concat = true
530
- next
531
- when '&' # Line continuation
532
- @line += line[0..-2]
533
- next
528
+ if string_concat
529
+ # Skip comment lines after a string concatenation
530
+ if (line[0] == '#')
531
+ next
532
+ end
533
+ # Remove the opening quote if we're continuing the line
534
+ line = line[1..-1]
535
+ end
536
+
537
+ # Check for string continuation
538
+ case line[-1]
539
+ when '+', '\\' # String concatenation
540
+ newline = line[-1] == '+'
541
+ # Trim off the concat character plus any spaces, e.g. "line" \
542
+ trim = line[0..-2].strip()
543
+ # Now trim off the last quote so it will flow into the next line
544
+ @line += trim[0..-2]
545
+ @line += "\n" if newline
546
+ string_concat = true
547
+ next
548
+ when '&' # Line continuation
549
+ @line += line[0..-2]
550
+ next
551
+ else
552
+ @line += line
553
+ end
534
554
  else
535
- @line += line
555
+ @line = line
536
556
  end
537
557
  string_concat = false
538
558
 
@@ -14,7 +14,7 @@
14
14
  # GNU Affero General Public License for more details.
15
15
 
16
16
  # Modified by OpenC3, Inc.
17
- # All changes Copyright 2025, OpenC3, Inc.
17
+ # All changes Copyright 2026, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -58,21 +58,32 @@ end
58
58
  class String
59
59
  NON_ASCII_PRINTABLE = /[^\x21-\x7e\s]/
60
60
  NON_UTF8_PRINTABLE = /[\x00-\x08\x0E-\x1F\x7F]/
61
- def as_json(_options = nil)
62
- # If string is ASCII-8BIT (binary) and has non-ASCII bytes (> 127), encode as binary
63
- # This handles data from hex_to_byte_string and other binary sources
64
- if self.encoding == Encoding::ASCII_8BIT && self.bytes.any? { |b| b > 127 }
65
- return self.to_json_raw_object
66
- end
61
+ # Matches characters outside the Latin range (U+0000-U+00FF) or C1 control characters (U+0080-U+009F)
62
+ # Latin range covers Basic Latin (U+0000-U+007F) and Latin-1 Supplement (U+00A0-U+00FF)
63
+ # This includes common characters like µ (U+00B5), ° (U+00B0), ñ (U+00F1), etc.
64
+ OUTSIDE_LATIN_RANGE = /[^\u0000-\u007F\u00A0-\u00FF]/
67
65
 
66
+ def as_json(_options = nil)
67
+ # Try to interpret the string as UTF-8
68
+ # This handles both:
69
+ # 1. Unicode text in ASCII-8BIT strings (e.g., "µA" for micro-Ampères from config files)
70
+ # 2. Binary data from hex_to_byte_string (e.g., \xDE\xAD\xBE\xEF) which will fail valid_encoding?
68
71
  as_utf8 = self.dup.force_encoding('UTF-8')
69
72
  if as_utf8.valid_encoding?
73
+ # Valid UTF-8 - check for non-printable control characters
70
74
  if as_utf8 =~ NON_UTF8_PRINTABLE
71
75
  return self.to_json_raw_object
72
- else
73
- return as_utf8
74
76
  end
77
+ # Check if all characters are in the expected Latin range (U+0000-U+00FF)
78
+ # This prevents binary data that happens to be valid UTF-8 from being treated as text
79
+ # For example, \xDE\xAD decodes to U+07AD (Thaana script) which should be treated as binary
80
+ # Also reject C1 control characters (U+0080-U+009F) which are non-printable
81
+ if as_utf8 =~ OUTSIDE_LATIN_RANGE
82
+ return self.to_json_raw_object
83
+ end
84
+ return as_utf8
75
85
  else
86
+ # Invalid UTF-8 means this is truly binary data, encode as raw object
76
87
  return self.to_json_raw_object
77
88
  end
78
89
  end #:nodoc:
@@ -14,7 +14,7 @@
14
14
  # GNU Affero General Public License for more details.
15
15
 
16
16
  # Modified by OpenC3, Inc.
17
- # All changes Copyright 2022, OpenC3, Inc.
17
+ # All changes Copyright 2026, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -125,11 +125,13 @@ module OpenC3
125
125
 
126
126
  # Phase 1 Gather Variables
127
127
  variables = {}
128
+ current_variable_name = nil
128
129
  parser.parse_file(plugin_txt_path,
129
130
  false,
130
131
  true,
131
132
  false) do |keyword, params|
132
- if keyword == 'VARIABLE'
133
+ case keyword
134
+ when 'VARIABLE'
133
135
  usage = "#{keyword} <Variable Name> <Default Value>"
134
136
  parser.verify_num_parameters(2, nil, usage)
135
137
  variable_name = params[0]
@@ -137,10 +139,33 @@ module OpenC3
137
139
  raise "VARIABLE name '#{variable_name}' is reserved"
138
140
  end
139
141
  value = params[1..-1].join(" ")
140
- variables[variable_name] = value
142
+ variables[variable_name] = { 'value' => value }
143
+ current_variable_name = variable_name
141
144
  if existing_variables && existing_variables.key?(variable_name)
142
- variables[variable_name] = existing_variables[variable_name]
145
+ existing = existing_variables[variable_name]
146
+ # Handle both old format (string) and new format (hash)
147
+ if existing.is_a?(Hash)
148
+ variables[variable_name]['value'] = existing['value']
149
+ else
150
+ variables[variable_name]['value'] = existing
151
+ end
152
+ end
153
+ when 'VARIABLE_DESCRIPTION'
154
+ usage = "#{keyword} <Description>"
155
+ parser.verify_num_parameters(1, 1, usage)
156
+ unless current_variable_name
157
+ raise "VARIABLE_DESCRIPTION must follow a VARIABLE definition"
143
158
  end
159
+ variables[current_variable_name]['description'] = params[0]
160
+ when 'VARIABLE_STATE'
161
+ usage = "#{keyword} <Display Text> <Value>"
162
+ parser.verify_num_parameters(2, 2, usage)
163
+ unless current_variable_name
164
+ raise "VARIABLE_STATE must follow a VARIABLE definition"
165
+ end
166
+ variables[current_variable_name]['options'] ||= []
167
+ option = { 'value' => params[1], 'text' => params[0] }
168
+ variables[current_variable_name]['options'] << option
144
169
  end
145
170
  end
146
171
 
@@ -285,20 +310,26 @@ module OpenC3
285
310
  plugin_txt_path = tf.path
286
311
  variables = plugin_hash['variables']
287
312
  variables ||= {}
288
- variables['scope'] = scope
313
+ # Extract simple key-value pairs for ERB substitution
314
+ # Variables can be either new format (hash with 'value' key) or old format (string)
315
+ erb_variables = {}
316
+ variables.each do |name, var|
317
+ erb_variables[name] = var.is_a?(Hash) ? var['value'] : var
318
+ end
319
+ erb_variables['scope'] = scope
289
320
  if File.exist?(plugin_txt_path)
290
321
  parser = OpenC3::ConfigParser.new("https://openc3.com")
291
322
 
292
323
  current_model = nil
293
- parser.parse_file(plugin_txt_path, false, true, true, variables) do |keyword, params|
324
+ parser.parse_file(plugin_txt_path, false, true, true, erb_variables) do |keyword, params|
294
325
  case keyword
295
- when 'VARIABLE', 'NEEDS_DEPENDENCIES'
326
+ when 'VARIABLE', 'VARIABLE_DESCRIPTION', 'VARIABLE_STATE', 'NEEDS_DEPENDENCIES'
296
327
  # Ignore during phase 2
297
328
  when 'TARGET', 'INTERFACE', 'ROUTER', 'MICROSERVICE', 'TOOL', 'WIDGET', 'SCRIPT_ENGINE'
298
329
  begin
299
330
  if current_model
300
331
  current_model.create unless validate_only
301
- current_model.deploy(gem_path, variables, validate_only: validate_only)
332
+ current_model.deploy(gem_path, erb_variables, validate_only: validate_only)
302
333
  end
303
334
  # If something goes wrong in create, or more likely in deploy,
304
335
  # we want to clear the current_model and try to instantiate the next
@@ -318,7 +349,7 @@ module OpenC3
318
349
  end
319
350
  if current_model
320
351
  current_model.create unless validate_only
321
- current_model.deploy(gem_path, variables, validate_only: validate_only)
352
+ current_model.deploy(gem_path, erb_variables, validate_only: validate_only)
322
353
  current_model = nil
323
354
  end
324
355
  end
@@ -14,7 +14,7 @@
14
14
  # GNU Affero General Public License for more details.
15
15
 
16
16
  # Modified by OpenC3, Inc.
17
- # All changes Copyright 2025, OpenC3, Inc.
17
+ # All changes Copyright 2026, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -1527,7 +1527,7 @@ module OpenC3
1527
1527
  value = value.to_s
1528
1528
  end
1529
1529
  end
1530
- value << ' ' << item.units if value_type == :WITH_UNITS and item.units
1530
+ value = "#{value} #{item.units}" if value_type == :WITH_UNITS and item.units
1531
1531
  value
1532
1532
  end
1533
1533
 
@@ -180,6 +180,7 @@ module OpenC3
180
180
  @converted_type,
181
181
  @converted_bit_size) if keyword.include? "WRITE"
182
182
  @building_generic_conversion = false
183
+ parser.set_preserve_lines(false)
183
184
  # Add the current config.line to the conversion being built
184
185
  else
185
186
  @proc_text << parser.line << "\n"
@@ -712,6 +713,7 @@ module OpenC3
712
713
  parser.verify_num_parameters(0, 2, usage)
713
714
  @proc_text = ''
714
715
  @building_generic_conversion = true
716
+ parser.set_preserve_lines(true)
715
717
  @converted_type = nil
716
718
  @converted_bit_size = nil
717
719
  if params[0]
@@ -70,7 +70,7 @@ module OpenC3
70
70
  end
71
71
  if timeout
72
72
  end_time = Time.now
73
- if (start_time - end_time) > timeout
73
+ if (end_time - start_time) > timeout
74
74
  raise Timeout::Error, "No Data Timeout"
75
75
  end
76
76
  end
@@ -14,7 +14,7 @@
14
14
  # GNU Affero General Public License for more details.
15
15
 
16
16
  # Modified by OpenC3, Inc.
17
- # All changes Copyright 2024, OpenC3, Inc.
17
+ # All changes Copyright 2026, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -39,8 +39,9 @@ module OpenC3
39
39
  json_hash[item.name + "__F"] = packet.read_item(item, :FORMATTED) if item.format_string
40
40
  json_hash[item.name + "__U"] = packet.read_item(item, :WITH_UNITS) if item.units
41
41
  end
42
- json_hash['extra'] = JSON.generate(packet.extra.as_json, allow_nan: true)
42
+ json_hash['extra'] = JSON.generate(packet.extra.as_json, allow_nan: true) if packet.extra
43
43
  msg_hash['json_data'] = JSON.generate(json_hash.as_json, allow_nan: true)
44
+ msg_hash['extra'] = JSON.generate(packet.extra.as_json, allow_nan: true) if packet.extra
44
45
  EphemeralStoreQueued.write_topic(topic, msg_hash)
45
46
  end
46
47
 
@@ -228,7 +228,7 @@ module OpenC3
228
228
  else
229
229
  'requirements.txt'
230
230
  end
231
- s.files = Dir.glob("{targets,lib,tools,microservices}/**/*") + %w(Rakefile README.md LICENSE.txt plugin.txt) + [python_dep_file]
231
+ s.files = Dir.glob("{targets,lib,public,tools,microservices}/**/*") + %w(Rakefile README.md LICENSE.txt plugin.txt) + [python_dep_file]
232
232
  RUBY
233
233
  end
234
234
  File.write(gemspec_filename, gemspec)
@@ -1,14 +1,14 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- OPENC3_VERSION = '6.10.3'
3
+ OPENC3_VERSION = '6.10.4'
4
4
  module OpenC3
5
5
  module Version
6
6
  MAJOR = '6'
7
7
  MINOR = '10'
8
- PATCH = '3'
8
+ PATCH = '4'
9
9
  OTHER = ''
10
- BUILD = 'e6bdfd648829a6300d1b263f6be09a6128ef560d'
10
+ BUILD = '3ec40189fe2728ada6e0be2dd347073fafed95aa'
11
11
  end
12
- VERSION = '6.10.3'
13
- GEM_VERSION = '6.10.3'
12
+ VERSION = '6.10.4'
13
+ GEM_VERSION = '6.10.4'
14
14
  end
@@ -20,5 +20,5 @@ Gem::Specification.new do |s|
20
20
  time = Time.now.strftime("%Y%m%d%H%M%S")
21
21
  s.version = '0.0.0' + ".#{time}"
22
22
  end
23
- s.files = Dir.glob("{targets,lib,tools,microservices}/**/*") + %w(Rakefile README.md LICENSE.txt plugin.txt)
23
+ s.files = Dir.glob("{targets,lib,public,tools,microservices}/**/*") + %w(Rakefile README.md LICENSE.txt plugin.txt)
24
24
  end
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "<%= tool_name %>",
3
- "version": "6.10.3",
3
+ "version": "6.10.4",
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.3",
26
+ "@openc3/js-common": "6.10.4",
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.3",
19
+ "@openc3/js-common": "6.10.4",
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.3",
15
+ "@openc3/js-common": "6.10.4",
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": "6.10.3",
3
+ "version": "6.10.4",
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.3",
15
- "@openc3/vue-common": "6.10.3",
14
+ "@openc3/js-common": "6.10.4",
15
+ "@openc3/vue-common": "6.10.4",
16
16
  "axios": "^1.7.7",
17
17
  "date-fns": "^4.1.0",
18
18
  "lodash": "^4.17.21",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "<%= widget_name %>",
3
- "version": "6.10.3",
3
+ "version": "6.10.4",
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.3",
11
+ "@openc3/vue-common": "6.10.4",
12
12
  "vuetify": "^3.7.1"
13
13
  },
14
14
  "devDependencies": {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openc3
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.10.3
4
+ version: 6.10.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Melton