openc3 5.10.1 → 5.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +3 -2
- data/data/config/target.yaml +9 -0
- data/ext/openc3/ext/packet/packet.c +3 -0
- data/ext/openc3/ext/reducer_microservice/extconf.rb +13 -0
- data/ext/openc3/ext/reducer_microservice/reducer_microservice.c +165 -0
- data/ext/openc3/ext/structure/structure.c +7 -9
- data/lib/openc3/accessors/accessor.rb +53 -3
- data/lib/openc3/accessors/binary_accessor.rb +16 -0
- data/lib/openc3/accessors/cbor_accessor.rb +3 -3
- data/lib/openc3/accessors/form_accessor.rb +78 -0
- data/lib/openc3/accessors/http_accessor.rb +145 -0
- data/lib/openc3/accessors/json_accessor.rb +19 -3
- data/lib/openc3/accessors/xml_accessor.rb +18 -1
- data/lib/openc3/accessors.rb +3 -1
- data/lib/openc3/config/config_parser.rb +7 -5
- data/lib/openc3/config/meta_config_parser.rb +1 -1
- data/lib/openc3/core_ext/string.rb +16 -1
- data/lib/openc3/interfaces/http_client_interface.rb +202 -0
- data/lib/openc3/interfaces/http_server_interface.rb +183 -0
- data/lib/openc3/interfaces/interface.rb +86 -16
- data/lib/openc3/interfaces/mqtt_interface.rb +6 -5
- data/lib/openc3/interfaces/protocols/burst_protocol.rb +11 -11
- data/lib/openc3/interfaces/protocols/cobs_protocol.rb +7 -7
- data/lib/openc3/interfaces/protocols/crc_protocol.rb +7 -7
- data/lib/openc3/interfaces/protocols/length_protocol.rb +6 -6
- data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +9 -5
- data/lib/openc3/interfaces/protocols/protocol.rb +8 -6
- data/lib/openc3/interfaces/protocols/slip_protocol.rb +8 -8
- data/lib/openc3/interfaces/protocols/template_protocol.rb +6 -7
- data/lib/openc3/interfaces/protocols/terminated_protocol.rb +4 -4
- data/lib/openc3/interfaces/simulated_target_interface.rb +2 -0
- data/lib/openc3/interfaces/stream_interface.rb +6 -4
- data/lib/openc3/interfaces/tcpip_server_interface.rb +2 -0
- data/lib/openc3/interfaces/udp_interface.rb +8 -5
- data/lib/openc3/interfaces.rb +2 -0
- data/lib/openc3/logs/buffered_packet_log_writer.rb +6 -7
- data/lib/openc3/logs/log_writer.rb +2 -10
- data/lib/openc3/logs/packet_log_constants.rb +13 -3
- data/lib/openc3/logs/packet_log_reader.rb +35 -98
- data/lib/openc3/logs/packet_log_writer.rb +24 -62
- data/lib/openc3/logs/text_log_writer.rb +32 -6
- data/lib/openc3/microservices/cleanup_microservice.rb +23 -16
- data/lib/openc3/microservices/decom_microservice.rb +8 -20
- data/lib/openc3/microservices/log_microservice.rb +3 -1
- data/lib/openc3/microservices/reaction_microservice.rb +22 -11
- data/lib/openc3/microservices/reducer_microservice.rb +174 -130
- data/lib/openc3/{models/notification_model.rb → microservices/scope_cleanup_microservice.rb} +20 -21
- data/lib/openc3/microservices/text_log_microservice.rb +2 -5
- data/lib/openc3/microservices/timeline_microservice.rb +0 -1
- data/lib/openc3/microservices/trigger_group_microservice.rb +0 -1
- data/lib/openc3/migrations/20230915000002_no_scope_log_messages.rb +44 -0
- data/lib/openc3/models/microservice_model.rb +1 -1
- data/lib/openc3/models/scope_model.rb +65 -34
- data/lib/openc3/models/target_model.rb +25 -5
- data/lib/openc3/models/tool_model.rb +14 -2
- data/lib/openc3/models/widget_model.rb +1 -1
- data/lib/openc3/packets/json_packet.rb +10 -2
- data/lib/openc3/packets/packet.rb +30 -9
- data/lib/openc3/packets/packet_config.rb +6 -2
- data/lib/openc3/packets/parsers/packet_item_parser.rb +11 -6
- data/lib/openc3/packets/structure.rb +19 -12
- data/lib/openc3/script/storage.rb +1 -1
- data/lib/openc3/script/web_socket_api.rb +17 -14
- data/lib/openc3/topics/telemetry_topic.rb +2 -1
- data/lib/openc3/utilities/bucket_utilities.rb +2 -0
- data/lib/openc3/utilities/cli_generator.rb +1 -1
- data/lib/openc3/utilities/logger.rb +62 -47
- data/lib/openc3/utilities/metric.rb +19 -1
- data/lib/openc3/utilities/sleeper.rb +3 -1
- data/lib/openc3/utilities/throttle.rb +76 -0
- data/lib/openc3/version.rb +6 -6
- data/templates/tool_angular/package.json +20 -20
- data/templates/tool_angular/yarn.lock +112 -106
- data/templates/tool_react/package.json +16 -18
- data/templates/tool_react/yarn.lock +977 -664
- data/templates/tool_svelte/.prettierrc.js +5 -0
- data/templates/tool_svelte/package.json +18 -18
- data/templates/tool_svelte/src/services/cable.js +1 -1
- data/templates/tool_svelte/src/services/openc3-api.js +173 -173
- data/templates/tool_svelte/yarn.lock +767 -665
- data/templates/tool_vue/package.json +10 -10
- data/templates/tool_vue/yarn.lock +225 -43
- data/templates/widget/package.json +10 -10
- data/templates/widget/yarn.lock +223 -46
- metadata +41 -4
- data/lib/openc3/topics/notifications_topic.rb +0 -31
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'openc3/utilities/migration'
|
2
|
+
require 'openc3/models/microservice_model'
|
3
|
+
|
4
|
+
module OpenC3
|
5
|
+
class NoScopeLogMessages < Migration
|
6
|
+
def self.run
|
7
|
+
# Add NOSCOPE topic to log message microservice for DEFAULT scope
|
8
|
+
model = MicroserviceModel.get_model(name: "DEFAULT__OPENC3__LOG", scope: 'DEFAULT')
|
9
|
+
if model
|
10
|
+
model.topics = ["DEFAULT__openc3_log_messages", "NOSCOPE__openc3_log_messages"]
|
11
|
+
model.update
|
12
|
+
end
|
13
|
+
|
14
|
+
ScopeModel.get_all_models(scope: nil).each do |scope, scope_model|
|
15
|
+
parent = "#{scope}__SCOPEMULTI__#{scope}"
|
16
|
+
|
17
|
+
# Remove NOTIFICATION log microservice from scopes
|
18
|
+
model = MicroserviceModel.get_model(name: "#{scope}__NOTIFICATION__LOG", scope: scope)
|
19
|
+
if model
|
20
|
+
model.destroy
|
21
|
+
end
|
22
|
+
|
23
|
+
# Add Scope Cleanup microservice to scopes
|
24
|
+
model = MicroserviceModel.get_model(name: "#{scope}__SCOPECLEANUP__#{scope}", scope: scope)
|
25
|
+
unless model
|
26
|
+
scope_model.deploy_scopecleanup_microservice("", {}, parent)
|
27
|
+
end
|
28
|
+
|
29
|
+
model = MicroserviceModel.get_model(name: parent, scope: scope)
|
30
|
+
if model
|
31
|
+
model.cmd.delete("#{scope}__NOTIFICATION__LOG")
|
32
|
+
unless model.cmd.include?("#{scope}__SCOPECLEANUP__#{scope}")
|
33
|
+
model.cmd << "#{scope}__SCOPECLEANUP__#{scope}"
|
34
|
+
end
|
35
|
+
model.update
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
unless ENV['OPENC3_NO_MIGRATE']
|
43
|
+
OpenC3::NoScopeLogMessages.run
|
44
|
+
end
|
@@ -221,7 +221,7 @@ module OpenC3
|
|
221
221
|
# Load microservice files
|
222
222
|
data = File.read(filename, mode: "rb")
|
223
223
|
OpenC3.set_working_dir(File.dirname(filename)) do
|
224
|
-
data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable? and File.basename(filename)[0] != '_'
|
224
|
+
data = ERB.new(data.comment_erb(), trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable? and File.basename(filename)[0] != '_'
|
225
225
|
end
|
226
226
|
unless validate_only
|
227
227
|
@bucket.put_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: key, body: data)
|
@@ -32,6 +32,11 @@ module OpenC3
|
|
32
32
|
PRIMARY_KEY = 'openc3_scopes'
|
33
33
|
|
34
34
|
attr_accessor :children
|
35
|
+
attr_accessor :text_log_cycle_time
|
36
|
+
attr_accessor :text_log_cycle_size
|
37
|
+
attr_accessor :text_log_retain_time
|
38
|
+
attr_accessor :tool_log_retain_time
|
39
|
+
attr_accessor :cleanup_poll_time
|
35
40
|
|
36
41
|
# NOTE: The following three class methods are used by the ModelController
|
37
42
|
# and are reimplemented to enable various Model class methods to work
|
@@ -62,8 +67,31 @@ module OpenC3
|
|
62
67
|
end
|
63
68
|
end
|
64
69
|
|
65
|
-
def initialize(name:,
|
66
|
-
|
70
|
+
def initialize(name:,
|
71
|
+
text_log_cycle_time: 600,
|
72
|
+
text_log_cycle_size: 50_000_000,
|
73
|
+
text_log_retain_time: nil,
|
74
|
+
tool_log_retain_time: nil,
|
75
|
+
cleanup_poll_time: 900,
|
76
|
+
updated_at: nil,
|
77
|
+
scope: nil
|
78
|
+
)
|
79
|
+
super(
|
80
|
+
PRIMARY_KEY,
|
81
|
+
name: name,
|
82
|
+
text_log_cycle_time: text_log_cycle_time,
|
83
|
+
text_log_cycle_size: text_log_cycle_size,
|
84
|
+
text_log_retain_time: text_log_retain_time,
|
85
|
+
tool_log_retain_time: tool_log_retain_time,
|
86
|
+
cleanup_poll_time: cleanup_poll_time,
|
87
|
+
updated_at: updated_at,
|
88
|
+
scope: name
|
89
|
+
)
|
90
|
+
@text_log_cycle_time = text_log_cycle_time
|
91
|
+
@text_log_cycle_size = text_log_cycle_size
|
92
|
+
@text_log_retain_time = text_log_retain_time
|
93
|
+
@tool_log_retain_time = tool_log_retain_time
|
94
|
+
@cleanup_poll_time = cleanup_poll_time
|
67
95
|
@children = []
|
68
96
|
end
|
69
97
|
|
@@ -89,41 +117,31 @@ module OpenC3
|
|
89
117
|
|
90
118
|
def as_json(*a)
|
91
119
|
{ 'name' => @name,
|
92
|
-
'updated_at' => @updated_at
|
120
|
+
'updated_at' => @updated_at,
|
121
|
+
'text_log_cycle_time' => @text_log_cycle_time,
|
122
|
+
'text_log_cycle_size' => @text_log_cycle_size,
|
123
|
+
'text_log_retain_time' => @text_log_retain_time,
|
124
|
+
'tool_log_retain_time' => @tool_log_retain_time,
|
125
|
+
'cleanup_poll_time' => @cleanup_poll_time,
|
126
|
+
}
|
93
127
|
end
|
94
128
|
|
95
129
|
def deploy_openc3_log_messages_microservice(gem_path, variables, parent)
|
96
130
|
microservice_name = "#{@scope}__OPENC3__LOG"
|
131
|
+
topics = ["#{@scope}__openc3_log_messages"]
|
132
|
+
# Also log the NOSCOPE messages with this microservice for the DEFAULT scope
|
133
|
+
if @scope == 'DEFAULT'
|
134
|
+
topics << "NOSCOPE__openc3_log_messages"
|
135
|
+
end
|
97
136
|
microservice = MicroserviceModel.new(
|
98
137
|
name: microservice_name,
|
99
138
|
cmd: ["ruby", "text_log_microservice.rb", microservice_name],
|
100
139
|
work_dir: '/openc3/lib/openc3/microservices',
|
101
140
|
options: [
|
102
|
-
|
103
|
-
|
104
|
-
# ["CYCLE_SIZE", "50_000_000"] # Keep at most ~50MB per log
|
105
|
-
],
|
106
|
-
topics: ["#{@scope}__openc3_log_messages"],
|
107
|
-
parent: parent,
|
108
|
-
scope: @scope
|
109
|
-
)
|
110
|
-
microservice.create
|
111
|
-
microservice.deploy(gem_path, variables)
|
112
|
-
@children << microservice_name if parent
|
113
|
-
Logger.info "Configured microservice #{microservice_name}"
|
114
|
-
end
|
115
|
-
|
116
|
-
def deploy_openc3_notifications_microservice(gem_path, variables, parent)
|
117
|
-
microservice_name = "#{@scope}__NOTIFICATION__LOG"
|
118
|
-
microservice = MicroserviceModel.new(
|
119
|
-
name: microservice_name,
|
120
|
-
cmd: ["ruby", "text_log_microservice.rb", microservice_name],
|
121
|
-
work_dir: '/openc3/lib/openc3/microservices',
|
122
|
-
options: [
|
123
|
-
# The following options are optional (600 and 50_000_000 are the defaults)
|
124
|
-
["CYCLE_TIME", "3600"], # Keep at most 1 hour per log
|
141
|
+
["CYCLE_TIME", @text_log_cycle_time],
|
142
|
+
["CYCLE_SIZE", @text_log_cycle_size],
|
125
143
|
],
|
126
|
-
topics:
|
144
|
+
topics: topics,
|
127
145
|
parent: parent,
|
128
146
|
scope: @scope
|
129
147
|
)
|
@@ -192,6 +210,21 @@ module OpenC3
|
|
192
210
|
Logger.info "Configured microservice #{microservice_name}"
|
193
211
|
end
|
194
212
|
|
213
|
+
def deploy_scopecleanup_microservice(gem_path, variables, parent)
|
214
|
+
microservice_name = "#{@scope}__SCOPECLEANUP__#{@scope}"
|
215
|
+
microservice = MicroserviceModel.new(
|
216
|
+
name: microservice_name,
|
217
|
+
cmd: ["ruby", "scope_cleanup_microservice.rb", microservice_name],
|
218
|
+
work_dir: '/openc3/lib/openc3/microservices',
|
219
|
+
parent: parent,
|
220
|
+
scope: @scope
|
221
|
+
)
|
222
|
+
microservice.create
|
223
|
+
microservice.deploy(gem_path, variables)
|
224
|
+
@children << microservice_name if parent
|
225
|
+
Logger.info "Configured microservice #{microservice_name}"
|
226
|
+
end
|
227
|
+
|
195
228
|
def deploy_scopemulti_microservice(gem_path, variables)
|
196
229
|
microservice_name = "#{@scope}__SCOPEMULTI__#{@scope}"
|
197
230
|
microservice = MicroserviceModel.new(
|
@@ -220,17 +253,12 @@ module OpenC3
|
|
220
253
|
# Create UNKNOWN target for display of unknown data
|
221
254
|
model = TargetModel.new(name: "UNKNOWN", scope: @scope)
|
222
255
|
model.create
|
223
|
-
# Not deployed - we only want raw packet logging for UNKNOWN
|
224
|
-
# TODO: Cleanup support
|
225
256
|
|
226
257
|
@parent = "#{@scope}__SCOPEMULTI__#{@scope}"
|
227
258
|
|
228
259
|
# OpenC3 Log Microservice
|
229
260
|
deploy_openc3_log_messages_microservice(gem_path, variables, @parent)
|
230
261
|
|
231
|
-
# Notification Log Microservice
|
232
|
-
deploy_openc3_notifications_microservice(gem_path, variables, @parent)
|
233
|
-
|
234
262
|
# UNKNOWN CommandLog Microservice
|
235
263
|
deploy_unknown_commandlog_microservice(gem_path, variables, @parent)
|
236
264
|
|
@@ -240,6 +268,9 @@ module OpenC3
|
|
240
268
|
# Periodic Microservice
|
241
269
|
deploy_periodic_microservice(gem_path, variables, @parent)
|
242
270
|
|
271
|
+
# Scope Cleanup Microservice
|
272
|
+
deploy_scopecleanup_microservice(gem_path, variables, @parent)
|
273
|
+
|
243
274
|
# Multi Microservice to parent other scope microservices
|
244
275
|
deploy_scopemulti_microservice(gem_path, variables)
|
245
276
|
end
|
@@ -247,9 +278,9 @@ module OpenC3
|
|
247
278
|
def undeploy
|
248
279
|
model = MicroserviceModel.get_model(name: "#{@scope}__SCOPEMULTI__#{@scope}", scope: @scope)
|
249
280
|
model.destroy if model
|
250
|
-
model = MicroserviceModel.get_model(name: "#{@scope}
|
281
|
+
model = MicroserviceModel.get_model(name: "#{@scope}__SCOPECLEANUP__#{@scope}", scope: @scope)
|
251
282
|
model.destroy if model
|
252
|
-
model = MicroserviceModel.get_model(name: "#{@scope}
|
283
|
+
model = MicroserviceModel.get_model(name: "#{@scope}__OPENC3__LOG", scope: @scope)
|
253
284
|
model.destroy if model
|
254
285
|
model = MicroserviceModel.get_model(name: "#{@scope}__COMMANDLOG__UNKNOWN", scope: @scope)
|
255
286
|
model.destroy if model
|
@@ -44,6 +44,7 @@ module OpenC3
|
|
44
44
|
class TargetModel < Model
|
45
45
|
PRIMARY_KEY = 'openc3_targets'
|
46
46
|
VALID_TYPES = %i(CMD TLM)
|
47
|
+
ERB_EXTENSIONS = %w(.txt .rb .py .json .yaml .yml)
|
47
48
|
ITEM_MAP_CACHE_TIMEOUT = 10.0
|
48
49
|
@@item_map_cache = {}
|
49
50
|
|
@@ -346,6 +347,8 @@ module OpenC3
|
|
346
347
|
cleanup_poll_time: 900,
|
347
348
|
needs_dependencies: false,
|
348
349
|
target_microservices: {'REDUCER' => [[]]},
|
350
|
+
reducer_disable: false,
|
351
|
+
reducer_max_cpu_utilization: 30.0,
|
349
352
|
scope:
|
350
353
|
)
|
351
354
|
super("#{scope}__#{PRIMARY_KEY}", name: name, plugin: plugin, updated_at: updated_at,
|
@@ -360,6 +363,7 @@ module OpenC3
|
|
360
363
|
reduced_minute_log_retain_time: reduced_minute_log_retain_time,
|
361
364
|
reduced_hour_log_retain_time: reduced_hour_log_retain_time, reduced_day_log_retain_time: reduced_day_log_retain_time,
|
362
365
|
cleanup_poll_time: cleanup_poll_time, needs_dependencies: needs_dependencies, target_microservices: target_microservices,
|
366
|
+
reducer_disable: reducer_disable, reducer_max_cpu_utilization: reducer_max_cpu_utilization,
|
363
367
|
scope: scope)
|
364
368
|
@folder_name = folder_name
|
365
369
|
@requires = requires
|
@@ -390,6 +394,8 @@ module OpenC3
|
|
390
394
|
@cleanup_poll_time = cleanup_poll_time
|
391
395
|
@needs_dependencies = needs_dependencies
|
392
396
|
@target_microservices = target_microservices
|
397
|
+
@reducer_disable = reducer_disable
|
398
|
+
@reducer_max_cpu_utilization = reducer_max_cpu_utilization
|
393
399
|
@bucket = Bucket.getClient()
|
394
400
|
@children = []
|
395
401
|
end
|
@@ -427,7 +433,9 @@ module OpenC3
|
|
427
433
|
'reduced_day_log_retain_time' => @reduced_day_log_retain_time,
|
428
434
|
'cleanup_poll_time' => @cleanup_poll_time,
|
429
435
|
'needs_dependencies' => @needs_dependencies,
|
430
|
-
'target_microservices' => @target_microservices.as_json(:allow_nan => true)
|
436
|
+
'target_microservices' => @target_microservices.as_json(:allow_nan => true),
|
437
|
+
'reducer_disable' => @reducer_disable,
|
438
|
+
'reducer_max_cpu_utilization' => @reducer_max_cpu_utilization
|
431
439
|
}
|
432
440
|
end
|
433
441
|
|
@@ -509,6 +517,11 @@ module OpenC3
|
|
509
517
|
@reduced_hour_log_retain_time = reduced_log_retain_time.to_i
|
510
518
|
@reduced_day_log_retain_time = reduced_log_retain_time.to_i
|
511
519
|
end
|
520
|
+
when 'REDUCER_DISABLE', 'REDUCER_DISABLED' # Handle typos
|
521
|
+
@reducer_disable = true
|
522
|
+
when 'REDUCER_MAX_CPU_UTILIZATION', 'REDUCED_MAX_CPU_UTILIZATION' # Handle typos
|
523
|
+
parser.verify_num_parameters(1, 1, "#{keyword} <Max cpu utilization to allocate to the reducer microservice - 0.0 to 100.0>")
|
524
|
+
@reducer_max_cpu_utilization = Float(parameters[0])
|
512
525
|
when 'CLEANUP_POLL_TIME'
|
513
526
|
parser.verify_num_parameters(1, 1, "#{keyword} <Cleanup polling period in seconds>")
|
514
527
|
@cleanup_poll_time = parameters[0].to_i
|
@@ -556,7 +569,9 @@ module OpenC3
|
|
556
569
|
data = File.read(filename, mode: "rb")
|
557
570
|
begin
|
558
571
|
OpenC3.set_working_dir(File.dirname(filename)) do
|
559
|
-
|
572
|
+
if ERB_EXTENSIONS.include?(File.extname(filename).downcase) and File.basename(filename)[0] != '_'
|
573
|
+
data = ERB.new(data.force_encoding("UTF-8").comment_erb(), trim_mode: "-").result(binding.set_variables(variables))
|
574
|
+
end
|
560
575
|
end
|
561
576
|
rescue => error
|
562
577
|
# ERB error parsing a screen is just a logger error because life can go on
|
@@ -666,7 +681,7 @@ module OpenC3
|
|
666
681
|
|
667
682
|
begin
|
668
683
|
OpenC3.set_working_dir(File.dirname(path)) do
|
669
|
-
return ERB.new(File.read(path), trim_mode: "-").result(b)
|
684
|
+
return ERB.new(File.read(path).force_encoding("UTF-8").comment_erb(), trim_mode: "-").result(b)
|
670
685
|
end
|
671
686
|
rescue => error
|
672
687
|
raise "ERB error parsing: #{path}: #{error.formatted}"
|
@@ -909,6 +924,9 @@ module OpenC3
|
|
909
924
|
folder_name: @folder_name,
|
910
925
|
cmd: ["ruby", "reducer_microservice.rb", microservice_name],
|
911
926
|
work_dir: '/openc3/lib/openc3/microservices',
|
927
|
+
options: [
|
928
|
+
["MAX_CPU_UTILIZATION", @reducer_max_cpu_utilization],
|
929
|
+
],
|
912
930
|
topics: topics,
|
913
931
|
plugin: @plugin,
|
914
932
|
parent: parent,
|
@@ -1053,8 +1071,10 @@ module OpenC3
|
|
1053
1071
|
end
|
1054
1072
|
|
1055
1073
|
# Reducer Microservice
|
1056
|
-
|
1057
|
-
|
1074
|
+
unless @reducer_disable
|
1075
|
+
deploy_target_microservices('REDUCER', decom_topic_list, "#{@scope}__DECOM__{#{@name}}") do |topics, instance, parent|
|
1076
|
+
deploy_reducer_microservice(gem_path, variables, topics, instance, parent)
|
1077
|
+
end
|
1058
1078
|
end
|
1059
1079
|
end
|
1060
1080
|
|
@@ -155,8 +155,19 @@ module OpenC3
|
|
155
155
|
end
|
156
156
|
|
157
157
|
def create(update: false, force: false)
|
158
|
+
tools = self.class.all(scope: @scope)
|
159
|
+
|
160
|
+
# Make sure a tool with this folder_name doesn't already exist
|
161
|
+
unless update
|
162
|
+
tools.each do |_tool_name, tool|
|
163
|
+
if tool['folder_name'] == @folder_name
|
164
|
+
raise "Tool with folder_name #{@folder_name} already exists at create"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Autoset tool position
|
158
170
|
unless @position
|
159
|
-
tools = self.class.all(scope: @scope)
|
160
171
|
_, tool = tools.max_by { |_tool_name, tool| tool['position'] }
|
161
172
|
if tool
|
162
173
|
@position = tool['position'] + 1
|
@@ -164,6 +175,7 @@ module OpenC3
|
|
164
175
|
@position = 0
|
165
176
|
end
|
166
177
|
end
|
178
|
+
|
167
179
|
super(update: update, force: force)
|
168
180
|
end
|
169
181
|
|
@@ -228,7 +240,7 @@ module OpenC3
|
|
228
240
|
|
229
241
|
# Load tool files
|
230
242
|
data = File.read(filename, mode: "rb")
|
231
|
-
data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable?
|
243
|
+
data = ERB.new(data.comment_erb(), trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable?
|
232
244
|
unless validate_only
|
233
245
|
client = Bucket.getClient()
|
234
246
|
cache_control = BucketUtilities.get_cache_control(filename)
|
@@ -124,7 +124,7 @@ module OpenC3
|
|
124
124
|
# Load widget file
|
125
125
|
data = File.read(filename, mode: "rb")
|
126
126
|
OpenC3.set_working_dir(File.dirname(filename)) do
|
127
|
-
data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable?
|
127
|
+
data = ERB.new(data.comment_erb(), trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable?
|
128
128
|
end
|
129
129
|
unless validate_only
|
130
130
|
cache_control = BucketUtilities.get_cache_control(@filename)
|
@@ -32,13 +32,21 @@ module OpenC3
|
|
32
32
|
attr_accessor :packet_time
|
33
33
|
attr_accessor :stored
|
34
34
|
attr_accessor :json_hash
|
35
|
+
attr_accessor :received_time
|
36
|
+
attr_accessor :extra
|
35
37
|
|
36
|
-
def initialize(cmd_or_tlm, target_name, packet_name,
|
38
|
+
def initialize(cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, json_data, key_map = nil, received_time_nsec_since_epoch: nil, extra: nil)
|
37
39
|
@cmd_or_tlm = cmd_or_tlm.intern
|
38
40
|
@target_name = target_name
|
39
41
|
@packet_name = packet_name
|
40
|
-
@packet_time = ::Time.from_nsec_from_epoch(
|
42
|
+
@packet_time = ::Time.from_nsec_from_epoch(time_nsec_since_epoch)
|
41
43
|
@stored = ConfigParser.handle_true_false(stored)
|
44
|
+
if received_time_nsec_since_epoch
|
45
|
+
@received_time = ::Time.from_nsec_from_epoch(received_time_nsec_since_epoch)
|
46
|
+
else
|
47
|
+
@received_time = @packet_time
|
48
|
+
end
|
49
|
+
@extra = extra
|
42
50
|
@json_hash = json_data
|
43
51
|
@json_hash = JSON.parse(json_data, :allow_nan => true, :create_additions => true) if String === json_data
|
44
52
|
if key_map
|
@@ -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 2023, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -127,6 +127,7 @@ module OpenC3
|
|
127
127
|
@extra = nil
|
128
128
|
@cmd_or_tlm = nil
|
129
129
|
@template = nil
|
130
|
+
@packet_time = nil
|
130
131
|
end
|
131
132
|
|
132
133
|
# Sets the target name this packet is associated with. Unidentified packets
|
@@ -267,10 +268,18 @@ module OpenC3
|
|
267
268
|
if item
|
268
269
|
return read_item(item, :CONVERTED, @buffer)
|
269
270
|
else
|
270
|
-
|
271
|
+
if @packet_time
|
272
|
+
return @packet_time
|
273
|
+
else
|
274
|
+
return @received_time
|
275
|
+
end
|
271
276
|
end
|
272
277
|
end
|
273
278
|
|
279
|
+
def packet_time=(time)
|
280
|
+
@packet_time = time
|
281
|
+
end
|
282
|
+
|
274
283
|
# Calculates a unique hashing sum that changes if the parts of the packet configuration change that could affect
|
275
284
|
# the "shape" of the packet. This value is cached and that packet should not be changed if this method is being used
|
276
285
|
def config_name
|
@@ -294,9 +303,7 @@ module OpenC3
|
|
294
303
|
begin
|
295
304
|
internal_buffer_equals(buffer)
|
296
305
|
rescue RuntimeError
|
297
|
-
|
298
|
-
Logger.instance.error "#{@target_name} #{@packet_name} received with actual packet length of #{buffer.length} but defined length of #{@defined_length}"
|
299
|
-
end
|
306
|
+
Logger.instance.error "#{@target_name} #{@packet_name} received with actual packet length of #{buffer.length} but defined length of #{@defined_length}"
|
300
307
|
end
|
301
308
|
@read_conversion_cache.clear if @read_conversion_cache
|
302
309
|
process()
|
@@ -709,10 +716,10 @@ module OpenC3
|
|
709
716
|
if item.write_conversion
|
710
717
|
value = item.write_conversion.call(value, self, buffer)
|
711
718
|
else
|
712
|
-
raise "Cannot write DERIVED item #{item.name} without a write conversion" if item.data_type == :DERIVED
|
719
|
+
raise "Cannot write DERIVED item #{item.name} without a write conversion" if item.data_type == :DERIVED and @accessor.enforce_derived_write_conversion(item)
|
713
720
|
end
|
714
721
|
begin
|
715
|
-
super(item, value, :RAW, buffer)
|
722
|
+
super(item, value, :RAW, buffer)
|
716
723
|
rescue ArgumentError => err
|
717
724
|
if item.states and String === value and err.message =~ /invalid value for/
|
718
725
|
raise "Unknown state #{value} for #{item.name}"
|
@@ -1051,7 +1058,8 @@ module OpenC3
|
|
1051
1058
|
config['messages_disabled'] = true if @messages_disabled
|
1052
1059
|
config['disabled'] = true if @disabled
|
1053
1060
|
config['hidden'] = true if @hidden
|
1054
|
-
config['accessor'] = @accessor.to_s
|
1061
|
+
config['accessor'] = @accessor.class.to_s
|
1062
|
+
config['accessor_args'] = @accessor.args
|
1055
1063
|
config['template'] = Base64.encode64(@template) if @template
|
1056
1064
|
|
1057
1065
|
if @processors
|
@@ -1092,7 +1100,12 @@ module OpenC3
|
|
1092
1100
|
packet.hidden = hash['hidden']
|
1093
1101
|
if hash['accessor']
|
1094
1102
|
begin
|
1095
|
-
|
1103
|
+
accessor = OpenC3::const_get(hash['accessor'])
|
1104
|
+
if hash['accessor_args'] and hash['accessor_args'].length > 0
|
1105
|
+
packet.accessor = accessor.new(packet, *hash['accessor_args'])
|
1106
|
+
else
|
1107
|
+
packet.accessor = accessor.new(packet)
|
1108
|
+
end
|
1096
1109
|
rescue => error
|
1097
1110
|
Logger.instance.error "#{packet.target_name} #{packet.packet_name} accessor of #{hash['accessor']} could not be found due to #{error}"
|
1098
1111
|
end
|
@@ -1110,6 +1123,13 @@ module OpenC3
|
|
1110
1123
|
# Read all the RAW at once because this could be optimized by the accessor
|
1111
1124
|
json_hash = read_items(@sorted_items)
|
1112
1125
|
|
1126
|
+
# Decom extra into the values (all raw)
|
1127
|
+
if @extra
|
1128
|
+
@extra.each do |key, value|
|
1129
|
+
json_hash[key.upcase] = value
|
1130
|
+
end
|
1131
|
+
end
|
1132
|
+
|
1113
1133
|
# Now read all other value types - no accessor required
|
1114
1134
|
@sorted_items.each do |item|
|
1115
1135
|
given_raw = json_hash[item.name]
|
@@ -1119,6 +1139,7 @@ module OpenC3
|
|
1119
1139
|
limits_state = item.limits.state
|
1120
1140
|
json_hash["#{item.name}__L"] = limits_state if limits_state
|
1121
1141
|
end
|
1142
|
+
|
1122
1143
|
json_hash
|
1123
1144
|
end
|
1124
1145
|
|
@@ -433,10 +433,14 @@ module OpenC3
|
|
433
433
|
@current_packet.disabled = true
|
434
434
|
when 'ACCESSOR'
|
435
435
|
usage = "#{keyword} <Accessor class name>"
|
436
|
-
parser.verify_num_parameters(1,
|
436
|
+
parser.verify_num_parameters(1, nil, usage)
|
437
437
|
begin
|
438
438
|
klass = OpenC3.require_class(params[0])
|
439
|
-
|
439
|
+
if params.length > 1
|
440
|
+
@current_packet.accessor = klass.new(@current_packet, *params[1..-1])
|
441
|
+
else
|
442
|
+
@current_packet.accessor = klass.new(@current_packet)
|
443
|
+
end
|
440
444
|
rescue Exception => err
|
441
445
|
raise parser.error(err)
|
442
446
|
end
|
@@ -14,10 +14,10 @@
|
|
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 2023, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
|
-
# This file may also be used under the terms of a commercial license
|
20
|
+
# This file may also be used under the terms of a commercial license
|
21
21
|
# if purchased from OpenC3, Inc.
|
22
22
|
|
23
23
|
require 'openc3/packets/packet_item'
|
@@ -158,6 +158,7 @@ module OpenC3
|
|
158
158
|
return nil if data_type == :STRING or data_type == :BLOCK
|
159
159
|
|
160
160
|
index = append? ? 3 : 4
|
161
|
+
return nil if @parser.parameters[index] == 'nil'
|
161
162
|
min = ConfigParser.handle_defined_constants(
|
162
163
|
@parser.parameters[index].convert_to_value, get_data_type(), get_bit_size()
|
163
164
|
)
|
@@ -183,9 +184,13 @@ module OpenC3
|
|
183
184
|
return @parser.parameters[index]
|
184
185
|
end
|
185
186
|
else
|
186
|
-
|
187
|
-
|
188
|
-
|
187
|
+
if data_type != :DERIVED
|
188
|
+
return ConfigParser.handle_defined_constants(
|
189
|
+
@parser.parameters[index + 2].convert_to_value, data_type, get_bit_size()
|
190
|
+
)
|
191
|
+
else
|
192
|
+
return @parser.parameters[index + 2].convert_to_value
|
193
|
+
end
|
189
194
|
end
|
190
195
|
end
|
191
196
|
|
@@ -248,7 +253,7 @@ module OpenC3
|
|
248
253
|
data_type = get_data_type()
|
249
254
|
rescue
|
250
255
|
# If the data type could not be determined set something
|
251
|
-
data_type
|
256
|
+
data_type = :INT
|
252
257
|
end
|
253
258
|
# STRING and BLOCK types do not have min or max values
|
254
259
|
if data_type == :STRING || data_type == :BLOCK
|
@@ -54,7 +54,7 @@ module OpenC3
|
|
54
54
|
# required data size is allowed.
|
55
55
|
attr_accessor :short_buffer_allowed
|
56
56
|
|
57
|
-
# @return [Accessor]
|
57
|
+
# @return [Accessor] Instance of class used to access raw data of structure from buffer
|
58
58
|
attr_reader :accessor
|
59
59
|
|
60
60
|
if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
|
@@ -92,7 +92,7 @@ module OpenC3
|
|
92
92
|
@fixed_size = true
|
93
93
|
@short_buffer_allowed = false
|
94
94
|
@mutex = nil
|
95
|
-
@accessor = BinaryAccessor
|
95
|
+
@accessor = BinaryAccessor.new(self)
|
96
96
|
else
|
97
97
|
raise(ArgumentError, "Unknown endianness '#{default_endianness}', must be :BIG_ENDIAN or :LITTLE_ENDIAN")
|
98
98
|
end
|
@@ -107,8 +107,6 @@ module OpenC3
|
|
107
107
|
# @return Value based on the item definition. This could be a string, integer,
|
108
108
|
# float, or array of values.
|
109
109
|
def read_item(item, value_type = :RAW, buffer = @buffer)
|
110
|
-
return nil if item.data_type == :DERIVED
|
111
|
-
|
112
110
|
buffer = allocate_buffer_if_needed() unless buffer
|
113
111
|
return @accessor.read_item(item, buffer)
|
114
112
|
end
|
@@ -141,7 +139,10 @@ module OpenC3
|
|
141
139
|
# @param accessor [Accessor] The class to use as an accessor
|
142
140
|
def accessor=(accessor)
|
143
141
|
@accessor = accessor
|
144
|
-
|
142
|
+
if @accessor.enforce_short_buffer_allowed
|
143
|
+
@short_buffer_allowed = true
|
144
|
+
end
|
145
|
+
return accessor
|
145
146
|
end
|
146
147
|
|
147
148
|
# Read a list of items in the structure
|
@@ -493,6 +494,8 @@ module OpenC3
|
|
493
494
|
# Use instance_variable_set since we have overriden buffer= to do
|
494
495
|
# additional work that isn't neccessary here
|
495
496
|
structure.instance_variable_set("@buffer".freeze, @buffer.clone) if @buffer
|
497
|
+
# Need to update reference packet in the Accessor
|
498
|
+
structure.accessor.packet = structure
|
496
499
|
return structure
|
497
500
|
end
|
498
501
|
alias dup clone
|
@@ -567,13 +570,17 @@ module OpenC3
|
|
567
570
|
raise ArgumentError, "Buffer class is #{buffer.class} but must be String" unless String === buffer
|
568
571
|
|
569
572
|
@buffer = buffer.dup
|
570
|
-
@
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
573
|
+
if @accessor.enforce_encoding
|
574
|
+
@buffer.force_encoding(@accessor.enforce_encoding)
|
575
|
+
end
|
576
|
+
if @accessor.enforce_length
|
577
|
+
if @buffer.length != @defined_length
|
578
|
+
if @buffer.length < @defined_length
|
579
|
+
resize_buffer()
|
580
|
+
raise "Buffer length less than defined length" unless @short_buffer_allowed
|
581
|
+
elsif @fixed_size and @defined_length != 0
|
582
|
+
raise "Buffer length greater than defined length"
|
583
|
+
end
|
577
584
|
end
|
578
585
|
end
|
579
586
|
end
|
@@ -104,7 +104,7 @@ module OpenC3
|
|
104
104
|
if part == "targets_modified" and ENV['OPENC3_LOCAL_MODE']
|
105
105
|
local_file = OpenC3::LocalMode.open_local_file(path, scope: scope)
|
106
106
|
if local_file
|
107
|
-
OpenC3::Logger.info "Reading local #{scope}/#{path}"
|
107
|
+
OpenC3::Logger.info "Reading local #{scope}/#{part}/#{path}"
|
108
108
|
file = Tempfile.new('target', binmode: true)
|
109
109
|
file.filename = path
|
110
110
|
file.write(local_file.read)
|