openc3 6.10.1 → 6.10.3
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 +11 -1
- data/data/config/command_modifiers.yaml +2 -0
- data/data/config/item_modifiers.yaml +2 -2
- data/data/config/parameter_modifiers.yaml +2 -2
- data/data/config/plugins.yaml +2 -1
- data/data/config/screen.yaml +23 -0
- data/data/config/target.yaml +8 -3
- data/data/config/target_config.yaml +2 -2
- data/data/config/telemetry_modifiers.yaml +2 -0
- data/data/config/widgets.yaml +30 -0
- data/lib/openc3/accessors/json_accessor.rb +1 -1
- data/lib/openc3/api/tlm_api.rb +1 -6
- data/lib/openc3/core_ext/string.rb +11 -3
- data/lib/openc3/io/json_api_object.rb +38 -14
- data/lib/openc3/io/json_drb_object.rb +29 -11
- data/lib/openc3/microservices/interface_microservice.rb +8 -3
- data/lib/openc3/microservices/microservice.rb +1 -0
- data/lib/openc3/models/target_model.rb +2 -1
- data/lib/openc3/packets/packet.rb +72 -1
- data/lib/openc3/packets/packet_config.rb +2 -2
- data/lib/openc3/packets/parsers/packet_item_parser.rb +9 -0
- data/lib/openc3/packets/parsers/xtce_converter.rb +464 -100
- data/lib/openc3/version.rb +5 -5
- 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 +17 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c1459e2b7d8160f09840d33a7fc6aa6314eb34367d68f41d45902a0203b37f57
|
|
4
|
+
data.tar.gz: 6de6d85032257c4e19f9470846056d5cf4aa8acc0498e4f9691543598a12856f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 556297be7a37dac84357e9a4c63c0c69a8c2fae6b0f8478317c9eb5b95506880f2b4acb2c99a56e4d7d2b0e4c4f22f4cf78a244046f027b68aad6b73a87a7845
|
|
7
|
+
data.tar.gz: e36738a22fcdcfbc136a899a051f691c29b8a0701b6c6a6f79d36c3f4689d4ea8018c01b90a5908b082a07507d14f2fce3ab252fcd5abc3a04312daf935f1cfc
|
data/bin/openc3cli
CHANGED
|
@@ -33,6 +33,7 @@ require 'openc3/models/queue_model'
|
|
|
33
33
|
require 'openc3/models/scope_model'
|
|
34
34
|
require 'openc3/models/tool_model'
|
|
35
35
|
require 'openc3/packets/packet_config'
|
|
36
|
+
require 'openc3/packets/parsers/xtce_converter'
|
|
36
37
|
require 'openc3/utilities/bucket'
|
|
37
38
|
require 'openc3/utilities/cli_generator'
|
|
38
39
|
require 'openc3/utilities/local_mode'
|
|
@@ -107,7 +108,8 @@ def xtce_converter(args)
|
|
|
107
108
|
options = {}
|
|
108
109
|
option_parser = OptionParser.new do |opts|
|
|
109
110
|
opts.banner = "Usage: xtce_converter [options] --import input_xtce_filename --output output_dir\n"+
|
|
110
|
-
" xtce_converter [options] --plugin /PATH/FILENAME.gem --output output_dir
|
|
111
|
+
" xtce_converter [options] --plugin /PATH/FILENAME.gem --output output_dir "+
|
|
112
|
+
" --variables variables.txt --root_target root_target_name --time_association_name time_association_name"
|
|
111
113
|
opts.separator("")
|
|
112
114
|
opts.on("-h", "--help", "Show this message") do
|
|
113
115
|
puts opts
|
|
@@ -125,6 +127,12 @@ def xtce_converter(args)
|
|
|
125
127
|
opts.on("-v", "--variables", "Optional variables file to pass to the plugin") do |arg|
|
|
126
128
|
options[:variables] = arg
|
|
127
129
|
end
|
|
130
|
+
opts.on("-r ROOT_TARGET", "--root_target ROOT_TARGET", "Optional flag to set which target is at the root of an XTCE document. If not specified, each target will be placed under a generic 'root' spacesystem") do |arg|
|
|
131
|
+
options[:root_target_name] = arg
|
|
132
|
+
end
|
|
133
|
+
opts.on("-t TIME_ASSOCIATION_NAME", "--time_association_name TIME_ASSOCIATION_NAME", "Optional flag to set which target is at the root of an XTCE document. If not specified, each target will be placed under a generic 'root' spacesystem") do |arg|
|
|
134
|
+
options[:time_association_name] = "PACKET_TIME"
|
|
135
|
+
end
|
|
128
136
|
end
|
|
129
137
|
|
|
130
138
|
begin
|
|
@@ -156,8 +164,10 @@ def xtce_converter(args)
|
|
|
156
164
|
puts "Installing #{File.basename(options[:plugin])}"
|
|
157
165
|
plugin_hash = OpenC3::PluginModel.install_phase1(options[:plugin], existing_variables: variables, scope: 'DEFAULT', validate_only: true)
|
|
158
166
|
plugin_hash['variables']['xtce_output'] = options[:output]
|
|
167
|
+
plugin_hash['variables']['time_association_name'] = options[:time_association_name]
|
|
159
168
|
OpenC3::PluginModel.install_phase2(plugin_hash, scope: 'DEFAULT', validate_only: true,
|
|
160
169
|
gem_file_path: options[:plugin])
|
|
170
|
+
OpenC3::XtceConverter.combine_output_xtce(options[:output], options[:root_target_name])
|
|
161
171
|
result = 0 # bash and Windows consider 0 success
|
|
162
172
|
rescue => e
|
|
163
173
|
puts "Error: #{e.message}"
|
|
@@ -100,6 +100,7 @@ STRUCTURE:
|
|
|
100
100
|
modifiers:
|
|
101
101
|
<%= MetaConfigParser.load('parameter_modifiers.yaml').to_meta_config_yaml(4) %>
|
|
102
102
|
summary: Adds and flattens a structure (generally a virtual packet) into the current packet. The specific named item is BLOCK type and hidden.
|
|
103
|
+
since: 6.10.0
|
|
103
104
|
parameters:
|
|
104
105
|
- name: Name
|
|
105
106
|
required: true
|
|
@@ -134,6 +135,7 @@ APPEND_STRUCTURE:
|
|
|
134
135
|
modifiers:
|
|
135
136
|
<%= MetaConfigParser.load('parameter_modifiers.yaml').to_meta_config_yaml(4) %>
|
|
136
137
|
summary: Adds and flattens a structure (generally a virtual packet) into the current packet. The specific named item is BLOCK type and hidden.
|
|
138
|
+
since: 6.10.0
|
|
137
139
|
parameters:
|
|
138
140
|
- name: Name
|
|
139
141
|
required: true
|
|
@@ -79,12 +79,12 @@ GENERIC_READ_CONVERSION_START:
|
|
|
79
79
|
ruby_example: |
|
|
80
80
|
APPEND_ITEM ITEM1 32 UINT
|
|
81
81
|
GENERIC_READ_CONVERSION_START
|
|
82
|
-
|
|
82
|
+
(value * 1.5).to_i # Convert the value by a scale factor
|
|
83
83
|
GENERIC_READ_CONVERSION_END
|
|
84
84
|
python_example: |
|
|
85
85
|
APPEND_ITEM ITEM1 32 UINT
|
|
86
86
|
GENERIC_READ_CONVERSION_START
|
|
87
|
-
|
|
87
|
+
int(value * 1.5) # Convert the value by a scale factor
|
|
88
88
|
GENERIC_READ_CONVERSION_END
|
|
89
89
|
parameters:
|
|
90
90
|
- name: Converted Type
|
|
@@ -141,12 +141,12 @@ GENERIC_WRITE_CONVERSION_START:
|
|
|
141
141
|
ruby_example: |
|
|
142
142
|
APPEND_PARAMETER ITEM1 32 UINT 0 0xFFFFFFFF 0
|
|
143
143
|
GENERIC_WRITE_CONVERSION_START
|
|
144
|
-
|
|
144
|
+
(value * 1.5).to_i # Convert the value by a scale factor
|
|
145
145
|
GENERIC_WRITE_CONVERSION_END
|
|
146
146
|
python_example: |
|
|
147
147
|
APPEND_PARAMETER ITEM1 32 UINT 0 0xFFFFFFFF 0
|
|
148
148
|
GENERIC_WRITE_CONVERSION_START
|
|
149
|
-
|
|
149
|
+
int(value * 1.5) # Convert the value by a scale factor
|
|
150
150
|
GENERIC_WRITE_CONVERSION_END
|
|
151
151
|
GENERIC_WRITE_CONVERSION_END:
|
|
152
152
|
summary: Complete a generic write conversion
|
data/data/config/plugins.yaml
CHANGED
|
@@ -92,7 +92,8 @@ WIDGET:
|
|
|
92
92
|
SCRIPT_ENGINE:
|
|
93
93
|
summary: Define a script engine to add language support to Script Runner
|
|
94
94
|
example: SCRIPT_ENGINE .print print_script_engine.py
|
|
95
|
-
description: Defines a script engine to add language support to Script Runner
|
|
95
|
+
description: Defines a script engine to add language support to Script Runner. For a realistic example, see our [CSTOL](https://github.com/OpenC3/openc3-cosmos-script-engine-cstol) plugin.
|
|
96
|
+
since: 6.5.0
|
|
96
97
|
parameters:
|
|
97
98
|
- name: Extension
|
|
98
99
|
description: Extension that will use this script engine
|
data/data/config/screen.yaml
CHANGED
|
@@ -125,6 +125,29 @@ SUBSETTING:
|
|
|
125
125
|
LABELVALUELIMITSBAR INST HEALTH_STATUS TEMP1
|
|
126
126
|
SUBSETTING 0 TEXTCOLOR green # Change the label's text to green
|
|
127
127
|
END
|
|
128
|
+
TOOLTIP:
|
|
129
|
+
summary: Adds a tooltip to the previously defined widget
|
|
130
|
+
description: TOOLTIP applies a custom hover tooltip to the widget defined immediately before it.
|
|
131
|
+
This allows you to provide helpful descriptions, mnemonics, or other contextual information
|
|
132
|
+
that appears when the user hovers over a widget. The tooltip overrides any default tooltip
|
|
133
|
+
that the widget may have.
|
|
134
|
+
since: 6.10.3
|
|
135
|
+
parameters:
|
|
136
|
+
- name: Tooltip Text
|
|
137
|
+
required: true
|
|
138
|
+
description: The text to display in the tooltip when hovering over the widget.
|
|
139
|
+
values: .+
|
|
140
|
+
- name: Delay
|
|
141
|
+
required: false
|
|
142
|
+
description: The delay in milliseconds before the tooltip appears (default = 600).
|
|
143
|
+
values: \d+
|
|
144
|
+
example: |
|
|
145
|
+
LED INST PARAMS VALUE5 RAW 25 20
|
|
146
|
+
SETTING LED_COLOR 0 GREEN
|
|
147
|
+
SETTING LED_COLOR 1 RED
|
|
148
|
+
TOOLTIP "Mnemonic: ABCDEF. This is the Star Tracker On/Off Status"
|
|
149
|
+
VALUE INST HEALTH_STATUS TEMP1
|
|
150
|
+
TOOLTIP "Temperature sensor 1: Primary thermal control" 1000
|
|
128
151
|
NAMED_WIDGET:
|
|
129
152
|
summary: Name a widget to allow access to it via the getNamedWidget method
|
|
130
153
|
description: To programmatically access parts of a telemetry screen you need
|
data/data/config/target.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
TARGET:
|
|
3
3
|
summary: Defines a new target
|
|
4
|
-
example: TARGET
|
|
4
|
+
example: TARGET KEYSIGHT_N6700 PWR_SUPPLY1
|
|
5
5
|
parameters:
|
|
6
6
|
- name: Folder Name
|
|
7
7
|
required: true
|
|
@@ -10,8 +10,13 @@ TARGET:
|
|
|
10
10
|
- name: Name
|
|
11
11
|
required: true
|
|
12
12
|
description:
|
|
13
|
-
The target name. While this
|
|
14
|
-
it can be different to create multiple targets based on the same target
|
|
13
|
+
The target name. While this typically matches the Folder Name
|
|
14
|
+
it can be different to create multiple targets based on the same target definition.
|
|
15
|
+
As in the Example Usage, the target folder is KEYSIGHT_N6700 but the target name is PWR_SUPPLY1.
|
|
16
|
+
To create multiple targets from the same folder, just define multiple TARGET entries
|
|
17
|
+
with different target names. To make the target definition flexbible, you can use ERB to
|
|
18
|
+
insert the target name in procedures, libraries, etc via <%= target_name %>.
|
|
19
|
+
See [ERB target_name](/docs/configuration/format#target_name) for more information.
|
|
15
20
|
values: .*
|
|
16
21
|
modifiers:
|
|
17
22
|
CMD_BUFFER_DEPTH:
|
|
@@ -91,13 +91,13 @@ CMD_UNIQUE_ID_MODE:
|
|
|
91
91
|
Ideally all commands for a target are identified using the exact same bit offset, size,
|
|
92
92
|
and type field in each command. If ANY command identifiers differ then this flag must be set
|
|
93
93
|
to force a brute force identification method.
|
|
94
|
-
warning: Using this mode significantly slows packet identification
|
|
95
94
|
since: 4.4.0
|
|
95
|
+
deprecated: Since 6.10.0 this condition is now automatically detected
|
|
96
96
|
TLM_UNIQUE_ID_MODE:
|
|
97
97
|
summary: Telemetry packets identifiers don't all share the same bit offset, size, and type
|
|
98
98
|
description:
|
|
99
99
|
Ideally all telemetry for a target are identified using the exact same bit offset, size,
|
|
100
100
|
and type field in each packet. If ANY telemetry identifiers differ then this flag must be set
|
|
101
101
|
to force a brute force identification method.
|
|
102
|
-
warning: Using this mode significantly slows packet identification
|
|
103
102
|
since: 4.4.0
|
|
103
|
+
deprecated: Since 6.10.0 this condition is now automatically detected
|
|
@@ -90,6 +90,7 @@ STRUCTURE:
|
|
|
90
90
|
modifiers:
|
|
91
91
|
<%= MetaConfigParser.load('item_modifiers.yaml').to_meta_config_yaml(4) %>
|
|
92
92
|
summary: Adds and flattens a structure (generally a virtual packet) into the current packet. The specific named item is BLOCK type and hidden.
|
|
93
|
+
since: 6.10.0
|
|
93
94
|
parameters:
|
|
94
95
|
- name: Name
|
|
95
96
|
required: true
|
|
@@ -124,6 +125,7 @@ APPEND_STRUCTURE:
|
|
|
124
125
|
modifiers:
|
|
125
126
|
<%= MetaConfigParser.load('item_modifiers.yaml').to_meta_config_yaml(4) %>
|
|
126
127
|
summary: Adds and flattens a structure (generally a virtual packet) into the current packet. The specific named item is BLOCK type and hidden.
|
|
128
|
+
since: 6.10.0
|
|
127
129
|
parameters:
|
|
128
130
|
- name: Name
|
|
129
131
|
required: true
|
data/data/config/widgets.yaml
CHANGED
|
@@ -204,6 +204,36 @@ Decoration Widgets:
|
|
|
204
204
|
SPACER 0 100
|
|
205
205
|
LABEL "Spacer above"
|
|
206
206
|
END
|
|
207
|
+
FILEDISPLAY:
|
|
208
|
+
summary: Displays the contents of a target file with syntax highlighting
|
|
209
|
+
since: 6.10.3
|
|
210
|
+
parameters:
|
|
211
|
+
- name: File path
|
|
212
|
+
required: true
|
|
213
|
+
description: Path to the file relative to the target folder (e.g. "INST/procedures/file.rb")
|
|
214
|
+
values: .+
|
|
215
|
+
- name: Width
|
|
216
|
+
required: false
|
|
217
|
+
description: Width of the widget in pixels (default = 600)
|
|
218
|
+
values: \d+
|
|
219
|
+
- name: Height
|
|
220
|
+
required: false
|
|
221
|
+
description: Height of the widget in pixels (default = 300)
|
|
222
|
+
values: \d+
|
|
223
|
+
example: |
|
|
224
|
+
FILEDISPLAY "INST/data/sample.json" 400 200
|
|
225
|
+
FILECHECKSUM:
|
|
226
|
+
summary: Displays SHA-256 checksum of one or more files, with comparison if multiple
|
|
227
|
+
since: 6.10.3
|
|
228
|
+
parameters:
|
|
229
|
+
- name: File path
|
|
230
|
+
required: true
|
|
231
|
+
description: Path to a file relative to the target folder (e.g. "INST/procedures/file.rb"). Multiple file paths can be provided to compare checksums.
|
|
232
|
+
values: .+
|
|
233
|
+
example: |
|
|
234
|
+
FILECHECKSUM "INST/data/sample.json"
|
|
235
|
+
FILECHECKSUM "INST/data/sample.json" "INST2/data/sample.json"
|
|
236
|
+
FILECHECKSUM "INST/data/file1.bin" "INST/data/file2.bin" "INST/data/file3.bin"
|
|
207
237
|
Telemetry Widgets:
|
|
208
238
|
description: Telemetry widgets are used to display telemetry values.
|
|
209
239
|
The first parameters to each of these widgets is a telemetry mnemonic.
|
|
@@ -25,7 +25,7 @@ require 'openc3/accessors/accessor'
|
|
|
25
25
|
OpenC3.disable_warnings do
|
|
26
26
|
class JsonPath
|
|
27
27
|
def self.process_object(obj_or_str, opts = {})
|
|
28
|
-
obj_or_str.is_a?(String) ? MultiJson.
|
|
28
|
+
obj_or_str.is_a?(String) ? MultiJson.load(obj_or_str, max_nesting: opts[:max_nesting], create_additions: true, allow_nan: true) : obj_or_str
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
end
|
data/lib/openc3/api/tlm_api.rb
CHANGED
|
@@ -284,13 +284,8 @@ module OpenC3
|
|
|
284
284
|
value_type = 'RAW' # Must request the raw value when dealing with the reserved items
|
|
285
285
|
end
|
|
286
286
|
|
|
287
|
-
#
|
|
287
|
+
# Arrays must be accessed as RAW since there's no conversion
|
|
288
288
|
if item['array_size']
|
|
289
|
-
# TODO: This needs work ... we're JSON encoding non numeric array values
|
|
290
|
-
if item['data_type'] == 'STRING' or item['data_type'] == 'BLOCK'
|
|
291
|
-
results << nil
|
|
292
|
-
next
|
|
293
|
-
end
|
|
294
289
|
value_type = 'RAW'
|
|
295
290
|
end
|
|
296
291
|
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
require 'openc3/packets/binary_accessor'
|
|
24
24
|
require 'openc3/ext/string' if RUBY_ENGINE == 'ruby' and !ENV['OPENC3_NO_EXT']
|
|
25
|
+
require 'yaml'
|
|
25
26
|
|
|
26
27
|
# OpenC3 specific additions to the Ruby String class
|
|
27
28
|
class String
|
|
@@ -40,6 +41,8 @@ class String
|
|
|
40
41
|
HEX_CHECK_REGEX = /\A\s*0[xX][\dabcdefABCDEF]+\s*\z/
|
|
41
42
|
# Regular expression to identify a String as an Array of numbers
|
|
42
43
|
ARRAY_CHECK_REGEX = /\A\s*\[.*\]\s*\z/
|
|
44
|
+
# Regular expression to identify a String containing object notation
|
|
45
|
+
OBJECT_CHECK_REGEX = /\A\s*\{.*\}\s*\z/
|
|
43
46
|
|
|
44
47
|
# Displays a String containing binary data in a human readable format by
|
|
45
48
|
# converting each byte to the hex representation.
|
|
@@ -209,6 +212,11 @@ class String
|
|
|
209
212
|
if ARRAY_CHECK_REGEX.match?(self) then true else false end
|
|
210
213
|
end
|
|
211
214
|
|
|
215
|
+
# @return [Boolean] Whether the String represents an Object
|
|
216
|
+
def is_object?
|
|
217
|
+
if OBJECT_CHECK_REGEX.match?(self) then true else false end
|
|
218
|
+
end
|
|
219
|
+
|
|
212
220
|
# @return [Boolean] Whether the string contains only printable characters
|
|
213
221
|
def is_printable?
|
|
214
222
|
if NON_PRINTABLE_REGEX.match?(self) then false else true end
|
|
@@ -238,9 +246,9 @@ class String
|
|
|
238
246
|
elsif self.is_hex?
|
|
239
247
|
# Hex
|
|
240
248
|
return_value = Integer(self)
|
|
241
|
-
elsif self.is_array?
|
|
242
|
-
# Array
|
|
243
|
-
return_value =
|
|
249
|
+
elsif self.is_array? or self.is_object?
|
|
250
|
+
# Array or Object
|
|
251
|
+
return_value = YAML.safe_load(self)
|
|
244
252
|
end
|
|
245
253
|
rescue Exception
|
|
246
254
|
# Something went wrong so just return the string as is
|
|
@@ -34,6 +34,10 @@ require 'faraday'
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
module OpenC3
|
|
37
|
+
# Number of times to retry a request when a connection error occurs
|
|
38
|
+
RETRY_COUNT = 3
|
|
39
|
+
# Delay between retries in seconds
|
|
40
|
+
RETRY_DELAY = 0.1
|
|
37
41
|
|
|
38
42
|
class JsonApiError < StandardError; end
|
|
39
43
|
|
|
@@ -200,21 +204,41 @@ module OpenC3
|
|
|
200
204
|
|
|
201
205
|
# NOTE: This is a helper method and should not be called directly
|
|
202
206
|
def _send_request(method:, endpoint:, kwargs:)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
207
|
+
uri = URI("#{@url}#{endpoint}")
|
|
208
|
+
@log[0] = "#{method} Request: #{uri.to_s} #{kwargs}"
|
|
209
|
+
STDOUT.puts @log[0] if JsonDRb.debug?
|
|
210
|
+
|
|
211
|
+
retry_count = 0
|
|
212
|
+
while retry_count <= RETRY_COUNT
|
|
213
|
+
begin
|
|
214
|
+
resp = _http_request(method: method, uri: uri, kwargs: kwargs)
|
|
215
|
+
@log[1] = "#{method} Response: #{resp.status} #{resp.headers} #{resp.body}"
|
|
216
|
+
STDOUT.puts @log[1] if JsonDRb.debug?
|
|
217
|
+
@response_data = resp.body
|
|
218
|
+
return resp
|
|
219
|
+
rescue Faraday::ConnectionFailed, Errno::ECONNRESET, Errno::EPIPE, IOError => e
|
|
220
|
+
# Connection errors are retryable - reconnect and try again
|
|
221
|
+
retry_count += 1
|
|
222
|
+
@log[2] = "#{method} Exception: #{e.class}, #{e.message}, #{e.backtrace}"
|
|
223
|
+
if retry_count <= RETRY_COUNT
|
|
224
|
+
Logger.warn("JsonApiObject: Connection error, retry #{retry_count}/#{RETRY_COUNT}: #{e.class} #{e.message}")
|
|
225
|
+
disconnect()
|
|
226
|
+
sleep(RETRY_DELAY)
|
|
227
|
+
connect()
|
|
228
|
+
else
|
|
229
|
+
error = "Api Exception: #{@log[0]} ::: #{@log[1]} ::: #{@log[2]}"
|
|
230
|
+
raise error
|
|
231
|
+
end
|
|
232
|
+
rescue StandardError => e
|
|
233
|
+
@log[2] = "#{method} Exception: #{e.class}, #{e.message}, #{e.backtrace}"
|
|
234
|
+
disconnect()
|
|
235
|
+
error = "Api Exception: #{@log[0]} ::: #{@log[1]} ::: #{@log[2]}"
|
|
236
|
+
raise error
|
|
237
|
+
end
|
|
217
238
|
end
|
|
239
|
+
# Should not reach here, but just in case
|
|
240
|
+
error = "Api Exception: #{@log[0]} ::: #{@log[1]} ::: #{@log[2]}"
|
|
241
|
+
raise error
|
|
218
242
|
end
|
|
219
243
|
|
|
220
244
|
# NOTE: This is a helper method and should not be called directly
|
|
@@ -92,18 +92,36 @@ module OpenC3
|
|
|
92
92
|
'Content-Type' => 'application/json-rpc',
|
|
93
93
|
}
|
|
94
94
|
end
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
95
|
+
|
|
96
|
+
@log[0] = "Request: #{@uri.to_s} #{USER_AGENT} #{data.to_s}"
|
|
97
|
+
STDOUT.puts @log[0] if JsonDRb.debug?
|
|
98
|
+
|
|
99
|
+
retry_count = 0
|
|
100
|
+
while retry_count <= RETRY_COUNT
|
|
101
|
+
begin
|
|
102
|
+
resp = @http.post(@uri, data, headers)
|
|
103
|
+
@log[1] = "Response: #{resp.status} #{resp.headers} #{resp.body}"
|
|
104
|
+
@response_data = resp.body
|
|
105
|
+
STDOUT.puts @log[1] if JsonDRb.debug?
|
|
106
|
+
return resp.body
|
|
107
|
+
rescue Faraday::ConnectionFailed, Errno::ECONNRESET, Errno::EPIPE, IOError => e
|
|
108
|
+
# Connection errors are retryable - reconnect and try again
|
|
109
|
+
retry_count += 1
|
|
110
|
+
@log[2] = "Exception: #{e.class}, #{e.message}, #{e.backtrace}"
|
|
111
|
+
if retry_count <= RETRY_COUNT
|
|
112
|
+
Logger.warn("JsonDRbObject: Connection error, retry #{retry_count}/#{RETRY_COUNT}: #{e.class} #{e.message}")
|
|
113
|
+
disconnect()
|
|
114
|
+
sleep(RETRY_DELAY)
|
|
115
|
+
connect()
|
|
116
|
+
else
|
|
117
|
+
return nil
|
|
118
|
+
end
|
|
119
|
+
rescue StandardError => e
|
|
120
|
+
@log[2] = "Exception: #{e.class}, #{e.message}, #{e.backtrace}"
|
|
121
|
+
return nil
|
|
122
|
+
end
|
|
106
123
|
end
|
|
124
|
+
return nil
|
|
107
125
|
end
|
|
108
126
|
|
|
109
127
|
def handle_response(response:)
|
|
@@ -89,6 +89,7 @@ module OpenC3
|
|
|
89
89
|
InterfaceTopic.receive_commands(@interface, scope: @scope) do |topic, msg_id, msg_hash, _redis|
|
|
90
90
|
OpenC3.with_context(msg_hash) do
|
|
91
91
|
release_critical = false
|
|
92
|
+
critical_model = nil
|
|
92
93
|
msgid_seconds_from_epoch = msg_id.split('-')[0].to_i / 1000.0
|
|
93
94
|
delta = Time.now.to_f - msgid_seconds_from_epoch
|
|
94
95
|
@metric.set(name: 'interface_topic_delta_seconds', value: delta, type: 'gauge', unit: 'seconds', help: 'Delta time between data written to stream and interface cmd start') if @metric
|
|
@@ -188,9 +189,9 @@ module OpenC3
|
|
|
188
189
|
end
|
|
189
190
|
if msg_hash.key?('release_critical')
|
|
190
191
|
# Note: intentional fall through below this point
|
|
191
|
-
|
|
192
|
-
if
|
|
193
|
-
msg_hash =
|
|
192
|
+
critical_model = CriticalCmdModel.get_model(name: msg_hash['release_critical'], scope: @scope)
|
|
193
|
+
if critical_model
|
|
194
|
+
msg_hash = critical_model.cmd_hash
|
|
194
195
|
release_critical = true
|
|
195
196
|
else
|
|
196
197
|
next "Critical command #{msg_hash['release_critical']} not found"
|
|
@@ -279,6 +280,10 @@ module OpenC3
|
|
|
279
280
|
command.extra ||= {}
|
|
280
281
|
command.extra['cmd_string'] = msg_hash['cmd_string']
|
|
281
282
|
command.extra['username'] = msg_hash['username']
|
|
283
|
+
# Add approver info if this was a critical command that was approved
|
|
284
|
+
if critical_model
|
|
285
|
+
command.extra['approver'] = critical_model.approver
|
|
286
|
+
end
|
|
282
287
|
hazardous, hazardous_description = System.commands.cmd_pkt_hazardous?(command)
|
|
283
288
|
|
|
284
289
|
# Initial Are you sure? Hazardous check
|
|
@@ -55,6 +55,7 @@ module OpenC3
|
|
|
55
55
|
MicroserviceStatusModel.set(microservice.as_json(), scope: microservice.scope)
|
|
56
56
|
microservice.state = 'RUNNING'
|
|
57
57
|
microservice.run
|
|
58
|
+
Logger.info("Microservice #{name} run method returned cleanly and will now shutdown.")
|
|
58
59
|
microservice.state = 'FINISHED'
|
|
59
60
|
rescue Exception => e
|
|
60
61
|
if SystemExit === e or SignalException === e
|
|
@@ -638,7 +638,8 @@ module OpenC3
|
|
|
638
638
|
system = System.new([@name], temp_dir)
|
|
639
639
|
if variables["xtce_output"]
|
|
640
640
|
puts "Converting target #{@name} to .xtce files in #{variables["xtce_output"]}/#{@name}"
|
|
641
|
-
|
|
641
|
+
puts " Using mnemonic '#{variables['time_association_name']}' as the packet time item."
|
|
642
|
+
system.packet_config.to_xtce(variables["xtce_output"], variables['time_association_name'])
|
|
642
643
|
end
|
|
643
644
|
unless validate_only
|
|
644
645
|
build_target_archive(temp_dir, target_folder)
|
|
@@ -433,6 +433,9 @@ module OpenC3
|
|
|
433
433
|
previous_item = nil
|
|
434
434
|
warnings = []
|
|
435
435
|
@sorted_items.each do |item|
|
|
436
|
+
# Skip items with a parent_item since those are accessor-based items within a structure
|
|
437
|
+
# (e.g., JSON, CBOR) that don't have meaningful bit positions - they share the parent's bit_offset
|
|
438
|
+
next if item.parent_item
|
|
436
439
|
if expected_next_offset and (item.bit_offset < expected_next_offset) and !item.overlap
|
|
437
440
|
msg = "Bit definition overlap at bit offset #{item.bit_offset} for packet #{@target_name} #{@packet_name} items #{item.name} and #{previous_item.name}"
|
|
438
441
|
Logger.instance.warn(msg)
|
|
@@ -905,6 +908,11 @@ module OpenC3
|
|
|
905
908
|
write_item(item, item.structure.buffer(false), :RAW, buffer)
|
|
906
909
|
end
|
|
907
910
|
elsif not item.default.nil? and not item.parent_item
|
|
911
|
+
# Skip writing default for accessor-based items when template is used
|
|
912
|
+
# The template already contains the correct default value
|
|
913
|
+
# Only skip if key is explicitly set (different from item name) - this distinguishes
|
|
914
|
+
# JsonAccessor (key="$.field") from TemplateAccessor (key=name="FIELD")
|
|
915
|
+
next if item.key and item.key != item.name and @template and use_template
|
|
908
916
|
write_item(item, item.default, :CONVERTED, buffer) unless skip_item_names and upcase_skip_item_names.include?(item.name)
|
|
909
917
|
end
|
|
910
918
|
end
|
|
@@ -1233,7 +1241,20 @@ module OpenC3
|
|
|
1233
1241
|
# Items with derived items last
|
|
1234
1242
|
@sorted_items.each do |item|
|
|
1235
1243
|
if item.data_type != :DERIVED
|
|
1236
|
-
|
|
1244
|
+
item_hash = item.as_json(*a)
|
|
1245
|
+
# For accessor-based items with a template, extract the default from the template
|
|
1246
|
+
# Only extract for items with explicit keys (different from item name) - this distinguishes
|
|
1247
|
+
# JsonAccessor (key="$.field") from TemplateAccessor (key=name="FIELD")
|
|
1248
|
+
if item.key and item.key != item.name and @template
|
|
1249
|
+
begin
|
|
1250
|
+
template_value = read_item_from_template(item)
|
|
1251
|
+
item_hash['default'] = template_value unless template_value.nil?
|
|
1252
|
+
rescue => e
|
|
1253
|
+
# If we can't read from template, keep the original default
|
|
1254
|
+
Logger.debug("Could not read template default for #{@target_name} #{@packet_name} #{item.name}: #{e.message}")
|
|
1255
|
+
end
|
|
1256
|
+
end
|
|
1257
|
+
items << item_hash
|
|
1237
1258
|
end
|
|
1238
1259
|
end
|
|
1239
1260
|
@sorted_items.each do |item|
|
|
@@ -1358,6 +1379,56 @@ module OpenC3
|
|
|
1358
1379
|
|
|
1359
1380
|
protected
|
|
1360
1381
|
|
|
1382
|
+
# Read item value from template, handling PythonProxy accessors
|
|
1383
|
+
# For PythonProxy, parse template directly since the proxy's class method returns a string
|
|
1384
|
+
def read_item_from_template(item)
|
|
1385
|
+
accessor_class = @accessor.class
|
|
1386
|
+
|
|
1387
|
+
# For PythonProxy, accessor.class returns the class name as a string
|
|
1388
|
+
# We need to parse the template directly based on the accessor type
|
|
1389
|
+
if accessor_class.is_a?(String)
|
|
1390
|
+
case accessor_class
|
|
1391
|
+
when 'JsonAccessor'
|
|
1392
|
+
return read_json_template_item(item)
|
|
1393
|
+
when 'CborAccessor'
|
|
1394
|
+
return read_cbor_template_item(item)
|
|
1395
|
+
when 'XmlAccessor'
|
|
1396
|
+
return read_xml_template_item(item)
|
|
1397
|
+
else
|
|
1398
|
+
# Unknown accessor type - can't read from template
|
|
1399
|
+
return nil
|
|
1400
|
+
end
|
|
1401
|
+
else
|
|
1402
|
+
# Normal accessor - use the class method
|
|
1403
|
+
return accessor_class.read_item(item, @template)
|
|
1404
|
+
end
|
|
1405
|
+
end
|
|
1406
|
+
|
|
1407
|
+
# Parse JSON template and extract item value using JSONPath key
|
|
1408
|
+
def read_json_template_item(item)
|
|
1409
|
+
require 'json'
|
|
1410
|
+
require 'jsonpath'
|
|
1411
|
+
json_data = JSON.parse(@template.to_s, allow_nan: true, create_additions: true)
|
|
1412
|
+
JsonPath.new(item.key).first(json_data)
|
|
1413
|
+
end
|
|
1414
|
+
|
|
1415
|
+
# Parse CBOR template and extract item value using JSONPath key
|
|
1416
|
+
def read_cbor_template_item(item)
|
|
1417
|
+
require 'cbor'
|
|
1418
|
+
require 'jsonpath'
|
|
1419
|
+
cbor_data = CBOR.decode(@template.to_s)
|
|
1420
|
+
JsonPath.new(item.key).first(cbor_data)
|
|
1421
|
+
end
|
|
1422
|
+
|
|
1423
|
+
# Parse XML template and extract item value using XPath key
|
|
1424
|
+
def read_xml_template_item(item)
|
|
1425
|
+
require 'nokogiri'
|
|
1426
|
+
doc = Nokogiri::XML(@template.to_s)
|
|
1427
|
+
node = doc.xpath(item.key).first
|
|
1428
|
+
return nil unless node
|
|
1429
|
+
node.text
|
|
1430
|
+
end
|
|
1431
|
+
|
|
1361
1432
|
def handle_limits_states(item, value)
|
|
1362
1433
|
# Retrieve limits state for the given value
|
|
1363
1434
|
limits_state = item.state_colors[value]
|
|
@@ -331,8 +331,8 @@ module OpenC3
|
|
|
331
331
|
end
|
|
332
332
|
end # def to_config
|
|
333
333
|
|
|
334
|
-
def to_xtce(output_dir)
|
|
335
|
-
XtceConverter.convert(@commands, @telemetry, output_dir)
|
|
334
|
+
def to_xtce(output_dir, time_association_name)
|
|
335
|
+
XtceConverter.convert(@commands, @telemetry, output_dir, time_association_name)
|
|
336
336
|
end
|
|
337
337
|
|
|
338
338
|
# Add current packet into hash if it exists
|
|
@@ -65,6 +65,15 @@ module OpenC3
|
|
|
65
65
|
@parser.verify_num_parameters(max_options - 2, max_options, @usage)
|
|
66
66
|
end
|
|
67
67
|
@parser.verify_parameter_naming(1) # Item name is the 1st parameter
|
|
68
|
+
|
|
69
|
+
# ARRAY items cannot have brackets in their name because brackets are used
|
|
70
|
+
# for array indexing in the UI and would cause confusion
|
|
71
|
+
if @parser.keyword.include?('ARRAY')
|
|
72
|
+
item_name = @parser.parameters[0]
|
|
73
|
+
if item_name.include?('[') || item_name.include?(']')
|
|
74
|
+
raise @parser.error("ARRAY items cannot have brackets in their name: #{item_name}", @usage)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
68
77
|
end
|
|
69
78
|
|
|
70
79
|
def create_packet_item(packet, cmd_or_tlm)
|