openc3 5.15.1 → 5.16.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of openc3 might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Rakefile +1 -0
- data/bin/openc3cli +20 -0
- data/bin/pipinstall +3 -0
- data/data/config/interface_modifiers.yaml +4 -1
- data/data/config/telemetry_modifiers.yaml +6 -1
- data/data/config/widgets.yaml +1 -1
- data/ext/openc3/ext/burst_protocol/burst_protocol.c +317 -0
- data/ext/openc3/ext/burst_protocol/extconf.rb +13 -0
- data/lib/openc3/accessors/accessor.rb +1 -1
- data/lib/openc3/accessors/json_accessor.rb +11 -3
- data/lib/openc3/api/tlm_api.rb +1 -1
- data/lib/openc3/interfaces/http_client_interface.rb +8 -4
- data/lib/openc3/interfaces/http_server_interface.rb +22 -6
- data/lib/openc3/interfaces/interface.rb +6 -0
- data/lib/openc3/interfaces/linc_interface.rb +5 -3
- data/lib/openc3/interfaces/mqtt_interface.rb +7 -3
- data/lib/openc3/interfaces/mqtt_stream_interface.rb +8 -1
- data/lib/openc3/interfaces/protocols/burst_protocol.rb +104 -100
- data/lib/openc3/interfaces/protocols/fixed_protocol.rb +11 -3
- data/lib/openc3/interfaces/serial_interface.rb +16 -1
- data/lib/openc3/interfaces/simulated_target_interface.rb +7 -3
- data/lib/openc3/interfaces/tcpip_client_interface.rb +18 -1
- data/lib/openc3/interfaces/tcpip_server_interface.rb +24 -15
- data/lib/openc3/interfaces/udp_interface.rb +11 -1
- data/lib/openc3/io/posix_serial_driver.rb +20 -5
- data/lib/openc3/logs/packet_log_writer.rb +1 -1
- data/lib/openc3/microservices/decom_microservice.rb +3 -2
- data/lib/openc3/microservices/interface_microservice.rb +5 -5
- data/lib/openc3/models/activity_model.rb +104 -40
- data/lib/openc3/models/gem_model.rb +1 -1
- data/lib/openc3/models/plugin_model.rb +5 -3
- data/lib/openc3/models/python_package_model.rb +15 -5
- data/lib/openc3/models/scope_model.rb +1 -1
- data/lib/openc3/models/target_model.rb +1 -1
- data/lib/openc3/packets/packet.rb +27 -24
- data/lib/openc3/packets/packet_config.rb +18 -1
- data/lib/openc3/packets/parsers/packet_item_parser.rb +10 -6
- data/lib/openc3/packets/structure.rb +7 -7
- data/lib/openc3/packets/structure_item.rb +4 -2
- data/lib/openc3/script/api_shared.rb +33 -29
- data/lib/openc3/script/plugins.rb +13 -13
- data/lib/openc3/script/storage.rb +3 -4
- data/lib/openc3/script/web_socket_api.rb +10 -0
- data/lib/openc3/version.rb +6 -6
- data/templates/target/targets/TARGET/lib/target.py +2 -0
- data/templates/tool_angular/package.json +8 -8
- data/templates/tool_react/package.json +2 -2
- data/templates/tool_svelte/build/smui.css +1 -5
- data/templates/tool_svelte/package.json +3 -3
- data/templates/tool_vue/package.json +12 -12
- data/templates/widget/package.json +11 -11
- metadata +21 -18
@@ -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 2024, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -24,6 +24,7 @@
|
|
24
24
|
|
25
25
|
require 'openc3/models/model'
|
26
26
|
require 'openc3/topics/timeline_topic'
|
27
|
+
require 'securerandom'
|
27
28
|
|
28
29
|
module OpenC3
|
29
30
|
class ActivityError < StandardError; end
|
@@ -128,7 +129,7 @@ module OpenC3
|
|
128
129
|
self.new(**json.transform_keys(&:to_sym), name: name, scope: scope)
|
129
130
|
end
|
130
131
|
|
131
|
-
attr_reader :
|
132
|
+
attr_reader :start, :stop, :kind, :data, :events, :fulfillment, :recurring
|
132
133
|
|
133
134
|
def initialize(
|
134
135
|
name:,
|
@@ -138,9 +139,9 @@ module OpenC3
|
|
138
139
|
data:,
|
139
140
|
scope:,
|
140
141
|
updated_at: 0,
|
141
|
-
duration: 0,
|
142
142
|
fulfillment: nil,
|
143
|
-
events: nil
|
143
|
+
events: nil,
|
144
|
+
recurring: {}
|
144
145
|
)
|
145
146
|
super("#{scope}#{PRIMARY_KEY}__#{name}", name: name, scope: scope)
|
146
147
|
set_input(
|
@@ -150,22 +151,24 @@ module OpenC3
|
|
150
151
|
kind: kind,
|
151
152
|
data: data,
|
152
153
|
events: events,
|
154
|
+
recurring: recurring,
|
153
155
|
)
|
154
156
|
@updated_at = updated_at
|
155
157
|
end
|
156
158
|
|
157
|
-
# validate_time
|
158
|
-
#
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
#
|
163
|
-
#
|
159
|
+
# validate_time searches from the current activity @stop - 1 (because we allow overlap of stop with start)
|
160
|
+
# back through @start - MAX_DURATION. The method is trying to validate that this new activity does not
|
161
|
+
# overlap with anything else. The reason we search back past @start through MAX_DURATION is because we
|
162
|
+
# need to return all the activities that may start before us and verify that we don't overlap them.
|
163
|
+
# Activities are only inserted by @start time so we need to go back to verify we don't overlap existing @stop.
|
164
|
+
# Note: Score is the Seconds since the Unix Epoch: (%s) Number of seconds since 1970-01-01 00:00:00 UTC.
|
165
|
+
# zrange rev byscore finds activites from in reverse order so the first task is the closest task to the current score.
|
166
|
+
# In this case a parameter ignore_score allows the request to ignore that time and skip to the next time
|
167
|
+
# but if nothing is found in the time range we can return nil.
|
164
168
|
#
|
165
169
|
# @param [Integer] ignore_score - should be nil unless you want to ignore a time when doing an update
|
166
170
|
def validate_time(ignore_score = nil)
|
167
|
-
|
168
|
-
array = Store.zrevrangebyscore(@primary_key, @stop, max_score)
|
171
|
+
array = Store.zrevrangebyscore(@primary_key, @stop - 1, @start - MAX_DURATION)
|
169
172
|
array.each do |value|
|
170
173
|
activity = JSON.parse(value, :allow_nan => true, :create_additions => true)
|
171
174
|
if ignore_score == activity['start']
|
@@ -190,10 +193,14 @@ module OpenC3
|
|
190
193
|
DateTime.strptime(start.to_s, '%s')
|
191
194
|
DateTime.strptime(stop.to_s, '%s')
|
192
195
|
rescue Date::Error
|
193
|
-
raise ActivityInputError.new "
|
196
|
+
raise ActivityInputError.new "start and stop must be seconds: #{start}, #{stop}"
|
197
|
+
end
|
198
|
+
now_i = Time.now.to_i
|
199
|
+
begin
|
200
|
+
duration = stop - start
|
201
|
+
rescue NoMethodError
|
202
|
+
raise ActivityInputError.new "start and stop must be seconds: #{start}, #{stop}"
|
194
203
|
end
|
195
|
-
now_i = Time.now.to_i + 10
|
196
|
-
duration = stop - start
|
197
204
|
if now_i >= start
|
198
205
|
raise ActivityInputError.new "activity must be in the future, current_time: #{now_i} vs #{start}"
|
199
206
|
elsif duration >= MAX_DURATION
|
@@ -210,47 +217,92 @@ module OpenC3
|
|
210
217
|
end
|
211
218
|
|
212
219
|
# Set the values of the instance, @start, @kind, @data, @events...
|
213
|
-
def set_input(start:, stop:, kind: nil, data: nil, events: nil, fulfillment: nil)
|
214
|
-
|
215
|
-
DateTime.strptime(start.to_s, '%s')
|
216
|
-
DateTime.strptime(stop.to_s, '%s')
|
217
|
-
rescue ArgumentError
|
218
|
-
raise ActivityInputError.new "invalid input must be seconds: #{start}, #{stop}"
|
219
|
-
end
|
220
|
+
def set_input(start:, stop:, kind: nil, data: nil, events: nil, fulfillment: nil, recurring: nil)
|
221
|
+
validate_input(start: start, stop: stop, kind: kind, data: data)
|
220
222
|
@start = start
|
221
223
|
@stop = stop
|
222
|
-
@duration = @stop - @start
|
223
224
|
@fulfillment = fulfillment.nil? ? false : fulfillment
|
224
225
|
@kind = kind.nil? ? @kind : kind
|
225
226
|
@data = data.nil? ? @data : data
|
226
227
|
@events = events.nil? ? Array.new : events
|
228
|
+
@recurring = recurring.nil? ? @recurring : recurring
|
227
229
|
end
|
228
230
|
|
229
231
|
# Update the Redis hash at primary_key and set the score equal to the start Epoch time
|
230
232
|
# the member is set to the JSON generated via calling as_json
|
231
233
|
def create
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
234
|
+
if @recurring['end'] and @recurring['frequency'] and @recurring['span']
|
235
|
+
# First validate the initial recurring activity ... all others are just offsets
|
236
|
+
validate_input(start: @start, stop: @stop, kind: @kind, data: @data)
|
237
|
+
|
238
|
+
# Create a uuid for deleting related recurring in the future
|
239
|
+
@recurring['uuid'] = SecureRandom.uuid
|
240
|
+
@recurring['start'] = @start
|
241
|
+
duration = @stop - @start
|
242
|
+
recurrance = 0
|
243
|
+
case @recurring['span']
|
244
|
+
when 'minutes'
|
245
|
+
recurrance = @recurring['frequency'].to_i * 60
|
246
|
+
when 'hours'
|
247
|
+
recurrance = @recurring['frequency'].to_i * 3600
|
248
|
+
when 'days'
|
249
|
+
recurrance = @recurring['frequency'].to_i * 86400
|
250
|
+
end
|
237
251
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
252
|
+
# Get all the existing events in the recurring time range as well as those before
|
253
|
+
# the start of the recurring time range to ensure we don't start inside an existing event
|
254
|
+
existing = Store.zrevrangebyscore(@primary_key, @recurring['end'] - 1, @recurring['start'] - MAX_DURATION)
|
255
|
+
existing.map! {|value| JSON.parse(value, :allow_nan => true, :create_additions => true) }
|
256
|
+
last_stop = nil
|
257
|
+
|
258
|
+
# Update @updated_at and add an event assuming it all completes ok
|
259
|
+
@updated_at = Time.now.to_nsec_from_epoch
|
260
|
+
add_event(status: 'created')
|
261
|
+
|
262
|
+
Store.multi do |multi|
|
263
|
+
(@start..@recurring['end']).step(recurrance).each do |start_time|
|
264
|
+
@start = start_time
|
265
|
+
@stop = start_time + duration
|
266
|
+
|
267
|
+
if last_stop and @start < last_stop
|
268
|
+
@events.pop # Remove previously created event
|
269
|
+
raise ActivityOverlapError.new "Recurring activity overlap. Increase recurrance delta or decrease activity duration."
|
270
|
+
end
|
271
|
+
existing.each do |value|
|
272
|
+
if (@start >= value['start'] and @start < value['stop']) ||
|
273
|
+
(@stop > value['start'] and @stop <= value['stop'])
|
274
|
+
@events.pop # Remove previously created event
|
275
|
+
raise ActivityOverlapError.new "activity overlaps existing at #{value['start']}"
|
276
|
+
end
|
277
|
+
end
|
278
|
+
multi.zadd(@primary_key, @start, JSON.generate(self.as_json(:allow_nan => true)))
|
279
|
+
last_stop = @stop
|
280
|
+
end
|
281
|
+
end
|
282
|
+
notify(kind: 'created')
|
283
|
+
else
|
284
|
+
validate_input(start: @start, stop: @stop, kind: @kind, data: @data)
|
285
|
+
collision = validate_time()
|
286
|
+
unless collision.nil?
|
287
|
+
raise ActivityOverlapError.new "activity overlaps existing at #{collision}"
|
288
|
+
end
|
289
|
+
|
290
|
+
@updated_at = Time.now.to_nsec_from_epoch
|
291
|
+
add_event(status: 'created')
|
292
|
+
Store.zadd(@primary_key, @start, JSON.generate(self.as_json(:allow_nan => true)))
|
293
|
+
notify(kind: 'created')
|
294
|
+
end
|
242
295
|
end
|
243
296
|
|
244
297
|
# Update the Redis hash at primary_key and remove the current activity at the current score
|
245
298
|
# and update the score to the new score equal to the start Epoch time this uses a multi
|
246
|
-
# to execute both the remove and create.
|
299
|
+
# to execute both the remove and create.
|
247
300
|
def update(start:, stop:, kind:, data:)
|
248
301
|
array = Store.zrangebyscore(@primary_key, @start, @start)
|
249
302
|
if array.length == 0
|
250
303
|
raise ActivityError.new "failed to find activity at: #{@start}"
|
251
304
|
end
|
252
305
|
|
253
|
-
validate_input(start: start, stop: stop, kind: kind, data: data)
|
254
306
|
old_start = @start
|
255
307
|
set_input(start: start, stop: stop, kind: kind, data: data, events: @events)
|
256
308
|
@updated_at = Time.now.to_nsec_from_epoch
|
@@ -299,12 +351,24 @@ module OpenC3
|
|
299
351
|
end
|
300
352
|
|
301
353
|
# destroy the activity from the redis database
|
302
|
-
def destroy
|
303
|
-
|
354
|
+
def destroy(recurring: false)
|
355
|
+
# Delete all recurring activities
|
356
|
+
if recurring and @recurring['end'] and @recurring['uuid']
|
357
|
+
uuid = @recurring['uuid']
|
358
|
+
array = Store.zrangebyscore("#{scope}#{PRIMARY_KEY}__#{@name}", @recurring['start'], @recurring['end'])
|
359
|
+
array.each do |value|
|
360
|
+
model = ActivityModel.from_json(value, name: @name, scope: @scope)
|
361
|
+
if model.recurring['uuid'] == uuid
|
362
|
+
Store.zremrangebyscore(@primary_key, model.start, model.start)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
else
|
366
|
+
Store.zremrangebyscore(@primary_key, @start, @start)
|
367
|
+
end
|
304
368
|
notify(kind: 'deleted')
|
305
369
|
end
|
306
370
|
|
307
|
-
#
|
371
|
+
# update the redis stream / timeline topic that something has changed
|
308
372
|
def notify(kind:, extra: nil)
|
309
373
|
notification = {
|
310
374
|
'data' => JSON.generate(as_json(:allow_nan => true)),
|
@@ -326,12 +390,12 @@ module OpenC3
|
|
326
390
|
'name' => @name,
|
327
391
|
'updated_at' => @updated_at,
|
328
392
|
'fulfillment' => @fulfillment,
|
329
|
-
'duration' => @duration,
|
330
393
|
'start' => @start,
|
331
394
|
'stop' => @stop,
|
332
395
|
'kind' => @kind,
|
333
396
|
'events' => @events,
|
334
|
-
'data' => @data.as_json(*a)
|
397
|
+
'data' => @data.as_json(*a),
|
398
|
+
'recurring' => @recurring.as_json(*a)
|
335
399
|
}
|
336
400
|
end
|
337
401
|
end
|
@@ -44,7 +44,7 @@ module OpenC3
|
|
44
44
|
# microservices and tools. The PluginModel installs all these pieces as well
|
45
45
|
# as destroys them all when the plugin is removed.
|
46
46
|
class PluginModel < Model
|
47
|
-
|
47
|
+
extend Api
|
48
48
|
|
49
49
|
PRIMARY_KEY = 'openc3_plugins'
|
50
50
|
# Reserved VARIABLE names. See local_mode.rb: update_local_plugin()
|
@@ -198,8 +198,10 @@ module OpenC3
|
|
198
198
|
pypi_url ||= 'https://pypi.org/simple'
|
199
199
|
end
|
200
200
|
end
|
201
|
-
|
202
|
-
|
201
|
+
unless validate_only
|
202
|
+
Logger.info "Installing python packages from requirements.txt with pypi_url=#{pypi_url}"
|
203
|
+
puts `/openc3/bin/pipinstall --user --no-warn-script-location -i #{pypi_url} -r #{File.join(gem_path, 'requirements.txt')}`
|
204
|
+
end
|
203
205
|
needs_dependencies = true
|
204
206
|
end
|
205
207
|
|
@@ -18,6 +18,7 @@
|
|
18
18
|
|
19
19
|
require 'fileutils'
|
20
20
|
require 'openc3/utilities/process_manager'
|
21
|
+
require 'openc3/api/api'
|
21
22
|
require 'pathname'
|
22
23
|
|
23
24
|
module OpenC3
|
@@ -26,6 +27,8 @@ module OpenC3
|
|
26
27
|
# and destroy to allow interaction with python package files from the PluginModel and
|
27
28
|
# the PackagesController.
|
28
29
|
class PythonPackageModel
|
30
|
+
extend Api
|
31
|
+
|
29
32
|
def self.names
|
30
33
|
paths = Dir.glob("#{ENV['PYTHONUSERBASE']}/lib/*")
|
31
34
|
results = []
|
@@ -71,16 +74,23 @@ module OpenC3
|
|
71
74
|
package_filename = File.basename(package_file_path)
|
72
75
|
begin
|
73
76
|
pypi_url = get_setting('pypi_url', scope: scope)
|
74
|
-
rescue
|
75
|
-
# If Redis isn't running try the ENV, then simply pypi.org/simple
|
76
|
-
pypi_url = ENV['PYPI_URL']
|
77
77
|
if pypi_url
|
78
78
|
pypi_url += '/simple'
|
79
79
|
end
|
80
|
-
|
80
|
+
rescue => e
|
81
|
+
Logger.error("Failed to retrieve pypi_url: #{e.formatted}")
|
82
|
+
ensure
|
83
|
+
if pypi_url.nil?
|
84
|
+
# If Redis isn't running try the ENV, then simply pypi.org/simple
|
85
|
+
pypi_url = ENV['PYPI_URL']
|
86
|
+
if pypi_url
|
87
|
+
pypi_url += '/simple'
|
88
|
+
end
|
89
|
+
pypi_url ||= 'https://pypi.org/simple'
|
90
|
+
end
|
81
91
|
end
|
82
92
|
Logger.info "Installing python package: #{name_or_path}"
|
83
|
-
result = OpenC3::ProcessManager.instance.spawn(["
|
93
|
+
result = OpenC3::ProcessManager.instance.spawn(["/openc3/bin/pipinstall", "--user", "--no-warn-script-location", "-i", pypi_url, package_file_path], "package_install", package_filename, Time.now + 3600.0, scope: scope)
|
84
94
|
return result.name
|
85
95
|
end
|
86
96
|
|
@@ -306,7 +306,7 @@ module OpenC3
|
|
306
306
|
setting = SettingModel.get(name: 'rubygems_url')
|
307
307
|
SettingModel.set({ name: 'rubygems_url', data: 'https://rubygems.org' }, scope: @scope) unless setting
|
308
308
|
setting = SettingModel.get(name: 'pypi_url')
|
309
|
-
SettingModel.set({ name: 'pypi_url', data: 'https://pypi.org
|
309
|
+
SettingModel.set({ name: 'pypi_url', data: 'https://pypi.org' }, scope: @scope) unless setting
|
310
310
|
end
|
311
311
|
end
|
312
312
|
end
|
@@ -405,7 +405,7 @@ module OpenC3
|
|
405
405
|
'ignored_items' => @ignored_items,
|
406
406
|
'limits_groups' => @limits_groups,
|
407
407
|
'cmd_tlm_files' => @cmd_tlm_files,
|
408
|
-
'cmd_unique_id_mode' => cmd_unique_id_mode,
|
408
|
+
'cmd_unique_id_mode' => @cmd_unique_id_mode,
|
409
409
|
'tlm_unique_id_mode' => @tlm_unique_id_mode,
|
410
410
|
'id' => @id,
|
411
411
|
'updated_at' => @updated_at,
|
@@ -101,6 +101,9 @@ module OpenC3
|
|
101
101
|
# @return [Array<Array<Target Name, Packet Name, Item Name>>] Related items
|
102
102
|
attr_accessor :related_items
|
103
103
|
|
104
|
+
# @return [Boolean] Whether to ignore overlapping items
|
105
|
+
attr_accessor :ignore_overlap
|
106
|
+
|
104
107
|
# Valid format types
|
105
108
|
VALUE_TYPES = [:RAW, :CONVERTED, :FORMATTED, :WITH_UNITS]
|
106
109
|
|
@@ -140,6 +143,7 @@ module OpenC3
|
|
140
143
|
@cmd_or_tlm = nil
|
141
144
|
@template = nil
|
142
145
|
@packet_time = nil
|
146
|
+
@ignore_overlap = false
|
143
147
|
end
|
144
148
|
|
145
149
|
# Sets the target name this packet is associated with. Unidentified packets
|
@@ -151,12 +155,10 @@ module OpenC3
|
|
151
155
|
if !(String === target_name)
|
152
156
|
raise(ArgumentError, "target_name must be a String but is a #{target_name.class}")
|
153
157
|
end
|
154
|
-
|
155
158
|
@target_name = target_name.upcase.freeze
|
156
159
|
else
|
157
160
|
@target_name = nil
|
158
161
|
end
|
159
|
-
@target_name
|
160
162
|
end
|
161
163
|
|
162
164
|
# Sets the packet name. Unidentified packets will have packet name set to
|
@@ -168,12 +170,10 @@ module OpenC3
|
|
168
170
|
if !(String === packet_name)
|
169
171
|
raise(ArgumentError, "packet_name must be a String but is a #{packet_name.class}")
|
170
172
|
end
|
171
|
-
|
172
173
|
@packet_name = packet_name.upcase.freeze
|
173
174
|
else
|
174
175
|
@packet_name = nil
|
175
176
|
end
|
176
|
-
@packet_name
|
177
177
|
end
|
178
178
|
|
179
179
|
# Sets the description of the packet
|
@@ -184,12 +184,10 @@ module OpenC3
|
|
184
184
|
if !(String === description)
|
185
185
|
raise(ArgumentError, "description must be a String but is a #{description.class}")
|
186
186
|
end
|
187
|
-
|
188
187
|
@description = description.to_utf8.freeze
|
189
188
|
else
|
190
189
|
@description = nil
|
191
190
|
end
|
192
|
-
@description
|
193
191
|
end
|
194
192
|
|
195
193
|
# Sets the received time of the packet
|
@@ -200,13 +198,11 @@ module OpenC3
|
|
200
198
|
if !(Time === received_time)
|
201
199
|
raise(ArgumentError, "received_time must be a Time but is a #{received_time.class}")
|
202
200
|
end
|
203
|
-
|
204
201
|
@received_time = received_time.clone.freeze
|
205
202
|
else
|
206
203
|
@received_time = nil
|
207
204
|
end
|
208
205
|
@read_conversion_cache.clear if @read_conversion_cache
|
209
|
-
@received_time
|
210
206
|
end
|
211
207
|
|
212
208
|
# Sets the received count of the packet
|
@@ -217,10 +213,8 @@ module OpenC3
|
|
217
213
|
if !(Integer === received_count)
|
218
214
|
raise(ArgumentError, "received_count must be an Integer but is a #{received_count.class}")
|
219
215
|
end
|
220
|
-
|
221
216
|
@received_count = received_count
|
222
217
|
@read_conversion_cache.clear if @read_conversion_cache
|
223
|
-
@received_count
|
224
218
|
end
|
225
219
|
|
226
220
|
end # if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
|
@@ -401,6 +395,10 @@ module OpenC3
|
|
401
395
|
#
|
402
396
|
# @return [Array<String>] Warning messages for big definition overlaps
|
403
397
|
def check_bit_offsets
|
398
|
+
if @ignore_overlap
|
399
|
+
Logger.instance.info("#{@target_name} #{@packet_name} has IGNORE_OVERLAP so bit overlaps ignored")
|
400
|
+
return []
|
401
|
+
end
|
404
402
|
expected_next_offset = nil
|
405
403
|
previous_item = nil
|
406
404
|
warnings = []
|
@@ -617,7 +615,7 @@ module OpenC3
|
|
617
615
|
|
618
616
|
unless using_cached_value
|
619
617
|
if item.array_size
|
620
|
-
value.map! do |val,
|
618
|
+
value.map! do |val, _index|
|
621
619
|
item.read_conversion.call(val, self, buffer)
|
622
620
|
end
|
623
621
|
else
|
@@ -646,7 +644,7 @@ module OpenC3
|
|
646
644
|
# Convert from value to state if possible
|
647
645
|
if item.states
|
648
646
|
if Array === value
|
649
|
-
value = value.map do |val,
|
647
|
+
value = value.map do |val, _index|
|
650
648
|
if item.states.key(val)
|
651
649
|
item.states.key(val)
|
652
650
|
elsif item.states.values.include?(ANY_STATE)
|
@@ -667,7 +665,7 @@ module OpenC3
|
|
667
665
|
end
|
668
666
|
else
|
669
667
|
if Array === value
|
670
|
-
value = value.map do |val,
|
668
|
+
value = value.map do |val, _index|
|
671
669
|
apply_format_string_and_units(item, val, value_type)
|
672
670
|
end
|
673
671
|
else
|
@@ -693,7 +691,7 @@ module OpenC3
|
|
693
691
|
# @param value_type [Symbol] Value type to read for every item
|
694
692
|
# @param buffer [String] The binary buffer to read the items from
|
695
693
|
# @return Hash of read names and values
|
696
|
-
def read_items(items, value_type = :RAW, buffer = @buffer,
|
694
|
+
def read_items(items, value_type = :RAW, buffer = @buffer, _raw_value = nil)
|
697
695
|
buffer = allocate_buffer_if_needed() unless buffer
|
698
696
|
if value_type == :RAW
|
699
697
|
result = super(items, value_type, buffer)
|
@@ -735,11 +733,11 @@ module OpenC3
|
|
735
733
|
end
|
736
734
|
begin
|
737
735
|
super(item, value, :RAW, buffer)
|
738
|
-
rescue ArgumentError =>
|
739
|
-
if item.states and String === value and
|
736
|
+
rescue ArgumentError => e
|
737
|
+
if item.states and String === value and e.message =~ /invalid value for/
|
740
738
|
raise "Unknown state #{value} for #{item.name}"
|
741
739
|
else
|
742
|
-
raise
|
740
|
+
raise e
|
743
741
|
end
|
744
742
|
end
|
745
743
|
when :FORMATTED, :WITH_UNITS
|
@@ -998,7 +996,7 @@ module OpenC3
|
|
998
996
|
end
|
999
997
|
return unless @processors
|
1000
998
|
|
1001
|
-
@processors.each do |
|
999
|
+
@processors.each do |_processor_name, processor|
|
1002
1000
|
processor.reset
|
1003
1001
|
end
|
1004
1002
|
end
|
@@ -1051,7 +1049,7 @@ module OpenC3
|
|
1051
1049
|
config << "COMMAND #{@target_name.to_s.quote_if_necessary} #{@packet_name.to_s.quote_if_necessary} #{@default_endianness} \"#{@description}\"\n"
|
1052
1050
|
end
|
1053
1051
|
if @accessor.class.to_s != 'OpenC3::BinaryAccessor'
|
1054
|
-
config << " ACCESSOR #{@accessor.class
|
1052
|
+
config << " ACCESSOR #{@accessor.class} #{@accessor.args.map { |a| a.to_s.quote_if_necessary }.join(" ")}\n"
|
1055
1053
|
end
|
1056
1054
|
# TODO: Add TEMPLATE_ENCODED so this can always be done inline regardless of content
|
1057
1055
|
if @template
|
@@ -1067,7 +1065,7 @@ module OpenC3
|
|
1067
1065
|
end
|
1068
1066
|
|
1069
1067
|
if @processors
|
1070
|
-
@processors.each do |
|
1068
|
+
@processors.each do |_processor_name, processor|
|
1071
1069
|
config << processor.to_config
|
1072
1070
|
end
|
1073
1071
|
end
|
@@ -1106,6 +1104,9 @@ module OpenC3
|
|
1106
1104
|
config << " RELATED_ITEM #{target_name.to_s.quote_if_necessary} #{packet_name.to_s.quote_if_necessary} #{item_name.to_s.quote_if_necessary}"
|
1107
1105
|
end
|
1108
1106
|
end
|
1107
|
+
if @ignore_overlap
|
1108
|
+
config << " IGNORE_OVERLAP"
|
1109
|
+
end
|
1109
1110
|
config
|
1110
1111
|
end
|
1111
1112
|
|
@@ -1128,7 +1129,7 @@ module OpenC3
|
|
1128
1129
|
|
1129
1130
|
if @processors
|
1130
1131
|
processors = []
|
1131
|
-
@processors.each do |
|
1132
|
+
@processors.each do |_processor_name, processor|
|
1132
1133
|
processors << processor.as_json(*a)
|
1133
1134
|
end
|
1134
1135
|
config['processors'] = processors
|
@@ -1161,6 +1162,7 @@ module OpenC3
|
|
1161
1162
|
if @related_items
|
1162
1163
|
config['related_items'] = @related_items
|
1163
1164
|
end
|
1165
|
+
config['ignore_overlap'] = true if @ignore_overlap
|
1164
1166
|
|
1165
1167
|
config
|
1166
1168
|
end
|
@@ -1182,8 +1184,8 @@ module OpenC3
|
|
1182
1184
|
else
|
1183
1185
|
packet.accessor = accessor.new(packet)
|
1184
1186
|
end
|
1185
|
-
rescue =>
|
1186
|
-
Logger.instance.error "#{packet.target_name} #{packet.packet_name} accessor of #{hash['accessor']} could not be found due to #{
|
1187
|
+
rescue => e
|
1188
|
+
Logger.instance.error "#{packet.target_name} #{packet.packet_name} accessor of #{hash['accessor']} could not be found due to #{e}"
|
1187
1189
|
end
|
1188
1190
|
end
|
1189
1191
|
packet.template = Base64.decode64(hash['template']) if hash['template']
|
@@ -1204,6 +1206,7 @@ module OpenC3
|
|
1204
1206
|
if hash['related_items']
|
1205
1207
|
packet.related_items = hash['related_items']
|
1206
1208
|
end
|
1209
|
+
packet.ignore_overlap = hash['ignore_overlap']
|
1207
1210
|
|
1208
1211
|
packet
|
1209
1212
|
end
|
@@ -1237,7 +1240,7 @@ module OpenC3
|
|
1237
1240
|
def process(buffer = @buffer)
|
1238
1241
|
return unless @processors
|
1239
1242
|
|
1240
|
-
@processors.each do |
|
1243
|
+
@processors.each do |_processor_name, processor|
|
1241
1244
|
processor.call(self, buffer)
|
1242
1245
|
end
|
1243
1246
|
end
|
@@ -220,7 +220,7 @@ module OpenC3
|
|
220
220
|
'APPEND_PARAMETER', 'APPEND_ID_ITEM', 'APPEND_ID_PARAMETER', 'APPEND_ARRAY_ITEM',\
|
221
221
|
'APPEND_ARRAY_PARAMETER', 'ALLOW_SHORT', 'HAZARDOUS', 'PROCESSOR', 'META',\
|
222
222
|
'DISABLE_MESSAGES', 'HIDDEN', 'DISABLED', 'ACCESSOR', 'TEMPLATE', 'TEMPLATE_FILE',\
|
223
|
-
'RESPONSE', 'ERROR_RESPONSE', 'SCREEN', 'RELATED_ITEM'
|
223
|
+
'RESPONSE', 'ERROR_RESPONSE', 'SCREEN', 'RELATED_ITEM', 'IGNORE_OVERLAP'
|
224
224
|
raise parser.error("No current packet for #{keyword}") unless @current_packet
|
225
225
|
|
226
226
|
process_current_packet(parser, keyword, params)
|
@@ -509,24 +509,41 @@ module OpenC3
|
|
509
509
|
when 'RESPONSE'
|
510
510
|
usage = "#{keyword} <Target Name> <Packet Name>"
|
511
511
|
parser.verify_num_parameters(2, 2, usage)
|
512
|
+
if @current_cmd_or_tlm == TELEMETRY
|
513
|
+
raise parser.error("#{keyword} only applies to command packets")
|
514
|
+
end
|
512
515
|
@current_packet.response = [params[0].upcase, params[1].upcase]
|
513
516
|
|
514
517
|
when 'ERROR_RESPONSE'
|
515
518
|
usage = "#{keyword} <Target Name> <Packet Name>"
|
516
519
|
parser.verify_num_parameters(2, 2, usage)
|
520
|
+
if @current_cmd_or_tlm == TELEMETRY
|
521
|
+
raise parser.error("#{keyword} only applies to command packets")
|
522
|
+
end
|
517
523
|
@current_packet.error_response = [params[0].upcase, params[1].upcase]
|
518
524
|
|
519
525
|
when 'SCREEN'
|
520
526
|
usage = "#{keyword} <Target Name> <Screen Name>"
|
521
527
|
parser.verify_num_parameters(2, 2, usage)
|
528
|
+
if @current_cmd_or_tlm == TELEMETRY
|
529
|
+
raise parser.error("#{keyword} only applies to command packets")
|
530
|
+
end
|
522
531
|
@current_packet.screen = [params[0].upcase, params[1].upcase]
|
523
532
|
|
524
533
|
when 'RELATED_ITEM'
|
525
534
|
usage = "#{keyword} <Target Name> <Packet Name> <Item Name>"
|
526
535
|
parser.verify_num_parameters(3, 3, usage)
|
536
|
+
if @current_cmd_or_tlm == TELEMETRY
|
537
|
+
raise parser.error("#{keyword} only applies to command packets")
|
538
|
+
end
|
527
539
|
@current_packet.related_items ||= []
|
528
540
|
@current_packet.related_items << [params[0].upcase, params[1].upcase, params[2].upcase]
|
529
541
|
|
542
|
+
when 'IGNORE_OVERLAP'
|
543
|
+
usage = "#{keyword}"
|
544
|
+
parser.verify_num_parameters(0, 0, usage)
|
545
|
+
@current_packet.ignore_overlap = true
|
546
|
+
|
530
547
|
end
|
531
548
|
|
532
549
|
end
|
@@ -173,6 +173,8 @@ module OpenC3
|
|
173
173
|
|
174
174
|
index = append? ? 3 : 4
|
175
175
|
data_type = get_data_type()
|
176
|
+
return [] if data_type == :ARRAY
|
177
|
+
return {} if data_type == :OBJECT
|
176
178
|
if data_type == :STRING or data_type == :BLOCK
|
177
179
|
# If the default value is 0x<data> (no quotes), it is treated as
|
178
180
|
# binary data. Otherwise, the default value is considered to be a string.
|
@@ -241,13 +243,13 @@ module OpenC3
|
|
241
243
|
def type_usage
|
242
244
|
keyword = @parser.keyword
|
243
245
|
# Item type usage is simple so just return it
|
244
|
-
return "<TYPE: INT/UINT/FLOAT/STRING/BLOCK/DERIVED> " if keyword.include?("ITEM")
|
246
|
+
return "<TYPE: INT/UINT/FLOAT/STRING/BLOCK/DERIVED/ARRAY/OBJECT> " if keyword.include?("ITEM")
|
245
247
|
|
246
248
|
# Build up the parameter type usage based on the keyword
|
247
249
|
usage = "<TYPE: "
|
248
250
|
# ARRAY types don't have min or max or default values
|
249
251
|
if keyword.include?("ARRAY")
|
250
|
-
usage << "INT/UINT/FLOAT/STRING/BLOCK> "
|
252
|
+
usage << "INT/UINT/FLOAT/STRING/BLOCK/OBJECT> "
|
251
253
|
else
|
252
254
|
begin
|
253
255
|
data_type = get_data_type()
|
@@ -255,14 +257,16 @@ module OpenC3
|
|
255
257
|
# If the data type could not be determined set something
|
256
258
|
data_type = :INT
|
257
259
|
end
|
258
|
-
# STRING
|
260
|
+
# STRING, BLOCK, ARRAY, OBJECT types do not have min or max values
|
259
261
|
if data_type == :STRING || data_type == :BLOCK
|
260
|
-
usage << "STRING/BLOCK> "
|
262
|
+
usage << "STRING/BLOCK/ARRAY/OBJECT> "
|
261
263
|
else
|
262
264
|
usage << "INT/UINT/FLOAT> <MIN VALUE> <MAX VALUE> "
|
263
265
|
end
|
264
|
-
# ID Values do not have default values
|
265
|
-
|
266
|
+
# ID Values do not have default values (or ARRAY/OBJECT)
|
267
|
+
unless keyword.include?("ID") or data_type == :ARRAY or data_type == :OBJECT
|
268
|
+
usage << "<DEFAULT_VALUE> "
|
269
|
+
end
|
266
270
|
end
|
267
271
|
usage
|
268
272
|
end
|