openc3 6.6.0 → 6.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/openc3cli +66 -10
- data/data/config/command_modifiers.yaml +3 -3
- data/data/config/interface_modifiers.yaml +1 -1
- data/data/config/table_manager.yaml +1 -1
- data/data/config/telemetry_modifiers.yaml +3 -3
- data/data/config/widgets.yaml +2 -2
- data/lib/openc3/accessors.rb +1 -1
- data/lib/openc3/api/settings_api.rb +8 -0
- data/lib/openc3/api/stash_api.rb +1 -1
- data/lib/openc3/api/tlm_api.rb +93 -14
- data/lib/openc3/core_ext/kernel.rb +3 -3
- data/lib/openc3/logs/log_writer.rb +16 -12
- data/lib/openc3/microservices/plugin_microservice.rb +2 -2
- data/lib/openc3/models/cvt_model.rb +140 -3
- data/lib/openc3/models/plugin_model.rb +7 -2
- data/lib/openc3/models/plugin_store_model.rb +70 -0
- data/lib/openc3/models/target_model.rb +26 -0
- data/lib/openc3/models/tool_model.rb +1 -1
- data/lib/openc3/packets/packet.rb +3 -3
- data/lib/openc3/packets/parsers/state_parser.rb +7 -1
- data/lib/openc3/packets/structure.rb +9 -2
- data/lib/openc3/script/calendar.rb +10 -10
- data/lib/openc3/script/commands.rb +1 -1
- data/lib/openc3/script/script_runner.rb +7 -2
- data/lib/openc3/script/tables.rb +3 -3
- data/lib/openc3/utilities/authorization.rb +1 -1
- data/lib/openc3/utilities/cosmos_rails_formatter.rb +1 -1
- data/lib/openc3/utilities/logger.rb +1 -1
- data/lib/openc3/utilities/running_script.rb +5 -1
- 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 +59 -2
@@ -14,12 +14,14 @@
|
|
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
|
21
21
|
# if purchased from OpenC3, Inc.
|
22
22
|
|
23
|
+
require 'pg'
|
24
|
+
require 'thread'
|
23
25
|
require 'openc3/utilities/store'
|
24
26
|
require 'openc3/utilities/store_queued'
|
25
27
|
require 'openc3/models/target_model'
|
@@ -28,6 +30,8 @@ module OpenC3
|
|
28
30
|
class CvtModel
|
29
31
|
@@packet_cache = {}
|
30
32
|
@@override_cache = {}
|
33
|
+
@@conn = nil
|
34
|
+
@@conn_mutex = Mutex.new
|
31
35
|
|
32
36
|
VALUE_TYPES = [:RAW, :CONVERTED, :FORMATTED, :WITH_UNITS]
|
33
37
|
def self.build_json_from_packet(packet)
|
@@ -117,22 +121,149 @@ module OpenC3
|
|
117
121
|
end
|
118
122
|
end
|
119
123
|
|
124
|
+
def self.tsdb_lookup(items, start_time:, end_time: nil)
|
125
|
+
tables = {}
|
126
|
+
names = []
|
127
|
+
nil_count = 0
|
128
|
+
items.each do |item|
|
129
|
+
target_name, packet_name, item_name, value_type, limits = item
|
130
|
+
# They will all be nil when item is a nil value
|
131
|
+
# A nil value indicates a value that does not exist as returned by get_tlm_available
|
132
|
+
if item_name.nil?
|
133
|
+
# We know timestamp always exists so we can use it to fill in the nil value
|
134
|
+
names << "timestamp as __nil#{nil_count}"
|
135
|
+
nil_count += 1
|
136
|
+
next
|
137
|
+
end
|
138
|
+
# See https://questdb.com/docs/reference/api/ilp/advanced-settings/#name-restrictions
|
139
|
+
table_name = "#{target_name}__#{packet_name}".gsub(/[?,'"\/:\)\(\+\*\%~]/, '_')
|
140
|
+
tables[table_name] = 1
|
141
|
+
index = tables.find_index {|k,v| k == table_name }
|
142
|
+
# See https://questdb.com/docs/reference/api/ilp/advanced-settings/#name-restrictions
|
143
|
+
# NOTE: Semicolon added as it appears invalid
|
144
|
+
item_name = item_name.gsub(/[?\.,'"\\\/:\)\(\+\-\*\%~;]/, '_')
|
145
|
+
case value_type
|
146
|
+
when 'WITH_UNITS'
|
147
|
+
names << "\"T#{index}.#{item_name}__U\""
|
148
|
+
when 'FORMATTED'
|
149
|
+
names << "\"T#{index}.#{item_name}__F\""
|
150
|
+
when 'CONVERTED'
|
151
|
+
names << "\"T#{index}.#{item_name}__C\""
|
152
|
+
else
|
153
|
+
names << "\"T#{index}.#{item_name}\""
|
154
|
+
end
|
155
|
+
if limits
|
156
|
+
names << "\"T#{index}.#{item_name}__L\""
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Build the SQL query
|
161
|
+
query = "SELECT #{names.join(", ")} FROM "
|
162
|
+
tables.each_with_index do |(table_name, _), index|
|
163
|
+
if index == 0
|
164
|
+
query += "#{table_name} as T#{index} "
|
165
|
+
else
|
166
|
+
query += "ASOF JOIN #{table_name} as T#{index} "
|
167
|
+
end
|
168
|
+
end
|
169
|
+
if start_time && !end_time
|
170
|
+
query += "WHERE T0.timestamp < '#{start_time}' LIMIT -1"
|
171
|
+
elsif start_time && end_time
|
172
|
+
query += "WHERE T0.timestamp >= '#{start_time}' AND T0.timestamp < '#{end_time}'"
|
173
|
+
end
|
174
|
+
|
175
|
+
retry_count = 0
|
176
|
+
begin
|
177
|
+
@@conn_mutex.synchronize do
|
178
|
+
@@conn ||= PG::Connection.new(host: ENV['OPENC3_TSDB_HOSTNAME'],
|
179
|
+
port: ENV['OPENC3_TSDB_QUERY_PORT'],
|
180
|
+
user: ENV['OPENC3_TSDB_USERNAME'],
|
181
|
+
password: ENV['OPENC3_TSDB_PASSWORD'],
|
182
|
+
dbname: 'qdb')
|
183
|
+
# Default connection is all strings but we want to map to the correct types
|
184
|
+
if @@conn.type_map_for_results.is_a? PG::TypeMapAllStrings
|
185
|
+
# TODO: This doesn't seem to be round tripping UINT64 correctly
|
186
|
+
# Try playback with P_2.2,2 and P(:6;): from the DEMO
|
187
|
+
@@conn.type_map_for_results = PG::BasicTypeMapForResults.new @@conn
|
188
|
+
end
|
189
|
+
|
190
|
+
result = @@conn.exec(query)
|
191
|
+
if result.nil? or result.ntuples == 0
|
192
|
+
return {}
|
193
|
+
else
|
194
|
+
data = []
|
195
|
+
# Build up a results set that is an array of arrays
|
196
|
+
# Each nested array is a set of 2 items: [value, limits state]
|
197
|
+
# If the item does not have limits the limits state is nil
|
198
|
+
result.each_with_index do |tuples, index|
|
199
|
+
data[index] ||= []
|
200
|
+
row_index = 0
|
201
|
+
tuples.each do |tuple|
|
202
|
+
if tuple[0].include?("__L")
|
203
|
+
data[index][row_index - 1][1] = tuple[1]
|
204
|
+
elsif tuple[0] =~ /^__nil/
|
205
|
+
data[index][row_index] = [nil, nil]
|
206
|
+
row_index += 1
|
207
|
+
else
|
208
|
+
data[index][row_index] = [tuple[1], nil]
|
209
|
+
row_index += 1
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
# If we only have one row then we return a single array
|
214
|
+
if result.ntuples == 1
|
215
|
+
data = data[0]
|
216
|
+
end
|
217
|
+
return data
|
218
|
+
end
|
219
|
+
end
|
220
|
+
rescue IOError, PG::Error => e
|
221
|
+
# Retry the query because various errors can occur that are recoverable
|
222
|
+
retry_count += 1
|
223
|
+
if retry_count > 4
|
224
|
+
# After the 5th retry just raise the error
|
225
|
+
raise "Error querying QuestDB: #{e.message}"
|
226
|
+
end
|
227
|
+
Logger.warn("QuestDB: Retrying due to error: #{e.message}")
|
228
|
+
Logger.warn("QuestDB: Last query: #{query}") # Log the last query for debugging
|
229
|
+
@@conn_mutex.synchronize do
|
230
|
+
if @@conn and !@@conn.finished?
|
231
|
+
@@conn.finish()
|
232
|
+
end
|
233
|
+
@@conn = nil # Force the new connection
|
234
|
+
end
|
235
|
+
sleep 0.1
|
236
|
+
retry
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
120
240
|
# Return all item values and limit state from the CVT
|
121
241
|
#
|
122
242
|
# @param items [Array<String>] Items to return. Must be formatted as TGT__PKT__ITEM__TYPE
|
123
243
|
# @param stale_time [Integer] Time in seconds from Time.now that value will be marked stale
|
124
244
|
# @return [Array] Array of values
|
125
|
-
def self.get_tlm_values(items, stale_time: 30, cache_timeout: nil, scope: $openc3_scope)
|
245
|
+
def self.get_tlm_values(items, stale_time: 30, cache_timeout: nil, start_time: nil, end_time: nil, scope: $openc3_scope)
|
126
246
|
now = Time.now
|
127
247
|
results = []
|
128
248
|
lookups = []
|
129
249
|
packet_lookup = {}
|
130
250
|
overrides = {}
|
251
|
+
|
252
|
+
# If a start_time is passed we're doing a QuestDB lookup and directly return the results
|
253
|
+
# TODO: This currently does NOT support the override values
|
254
|
+
if start_time
|
255
|
+
return tsdb_lookup(items, start_time: start_time, end_time: end_time)
|
256
|
+
end
|
257
|
+
|
131
258
|
# First generate a lookup hash of all the items represented so we can query the CVT
|
132
259
|
items.each { |item| _parse_item(now, lookups, overrides, item, cache_timeout: cache_timeout, scope: scope) }
|
133
260
|
|
134
261
|
now = now.to_f
|
135
262
|
lookups.each do |target_packet_key, target_name, packet_name, value_keys|
|
263
|
+
if target_packet_key.nil?
|
264
|
+
results << [nil, nil]
|
265
|
+
next
|
266
|
+
end
|
136
267
|
unless packet_lookup[target_packet_key]
|
137
268
|
packet_lookup[target_packet_key] = get(target_name: target_name, packet_name: packet_name, cache_timeout: cache_timeout, scope: scope)
|
138
269
|
end
|
@@ -161,7 +292,8 @@ module OpenC3
|
|
161
292
|
if hash.key?(value_keys[-1])
|
162
293
|
item_result[1] = nil
|
163
294
|
else
|
164
|
-
|
295
|
+
item_result[0] = nil
|
296
|
+
item_result[1] = nil
|
165
297
|
end
|
166
298
|
end
|
167
299
|
end
|
@@ -335,6 +467,11 @@ module OpenC3
|
|
335
467
|
# return an ordered array of hash with keys
|
336
468
|
def self._parse_item(now, lookups, overrides, item, cache_timeout:, scope:)
|
337
469
|
target_name, packet_name, item_name, value_type = item
|
470
|
+
# They will all be nil when item is a nil value
|
471
|
+
if item_name.nil?
|
472
|
+
lookups << nil
|
473
|
+
return
|
474
|
+
end
|
338
475
|
|
339
476
|
# We build lookup keys by including all the less formatted types to gracefully degrade lookups
|
340
477
|
# This allows the user to specify WITH_UNITS and if there is no conversions it will simply return the RAW value
|
@@ -55,6 +55,7 @@ module OpenC3
|
|
55
55
|
attr_accessor :variables
|
56
56
|
attr_accessor :plugin_txt_lines
|
57
57
|
attr_accessor :needs_dependencies
|
58
|
+
attr_accessor :store_id
|
58
59
|
|
59
60
|
# NOTE: The following three class methods are used by the ModelController
|
60
61
|
# and are reimplemented to enable various Model class methods to work
|
@@ -72,7 +73,7 @@ module OpenC3
|
|
72
73
|
|
73
74
|
# Called by the PluginsController to parse the plugin variables
|
74
75
|
# Doesn't actually create the plugin during the phase
|
75
|
-
def self.install_phase1(gem_file_path, existing_variables: nil, existing_plugin_txt_lines: nil, process_existing: false, scope:, validate_only: false)
|
76
|
+
def self.install_phase1(gem_file_path, existing_variables: nil, existing_plugin_txt_lines: nil, store_id: nil, process_existing: false, scope:, validate_only: false)
|
76
77
|
gem_name = File.basename(gem_file_path).split("__")[0]
|
77
78
|
|
78
79
|
temp_dir = Dir.mktmpdir
|
@@ -130,7 +131,8 @@ module OpenC3
|
|
130
131
|
end
|
131
132
|
end
|
132
133
|
|
133
|
-
|
134
|
+
store_id = Integer(store_id) if store_id
|
135
|
+
model = PluginModel.new(name: gem_name, variables: variables, plugin_txt_lines: plugin_txt_lines, store_id: store_id, scope: scope)
|
134
136
|
result = model.as_json(:allow_nan => true)
|
135
137
|
result['existing_plugin_txt_lines'] = existing_plugin_txt_lines if existing_plugin_txt_lines and not process_existing and existing_plugin_txt_lines != result['plugin_txt_lines']
|
136
138
|
return result
|
@@ -299,6 +301,7 @@ module OpenC3
|
|
299
301
|
variables: {},
|
300
302
|
plugin_txt_lines: [],
|
301
303
|
needs_dependencies: false,
|
304
|
+
store_id: nil,
|
302
305
|
updated_at: nil,
|
303
306
|
scope:
|
304
307
|
)
|
@@ -306,6 +309,7 @@ module OpenC3
|
|
306
309
|
@variables = variables
|
307
310
|
@plugin_txt_lines = plugin_txt_lines
|
308
311
|
@needs_dependencies = ConfigParser.handle_true_false(needs_dependencies)
|
312
|
+
@store_id = store_id
|
309
313
|
end
|
310
314
|
|
311
315
|
def create(update: false, force: false, queued: false)
|
@@ -319,6 +323,7 @@ module OpenC3
|
|
319
323
|
'variables' => @variables,
|
320
324
|
'plugin_txt_lines' => @plugin_txt_lines,
|
321
325
|
'needs_dependencies' => @needs_dependencies,
|
326
|
+
'store_id' => @store_id,
|
322
327
|
'updated_at' => @updated_at
|
323
328
|
}
|
324
329
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2025 OpenC3, Inc.
|
4
|
+
# All Rights Reserved.
|
5
|
+
#
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
7
|
+
# under the terms of the GNU Affero General Public License
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
|
16
|
+
# This file may also be used under the terms of a commercial license
|
17
|
+
# if purchased from OpenC3, Inc.
|
18
|
+
|
19
|
+
require 'openc3/models/model'
|
20
|
+
require 'openc3/utilities/store'
|
21
|
+
|
22
|
+
module OpenC3
|
23
|
+
class PluginStoreModel < Model
|
24
|
+
PRIMARY_KEY = 'openc3_plugin_store'
|
25
|
+
|
26
|
+
def self.set(plugin_store_data)
|
27
|
+
Store.set(PRIMARY_KEY, plugin_store_data)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.all()
|
31
|
+
Store.get(PRIMARY_KEY)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.plugin_store_error(message)
|
35
|
+
Store.set(PRIMARY_KEY, [{
|
36
|
+
date: Time.now.utc.iso8601,
|
37
|
+
title: 'Plugin Store Error',
|
38
|
+
body: message,
|
39
|
+
error: true,
|
40
|
+
}].to_json)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.get_by_id(id)
|
44
|
+
plugins = JSON.parse(all()) rescue []
|
45
|
+
plugins.find { |plugin| plugin["id"] == Integer(id) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.update
|
49
|
+
setting = SettingModel.get(name: 'store_url', scope: 'DEFAULT')
|
50
|
+
store_url = setting['data'] if setting
|
51
|
+
store_url = 'https://store.openc3.com' if store_url.nil? or store_url.strip.empty?
|
52
|
+
conn = Faraday.new(
|
53
|
+
url: store_url,
|
54
|
+
)
|
55
|
+
response = conn.get('/cosmos_plugins/json')
|
56
|
+
if response.success?
|
57
|
+
self.set(response.body)
|
58
|
+
else
|
59
|
+
self.plugin_store_error("Error contacting plugin store at #{store_url} (status: #{response.status})")
|
60
|
+
end
|
61
|
+
rescue Exception => e
|
62
|
+
self.plugin_store_error("Error contacting plugin store at #{store_url}. #{e.message})")
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.ensure_exists
|
66
|
+
plugins = self.all()
|
67
|
+
self.update() if plugins.nil? or plugins.length.zero? or plugins[0]['error']
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -1088,6 +1088,25 @@ module OpenC3
|
|
1088
1088
|
Logger.info "Configured microservice #{microservice_name}"
|
1089
1089
|
end
|
1090
1090
|
|
1091
|
+
def deploy_tsdb_microservice(gem_path, variables, topics, instance = nil, parent = nil)
|
1092
|
+
microservice_name = "#{@scope}__TSDB#{instance}__#{@name}"
|
1093
|
+
microservice = MicroserviceModel.new(
|
1094
|
+
name: microservice_name,
|
1095
|
+
folder_name: @folder_name,
|
1096
|
+
cmd: ["python", "quest_microservice.py", microservice_name],
|
1097
|
+
work_dir: "/openc3/python/openc3/microservices",
|
1098
|
+
topics: topics,
|
1099
|
+
plugin: @plugin,
|
1100
|
+
parent: nil,
|
1101
|
+
needs_dependencies: @needs_dependencies,
|
1102
|
+
shard: @shard,
|
1103
|
+
scope: @scope
|
1104
|
+
)
|
1105
|
+
microservice.create
|
1106
|
+
microservice.deploy(gem_path, variables)
|
1107
|
+
Logger.info "Configured microservice #{microservice_name}"
|
1108
|
+
end
|
1109
|
+
|
1091
1110
|
def deploy_reducer_microservice(gem_path, variables, topics, instance = nil, parent = nil)
|
1092
1111
|
microservice_name = "#{@scope}__REDUCER#{instance}__#{@name}"
|
1093
1112
|
microservice = MicroserviceModel.new(
|
@@ -1250,6 +1269,13 @@ module OpenC3
|
|
1250
1269
|
deploy_decom_microservice(system.targets[@name], gem_path, variables, topics, instance, parent)
|
1251
1270
|
end
|
1252
1271
|
|
1272
|
+
# TSDB Microservice
|
1273
|
+
if ENV['OPENC3_TSDB_HOSTNAME'] and ENV['OPENC3_TSDB_QUERY_PORT'] and ENV['OPENC3_TSDB_INGEST_PORT'] and ENV['OPENC3_TSDB_USERNAME'] and ENV['OPENC3_TSDB_PASSWORD']
|
1274
|
+
deploy_target_microservices('TSDB', decom_topic_list, "#{@scope}__DECOM__{#{@name}}") do |topics, instance, parent|
|
1275
|
+
deploy_tsdb_microservice(gem_path, variables, topics, instance, parent)
|
1276
|
+
end
|
1277
|
+
end
|
1278
|
+
|
1253
1279
|
# Reducer Microservice
|
1254
1280
|
unless @reducer_disable
|
1255
1281
|
# TODO: Does Reducer even need a topic list?
|
@@ -184,7 +184,7 @@ module OpenC3
|
|
184
184
|
end
|
185
185
|
end
|
186
186
|
|
187
|
-
if @url and !@url.start_with?('/') and
|
187
|
+
if @url and !@url.start_with?('/') and @url !~ URI::regexp
|
188
188
|
raise "URL must be a full URL (http://domain.com/path) or a relative path (/path)"
|
189
189
|
end
|
190
190
|
|
@@ -334,7 +334,7 @@ module OpenC3
|
|
334
334
|
synchronize() do
|
335
335
|
begin
|
336
336
|
internal_buffer_equals(buffer)
|
337
|
-
rescue RuntimeError
|
337
|
+
rescue RuntimeError
|
338
338
|
Logger.instance.error "#{@target_name} #{@packet_name} received with actual packet length of #{buffer.length} but defined length of #{@defined_length}"
|
339
339
|
end
|
340
340
|
@read_conversion_cache.clear if @read_conversion_cache
|
@@ -1315,7 +1315,7 @@ module OpenC3
|
|
1315
1315
|
|
1316
1316
|
begin
|
1317
1317
|
current_value = read(item.name, :RAW)
|
1318
|
-
|
1318
|
+
|
1319
1319
|
case current_value
|
1320
1320
|
when Array
|
1321
1321
|
# For arrays, create a new array of zeros with the same size
|
@@ -1325,7 +1325,7 @@ module OpenC3
|
|
1325
1325
|
when :FLOAT
|
1326
1326
|
obfuscated_value = Array.new(current_value.size, 0.0)
|
1327
1327
|
when :STRING, :BLOCK
|
1328
|
-
obfuscated_value = Array.new(current_value.size) { |i|
|
1328
|
+
obfuscated_value = Array.new(current_value.size) { |i|
|
1329
1329
|
"\x00" * current_value[i].length if current_value[i]
|
1330
1330
|
}
|
1331
1331
|
else
|
@@ -80,7 +80,13 @@ module OpenC3
|
|
80
80
|
if data_type == :STRING || data_type == :BLOCK
|
81
81
|
@parser.parameters[1]
|
82
82
|
else
|
83
|
-
@parser.parameters[1].convert_to_value
|
83
|
+
value = @parser.parameters[1].convert_to_value
|
84
|
+
# Check if the value is a string, which indicates an error in parsing
|
85
|
+
# except for 'ANY' which is a valid state value
|
86
|
+
if value.is_a?(String) and value != "ANY"
|
87
|
+
raise @parser.error("Invalid state value #{value} for data type #{data_type}.", @usage)
|
88
|
+
end
|
89
|
+
return value
|
84
90
|
end
|
85
91
|
end
|
86
92
|
|
@@ -603,7 +603,7 @@ module OpenC3
|
|
603
603
|
if item.variable_bit_size
|
604
604
|
# Bit size is determined by length field
|
605
605
|
length_value = self.read(item.variable_bit_size['length_item_name'], :CONVERTED)
|
606
|
-
if item.data_type == :INT or item.data_type == :UINT and not item.original_array_size
|
606
|
+
if (item.data_type == :INT or item.data_type == :UINT) and not item.original_array_size
|
607
607
|
case length_value
|
608
608
|
when 0
|
609
609
|
return 6
|
@@ -641,7 +641,14 @@ module OpenC3
|
|
641
641
|
# Calculate the actual current size of this variable length item
|
642
642
|
new_bit_size = calculate_total_bit_size(item)
|
643
643
|
|
644
|
-
if item.
|
644
|
+
if item.original_array_size
|
645
|
+
# Array size has changed from original - so we need to adjust everything after this item
|
646
|
+
# This includes items that may have the same bit_offset as the variable length item because it
|
647
|
+
# started out at zero bit_size
|
648
|
+
if item.original_array_size != new_bit_size
|
649
|
+
adjustment += (new_bit_size - item.original_array_size)
|
650
|
+
end
|
651
|
+
elsif item.original_bit_size != new_bit_size
|
645
652
|
# Bit size has changed from original - so we need to adjust everything after this item
|
646
653
|
# This includes items that may have the same bit_offset as the variable length item because it
|
647
654
|
# started out at zero bit_size
|
@@ -25,7 +25,7 @@ module OpenC3
|
|
25
25
|
|
26
26
|
def list_timelines(scope: $openc3_scope)
|
27
27
|
response = $api_server.request('get', "/openc3-api/timeline", scope: scope)
|
28
|
-
return
|
28
|
+
return _cal_handle_response(response, 'Failed to list timelines')
|
29
29
|
end
|
30
30
|
|
31
31
|
def create_timeline(name, color: nil, scope: $openc3_scope)
|
@@ -33,19 +33,19 @@ module OpenC3
|
|
33
33
|
data['name'] = name
|
34
34
|
data['color'] = color if color
|
35
35
|
response = $api_server.request('post', "/openc3-api/timeline", data: data, json: true, scope: scope)
|
36
|
-
return
|
36
|
+
return _cal_handle_response(response, 'Failed to create timeline')
|
37
37
|
end
|
38
38
|
|
39
39
|
def get_timeline(name, scope: $openc3_scope)
|
40
40
|
response = $api_server.request('get', "/openc3-api/timeline/#{name}", scope: scope)
|
41
|
-
return
|
41
|
+
return _cal_handle_response(response, 'Failed to get timeline')
|
42
42
|
end
|
43
43
|
|
44
44
|
def set_timeline_color(name, color, scope: $openc3_scope)
|
45
45
|
post_data = {}
|
46
46
|
post_data['color'] = color
|
47
47
|
response = $api_server.request('post', "/openc3-api/timeline/#{name}/color", data: post_data, json: true, scope: scope)
|
48
|
-
return
|
48
|
+
return _cal_handle_response(response, 'Failed to set timeline color')
|
49
49
|
end
|
50
50
|
|
51
51
|
def delete_timeline(name, force: false, scope: $openc3_scope)
|
@@ -54,7 +54,7 @@ module OpenC3
|
|
54
54
|
url += "?force=true"
|
55
55
|
end
|
56
56
|
response = $api_server.request('delete', url, scope: scope)
|
57
|
-
return
|
57
|
+
return _cal_handle_response(response, 'Failed to delete timeline')
|
58
58
|
end
|
59
59
|
|
60
60
|
def create_timeline_activity(name, kind:, start:, stop:, data: {}, scope: $openc3_scope)
|
@@ -69,12 +69,12 @@ module OpenC3
|
|
69
69
|
post_data['kind'] = kind
|
70
70
|
post_data['data'] = data
|
71
71
|
response = $api_server.request('post', "/openc3-api/timeline/#{name}/activities", data: post_data, json: true, scope: scope)
|
72
|
-
return
|
72
|
+
return _cal_handle_response(response, 'Failed to create timeline activity')
|
73
73
|
end
|
74
74
|
|
75
75
|
def get_timeline_activity(name, start, uuid, scope: $openc3_scope)
|
76
76
|
response = $api_server.request('get', "/openc3-api/timeline/#{name}/activity/#{start}/#{uuid}", scope: scope)
|
77
|
-
return
|
77
|
+
return _cal_handle_response(response, 'Failed to get timeline activity')
|
78
78
|
end
|
79
79
|
|
80
80
|
def get_timeline_activities(name, start: nil, stop: nil, limit: nil, scope: $openc3_scope)
|
@@ -86,16 +86,16 @@ module OpenC3
|
|
86
86
|
url += "?limit=#{limit}"
|
87
87
|
end
|
88
88
|
response = $api_server.request('get', url, scope: scope)
|
89
|
-
return
|
89
|
+
return _cal_handle_response(response, 'Failed to get timeline activities')
|
90
90
|
end
|
91
91
|
|
92
92
|
def delete_timeline_activity(name, start, uuid, scope: $openc3_scope)
|
93
93
|
response = $api_server.request('delete', "/openc3-api/timeline/#{name}/activity/#{start}/#{uuid}", scope: scope)
|
94
|
-
return
|
94
|
+
return _cal_handle_response(response, 'Failed to delete timeline activity')
|
95
95
|
end
|
96
96
|
|
97
97
|
# Helper method to handle the response
|
98
|
-
def
|
98
|
+
def _cal_handle_response(response, error_message)
|
99
99
|
return nil if response.nil?
|
100
100
|
if response.status >= 400
|
101
101
|
result = JSON.parse(response.body, :allow_nan => true, :create_additions => true)
|
@@ -71,7 +71,7 @@ module OpenC3
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
-
def script_run(filename, disconnect: false, environment: nil, scope: $openc3_scope)
|
74
|
+
def script_run(filename, disconnect: false, environment: nil, suite_runner: nil, scope: $openc3_scope)
|
75
75
|
if disconnect
|
76
76
|
endpoint = "/script-api/scripts/#{filename}/run/disconnect"
|
77
77
|
else
|
@@ -87,8 +87,13 @@ module OpenC3
|
|
87
87
|
else
|
88
88
|
env_data = []
|
89
89
|
end
|
90
|
+
data = { environment: env_data }
|
91
|
+
if suite_runner
|
92
|
+
# TODO 7.0: Should suiteRunner be snake case?
|
93
|
+
data['suiteRunner'] = suite_runner
|
94
|
+
end
|
90
95
|
# NOTE: json: true causes json_api_object to JSON generate and set the Content-Type to json
|
91
|
-
response = $script_runner_api_server.request('post', endpoint, json: true, data:
|
96
|
+
response = $script_runner_api_server.request('post', endpoint, json: true, data: data, scope: scope)
|
92
97
|
if response.nil? || response.status != 200
|
93
98
|
_script_response_error(response, "Failed to run #{filename}", scope: scope)
|
94
99
|
else
|
data/lib/openc3/script/tables.rb
CHANGED
@@ -24,7 +24,7 @@ module OpenC3
|
|
24
24
|
post_data = {}
|
25
25
|
post_data['definition'] = definition
|
26
26
|
response = $api_server.request('post', '/openc3-api/tables/generate', json: true, data: post_data, scope: scope)
|
27
|
-
return
|
27
|
+
return _tables_handle_response(response, 'Failed to create binary')
|
28
28
|
end
|
29
29
|
|
30
30
|
def table_create_report(filename, definition, table_name: nil, scope: $openc3_scope)
|
@@ -33,11 +33,11 @@ module OpenC3
|
|
33
33
|
post_data['definition'] = definition
|
34
34
|
post_data['table_name'] = table_name if table_name
|
35
35
|
response = $api_server.request('post', '/openc3-api/tables/report', json: true, data: post_data, scope: scope)
|
36
|
-
return
|
36
|
+
return _tables_handle_response(response, 'Failed to create report')
|
37
37
|
end
|
38
38
|
|
39
39
|
# Helper method to handle the response
|
40
|
-
def
|
40
|
+
def _tables_handle_response(response, error_message)
|
41
41
|
return nil if response.nil?
|
42
42
|
if response.status >= 400
|
43
43
|
result = JSON.parse(response.body, :allow_nan => true, :create_additions => true)
|
@@ -179,7 +179,7 @@ module OpenC3
|
|
179
179
|
data = { time: time.to_nsec_from_epoch, '@timestamp' => time.iso8601(6), level: log_level }
|
180
180
|
data[:microservice_name] = @microservice_name if @microservice_name
|
181
181
|
data[:detail] = @detail_string if @detail_string
|
182
|
-
data[:user] = user if user #
|
182
|
+
data[:user] = user if user # Enterprise: If a user is passed, put its name. Don't include user data if no user was passed.
|
183
183
|
if block_given?
|
184
184
|
message = yield
|
185
185
|
end
|
@@ -771,7 +771,11 @@ class RunningScript
|
|
771
771
|
def run
|
772
772
|
if @script_status.suite_runner
|
773
773
|
@script_status.suite_runner = JSON.parse(@script_status.suite_runner, :allow_nan => true, :create_additions => true) # Convert to hash
|
774
|
-
|
774
|
+
if @script_status.suite_runner['options']
|
775
|
+
parse_options(@script_status.suite_runner['options'])
|
776
|
+
else
|
777
|
+
parse_options({}) # Set default options
|
778
|
+
end
|
775
779
|
if @script_status.suite_runner['script']
|
776
780
|
run_text("OpenC3::SuiteRunner.start(#{@script_status.suite_runner['suite']}, #{@script_status.suite_runner['group']}, '#{@script_status.suite_runner['script']}')", initial_filename: "SCRIPTRUNNER")
|
777
781
|
elsif script_status.suite_runner['group']
|
data/lib/openc3/version.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
-
OPENC3_VERSION = '6.
|
3
|
+
OPENC3_VERSION = '6.7.0'
|
4
4
|
module OpenC3
|
5
5
|
module Version
|
6
6
|
MAJOR = '6'
|
7
|
-
MINOR = '
|
7
|
+
MINOR = '7'
|
8
8
|
PATCH = '0'
|
9
9
|
OTHER = ''
|
10
|
-
BUILD = '
|
10
|
+
BUILD = '6c4726a21dd004109cf840f83a99f5a8614cfbe3'
|
11
11
|
end
|
12
|
-
VERSION = '6.
|
13
|
-
GEM_VERSION = '6.
|
12
|
+
VERSION = '6.7.0'
|
13
|
+
GEM_VERSION = '6.7.0'
|
14
14
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "<%= tool_name %>",
|
3
|
-
"version": "6.
|
3
|
+
"version": "6.7.0",
|
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.
|
26
|
+
"@openc3/js-common": "6.7.0",
|
27
27
|
"rxjs": "~7.8.0",
|
28
28
|
"single-spa": "^5.9.5",
|
29
29
|
"single-spa-angular": "^9.2.0",
|