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 +4 -4
- data/bin/openc3cli +4 -3
- data/data/config/plugins.yaml +38 -0
- data/ext/openc3/ext/config_parser/config_parser.c +49 -37
- data/lib/openc3/accessors/accessor.rb +2 -2
- data/lib/openc3/config/config_parser.rb +45 -25
- data/lib/openc3/io/json_rpc.rb +20 -9
- data/lib/openc3/models/plugin_model.rb +40 -9
- data/lib/openc3/packets/packet.rb +2 -2
- data/lib/openc3/packets/packet_config.rb +2 -0
- data/lib/openc3/script/web_socket_api.rb +1 -1
- data/lib/openc3/topics/command_decom_topic.rb +3 -2
- data/lib/openc3/utilities/cli_generator.rb +1 -1
- data/lib/openc3/version.rb +5 -5
- data/templates/plugin/plugin.gemspec +1 -1
- data/templates/tool_angular/package.json +2 -2
- data/templates/tool_react/package.json +1 -1
- data/templates/tool_svelte/package.json +1 -1
- data/templates/tool_vue/package.json +3 -3
- data/templates/widget/package.json +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e6cc1e03ca8cd4ac75025fc7cd4758099c513957b012291e7b332e8c61c513cf
|
|
4
|
+
data.tar.gz: '058a71978241f50cc97a1ebefe70a7a6ecc69a9f1fb51e11690d50fd167ae60f'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
385
|
-
if
|
|
386
|
-
|
|
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
|
data/data/config/plugins.yaml
CHANGED
|
@@ -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
|
|
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,
|
|
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(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
508
|
-
|
|
509
|
-
next if line.length == 0
|
|
513
|
+
if not @preserve_lines
|
|
514
|
+
line.strip!
|
|
510
515
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
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
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
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
|
|
555
|
+
@line = line
|
|
536
556
|
end
|
|
537
557
|
string_concat = false
|
|
538
558
|
|
data/lib/openc3/io/json_rpc.rb
CHANGED
|
@@ -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
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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]
|
|
@@ -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
|
|
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)
|
data/lib/openc3/version.rb
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# encoding: ascii-8bit
|
|
2
2
|
|
|
3
|
-
OPENC3_VERSION = '6.10.
|
|
3
|
+
OPENC3_VERSION = '6.10.4'
|
|
4
4
|
module OpenC3
|
|
5
5
|
module Version
|
|
6
6
|
MAJOR = '6'
|
|
7
7
|
MINOR = '10'
|
|
8
|
-
PATCH = '
|
|
8
|
+
PATCH = '4'
|
|
9
9
|
OTHER = ''
|
|
10
|
-
BUILD = '
|
|
10
|
+
BUILD = '3ec40189fe2728ada6e0be2dd347073fafed95aa'
|
|
11
11
|
end
|
|
12
|
-
VERSION = '6.10.
|
|
13
|
-
GEM_VERSION = '6.10.
|
|
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
|
+
"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.
|
|
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",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "<%= tool_name %>",
|
|
3
|
-
"version": "6.10.
|
|
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.
|
|
15
|
-
"@openc3/vue-common": "6.10.
|
|
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
|
+
"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.
|
|
11
|
+
"@openc3/vue-common": "6.10.4",
|
|
12
12
|
"vuetify": "^3.7.1"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|