openc3 7.5.0 → 7.5.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86e5a8920effe09dd66ebc0ca5f90e8df99948804786b07abce52711dbd03d37
4
- data.tar.gz: 612c0bfe5198ecc591bb57722931174813ee343370db9ad1474b5f9e62a87a8f
3
+ metadata.gz: f72a662e14c8daa5866c844e519021cc51515020245d05de5b121af4a85465d8
4
+ data.tar.gz: 84b48fb4c32934ff568c1696474657d84dbcbab793e89d872fc83d6a202013ba
5
5
  SHA512:
6
- metadata.gz: 36d1781323b82533f1af150fef184e63ec9b59c93006f4faaef8949f841d1983adbec5204ff5551f629cdd8d843cf843890edc3d5193249215514359c7cb437e
7
- data.tar.gz: 3eafda9e5def7b46492a96627ce1e4b9a2c22d0f2e0ea3b51279051ebb2e658833ee5b18dacc119933cd3cafcb24219d587fc1afb8f11efab33daf9025ceabad
6
+ metadata.gz: 1dafbf8d89fc0599fb46463f66686f5b82e251af28349ff607122727ce862cb58c95f391739ac6f06b4efda52388731cdde5f49254e0151815fee97394f166c5
7
+ data.tar.gz: 23ba309134de54d6a2f7b2be28d834b03355cb2df7ca0a830dd03625ec59cbdd3fa2d8dd97f4846da3c78219faa8f98613cd660c91b0b47ed2f80538c1533fba
@@ -26,6 +26,7 @@ module OpenC3
26
26
  'get_out_of_limits',
27
27
  'get_overall_limits_state',
28
28
  'limits_enabled?',
29
+ 'limits_enabled', # Python scripts call this alias since Python method names can't end in ?
29
30
  'enable_limits',
30
31
  'disable_limits',
31
32
  'get_limits',
@@ -121,29 +122,16 @@ module OpenC3
121
122
  #
122
123
  # Favor the first syntax where possible as it is more succinct.
123
124
  #
124
- # @param args [String|Array<String>] See the description for calling style
125
+ # @param args [String|Array<String>] See the description for calling style.
126
+ # A packet name of LATEST applies to every packet which contains the item.
125
127
  def enable_limits(*args, manual: false, scope: $openc3_scope, token: $openc3_token)
126
- target_name, packet_name, item_name = _tlm_process_args(args, 'enable_limits', scope: scope)
127
- authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, manual: manual, scope: scope, token: token)
128
- packet = TargetModel.packet(target_name, packet_name, scope: scope)
129
- found_item = nil
130
- packet['items'].each do |item|
131
- if item['name'] == item_name
132
- item['limits']['enabled'] = true
133
- found_item = item
134
- break
135
- end
128
+ target_name, packet_names, item_name = _tlm_process_args_all_packets(args, 'enable_limits', scope: scope)
129
+ packet_names.each do |packet_name|
130
+ authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, manual: manual, scope: scope, token: token)
131
+ end
132
+ packet_names.each do |packet_name|
133
+ _set_limits_enabled(target_name, packet_name, item_name, true, scope: scope)
136
134
  end
137
- raise "Item '#{target_name} #{packet_name} #{item_name}' does not exist while enabling limits" unless found_item
138
-
139
- TargetModel.set_packet(target_name, packet_name, packet, scope: scope)
140
-
141
- message = "Enabling Limits For '#{target_name} #{packet_name} #{item_name}'"
142
- Logger.info(message, scope: scope)
143
-
144
- event = { type: :LIMITS_ENABLE_STATE, target_name: target_name, packet_name: packet_name,
145
- item_name: item_name, enabled: true, time_nsec: Time.now.to_nsec_from_epoch, message: message }
146
- LimitsEventTopic.write(event, scope: scope)
147
135
  end
148
136
 
149
137
  # Disable limit checking for a telemetry item
@@ -154,29 +142,16 @@ module OpenC3
154
142
  #
155
143
  # Favor the first syntax where possible as it is more succinct.
156
144
  #
157
- # @param args [String|Array<String>] See the description for calling style
145
+ # @param args [String|Array<String>] See the description for calling style.
146
+ # A packet name of LATEST applies to every packet which contains the item.
158
147
  def disable_limits(*args, manual: false, scope: $openc3_scope, token: $openc3_token)
159
- target_name, packet_name, item_name = _tlm_process_args(args, 'disable_limits', scope: scope)
160
- authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, manual: manual, scope: scope, token: token)
161
- packet = TargetModel.packet(target_name, packet_name, scope: scope)
162
- found_item = nil
163
- packet['items'].each do |item|
164
- if item['name'] == item_name
165
- item['limits']['enabled'] = false
166
- found_item = item
167
- break
168
- end
148
+ target_name, packet_names, item_name = _tlm_process_args_all_packets(args, 'disable_limits', scope: scope)
149
+ packet_names.each do |packet_name|
150
+ authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, manual: manual, scope: scope, token: token)
151
+ end
152
+ packet_names.each do |packet_name|
153
+ _set_limits_enabled(target_name, packet_name, item_name, false, scope: scope)
169
154
  end
170
- raise "Item '#{target_name} #{packet_name} #{item_name}' does not exist while disabling limits" unless found_item
171
-
172
- TargetModel.set_packet(target_name, packet_name, packet, scope: scope)
173
-
174
- message = "Disabling Limits for '#{target_name} #{packet_name} #{item_name}'"
175
- Logger.info(message, scope: scope)
176
-
177
- event = { type: :LIMITS_ENABLE_STATE, target_name: target_name, packet_name: packet_name,
178
- item_name: item_name, enabled: false, time_nsec: Time.now.to_nsec_from_epoch, message: message }
179
- LimitsEventTopic.write(event, scope: scope)
180
155
  end
181
156
 
182
157
  # Get a Hash of all the limits sets defined for an item. Hash keys are the limit
@@ -189,9 +164,16 @@ module OpenC3
189
164
  #
190
165
  # @return [Hash{String => Array<Number, Number, Number, Number, Number, Number>}]
191
166
  def get_limits(target_name, packet_name, item_name, cache_timeout: nil, manual: false, scope: $openc3_scope, token: $openc3_token)
167
+ target_name = target_name.upcase
168
+ packet_name = packet_name.upcase
169
+ item_name = item_name.upcase
170
+ # Resolve LATEST before authorizing so the real packet is authorized
171
+ if packet_name == 'LATEST'
172
+ packet_name = CvtModel.determine_latest_packet_for_item(target_name, item_name, cache_timeout: cache_timeout, scope: scope)
173
+ end
192
174
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, manual: manual, scope: scope, token: token)
193
175
  limits = {}
194
- item = _get_item(target_name, packet_name, item_name, cache_timeout: cache_timeout, scope: scope)
176
+ item = TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
195
177
  item['limits'].each do |key, vals|
196
178
  next unless vals.is_a?(Hash)
197
179
 
@@ -202,56 +184,58 @@ module OpenC3
202
184
  end
203
185
 
204
186
  # Change the limits settings for a given item. By default, a new limits set called 'CUSTOM'
205
- # is created to avoid overriding existing limits.
187
+ # is created to avoid overriding existing limits. A packet name of LATEST
188
+ # applies to every packet which contains the item.
206
189
  def set_limits(target_name, packet_name, item_name, red_low, yellow_low, yellow_high, red_high,
207
190
  green_low = nil, green_high = nil, limits_set = 'CUSTOM', persistence = nil, enabled = true,
208
191
  manual: false, scope: $openc3_scope, token: $openc3_token)
209
- authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, manual: manual, scope: scope, token: token)
192
+ target_name = target_name.upcase
193
+ packet_name = packet_name.upcase
194
+ item_name = item_name.upcase
195
+ packet_names = (packet_name == 'LATEST') ? _tlm_packet_names(target_name, packet_name, item_name, scope: scope) : [packet_name]
196
+ packet_names.each do |name|
197
+ authorize(permission: 'tlm_set', target_name: target_name, packet_name: name, manual: manual, scope: scope, token: token)
198
+ end
210
199
  if (red_low > yellow_low) || (yellow_low >= yellow_high) || (yellow_high > red_high)
211
200
  raise "Invalid limits specified. Ensure yellow limits are within red limits."
212
201
  end
213
202
  if (green_low && green_high) && ((yellow_low > green_low) || (green_low >= green_high) || (green_high > yellow_high))
214
203
  raise "Invalid limits specified. Ensure green limits are within yellow limits."
215
204
  end
216
- packet = TargetModel.packet(target_name, packet_name, scope: scope)
217
- found_item = nil
218
- packet['items'].each do |item|
219
- if item['name'] == item_name
220
- if item['limits']
221
- item['limits']['persistence_setting'] = persistence if persistence
222
- if enabled
223
- item['limits']['enabled'] = true
224
- else
225
- item['limits']['enabled'] = false
226
- end
227
- limits = {}
228
- limits['red_low'] = red_low
229
- limits['yellow_low'] = yellow_low
230
- limits['yellow_high'] = yellow_high
231
- limits['red_high'] = red_high
232
- limits['green_low'] = green_low if green_low && green_high
233
- limits['green_high'] = green_high if green_low && green_high
234
- item['limits'][limits_set] = limits
235
- found_item = item
236
- break
237
- else
238
- raise "Cannot set_limits on item without any limits"
239
- end
240
- end
205
+ # Validate every packet before changing any so an error doesn't leave a partial update.
206
+ # TargetModel.packet returns a cached hash so it must not be modified until validated.
207
+ items = packet_names.map do |name|
208
+ packet = TargetModel.packet(target_name, name, scope: scope)
209
+ item = packet['items'].find { |packet_item| packet_item['name'] == item_name }
210
+ raise "Item '#{target_name} #{name} #{item_name}' does not exist while setting limits" unless item
211
+ raise "Cannot set_limits on item without any limits" unless item['limits']
212
+ [name, packet, item]
213
+ end
214
+ items.each do |name, packet, item|
215
+ item['limits']['persistence_setting'] = persistence if persistence
216
+ item['limits']['enabled'] = enabled ? true : false
217
+ limits = {}
218
+ limits['red_low'] = red_low
219
+ limits['yellow_low'] = yellow_low
220
+ limits['yellow_high'] = yellow_high
221
+ limits['red_high'] = red_high
222
+ limits['green_low'] = green_low if green_low && green_high
223
+ limits['green_high'] = green_high if green_low && green_high
224
+ item['limits'][limits_set] = limits
225
+
226
+ message = "Setting '#{target_name} #{name} #{item_name}' limits to #{red_low} #{yellow_low} #{yellow_high} #{red_high}"
227
+ message << " #{green_low} #{green_high}" if green_low && green_high
228
+ message << " in set #{limits_set} with persistence #{persistence} as enabled #{enabled}"
229
+ Logger.info(message, scope: scope)
230
+
231
+ TargetModel.set_packet(target_name, name, packet, scope: scope)
232
+
233
+ event = { type: :LIMITS_SETTINGS, target_name: target_name, packet_name: name,
234
+ item_name: item_name, red_low: red_low, yellow_low: yellow_low, yellow_high: yellow_high, red_high: red_high,
235
+ green_low: green_low, green_high: green_high, limits_set: limits_set, persistence: persistence, enabled: enabled,
236
+ time_nsec: Time.now.to_nsec_from_epoch, message: message }
237
+ LimitsEventTopic.write(event, scope: scope)
241
238
  end
242
- raise "Item '#{target_name} #{packet_name} #{item_name}' does not exist while setting limits" unless found_item
243
- message = "Setting '#{target_name} #{packet_name} #{item_name}' limits to #{red_low} #{yellow_low} #{yellow_high} #{red_high}"
244
- message << " #{green_low} #{green_high}" if green_low && green_high
245
- message << " in set #{limits_set} with persistence #{persistence} as enabled #{enabled}"
246
- Logger.info(message, scope: scope)
247
-
248
- TargetModel.set_packet(target_name, packet_name, packet, scope: scope)
249
-
250
- event = { type: :LIMITS_SETTINGS, target_name: target_name, packet_name: packet_name,
251
- item_name: item_name, red_low: red_low, yellow_low: yellow_low, yellow_high: yellow_high, red_high: red_high,
252
- green_low: green_low, green_high: green_high, limits_set: limits_set, persistence: persistence, enabled: enabled,
253
- time_nsec: Time.now.to_nsec_from_epoch, message: message }
254
- LimitsEventTopic.write(event, scope: scope)
255
239
  end
256
240
 
257
241
  # Change the color associated with a telemetry item state in realtime.
@@ -266,9 +250,16 @@ module OpenC3
266
250
  # or RED. Pass nil to clear (remove) the state color. Setting a color also
267
251
  # enables limits for the item, but clearing the color (nil) does not change
268
252
  # whether limits are enabled.
253
+ # A packet name of LATEST applies to every packet which contains the item.
269
254
  def set_state_color(target_name, packet_name, item_name, state_name, color,
270
255
  manual: false, scope: $openc3_scope, token: $openc3_token)
271
- authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, manual: manual, scope: scope, token: token)
256
+ target_name = target_name.upcase
257
+ packet_name = packet_name.upcase
258
+ item_name = item_name.upcase
259
+ packet_names = (packet_name == 'LATEST') ? _tlm_packet_names(target_name, packet_name, item_name, scope: scope) : [packet_name]
260
+ packet_names.each do |name|
261
+ authorize(permission: 'tlm_set', target_name: target_name, packet_name: name, manual: manual, scope: scope, token: token)
262
+ end
272
263
  state_name = state_name.to_s.upcase
273
264
  unless color.nil?
274
265
  color = color.to_s.upcase
@@ -276,40 +267,42 @@ module OpenC3
276
267
  raise "Invalid state color '#{color}'. Must be one of #{PacketItem::STATE_COLORS.join(', ')}."
277
268
  end
278
269
  end
279
- packet = TargetModel.packet(target_name, packet_name, scope: scope)
280
- found_item = nil
281
- packet['items'].each do |item|
282
- if item['name'] == item_name
283
- unless item['states'] && item['states'][state_name]
284
- raise "State '#{state_name}' does not exist for item '#{target_name} #{packet_name} #{item_name}'"
285
- end
286
- if color.nil?
287
- # Clear the state color
288
- item['states'][state_name].delete('color')
289
- else
290
- item['states'][state_name]['color'] = color
291
- # Defining a state color implies limits are enabled for the item
292
- item['limits'] ||= {}
293
- item['limits']['enabled'] = true
294
- end
295
- found_item = item
296
- break
270
+ # Validate every packet before changing any so an error doesn't leave a partial update.
271
+ # TargetModel.packet returns a cached hash so it must not be modified until validated.
272
+ items = packet_names.map do |name|
273
+ packet = TargetModel.packet(target_name, name, scope: scope)
274
+ item = packet['items'].find { |packet_item| packet_item['name'] == item_name }
275
+ raise "Item '#{target_name} #{name} #{item_name}' does not exist while setting state color" unless item
276
+ unless item['states'] && item['states'][state_name]
277
+ raise "State '#{state_name}' does not exist for item '#{target_name} #{name} #{item_name}'"
297
278
  end
279
+ [name, packet, item]
298
280
  end
299
- raise "Item '#{target_name} #{packet_name} #{item_name}' does not exist while setting state color" unless found_item
300
- if color.nil?
301
- message = "Clearing '#{target_name} #{packet_name} #{item_name}' state #{state_name} color"
302
- else
303
- message = "Setting '#{target_name} #{packet_name} #{item_name}' state #{state_name} color to #{color}"
304
- end
305
- Logger.info(message, scope: scope)
281
+ items.each do |name, packet, item|
282
+ if color.nil?
283
+ # Clear the state color
284
+ item['states'][state_name].delete('color')
285
+ else
286
+ item['states'][state_name]['color'] = color
287
+ # Defining a state color implies limits are enabled for the item
288
+ item['limits'] ||= {}
289
+ item['limits']['enabled'] = true
290
+ end
306
291
 
307
- TargetModel.set_packet(target_name, packet_name, packet, scope: scope)
292
+ if color.nil?
293
+ message = "Clearing '#{target_name} #{name} #{item_name}' state #{state_name} color"
294
+ else
295
+ message = "Setting '#{target_name} #{name} #{item_name}' state #{state_name} color to #{color}"
296
+ end
297
+ Logger.info(message, scope: scope)
308
298
 
309
- event = { type: :LIMITS_STATE_COLOR, target_name: target_name, packet_name: packet_name,
310
- item_name: item_name, state_name: state_name, color: color,
311
- time_nsec: Time.now.to_nsec_from_epoch, message: message }
312
- LimitsEventTopic.write(event, scope: scope)
299
+ TargetModel.set_packet(target_name, name, packet, scope: scope)
300
+
301
+ event = { type: :LIMITS_STATE_COLOR, target_name: target_name, packet_name: name,
302
+ item_name: item_name, state_name: state_name, color: color,
303
+ time_nsec: Time.now.to_nsec_from_epoch, message: message }
304
+ LimitsEventTopic.write(event, scope: scope)
305
+ end
313
306
  end
314
307
 
315
308
  # Returns all limits_groups and their members
@@ -407,58 +400,54 @@ module OpenC3
407
400
  def _limits_group(group_name, action:, manual:, scope:, token:)
408
401
  authorize(permission: 'tlm_set', manual: manual, scope: scope, token: token)
409
402
  group_name.upcase!
410
- group = get_limits_groups(scope: scope, token: token)[group_name]
411
- raise "LIMITS_GROUP #{group_name} undefined. Ensure your telemetry definition contains the line: LIMITS_GROUP #{group_name}" unless group
412
-
413
- Logger.info("#{action.to_s.capitalize} Limits Group: #{group_name}", scope: scope)
414
- last_target_name = nil
415
- last_packet_name = nil
416
- packet = nil
417
- group.sort.each do |target_name, packet_name, item_name|
418
- if last_target_name != target_name || last_packet_name != packet_name
419
- if last_target_name && last_packet_name
420
- TargetModel.set_packet(last_target_name, last_packet_name, packet, scope: scope)
421
- end
403
+ # Items grouped by target and packet so each packet is read and written once
404
+ items = LimitsEventTopic.limits_group_items(group_name, scope: scope)
405
+ raise "LIMITS_GROUP #{group_name} undefined. Ensure your telemetry definition contains the line: LIMITS_GROUP #{group_name}" unless items
406
+
407
+ enabled = (action == :enable)
408
+ message = "#{enabled ? 'Enabling' : 'Disabling'} Limits Group: #{group_name}"
409
+ Logger.info(message, scope: scope)
410
+
411
+ items.each do |target_name, packets|
412
+ packets.each do |packet_name, item_names|
422
413
  packet = TargetModel.packet(target_name, packet_name, scope: scope)
423
- end
424
- packet['items'].each do |item|
425
- if item['name'] == item_name
426
- if action == :enable
427
- enabled = true
428
- item['limits']['enabled'] = true
429
- message = "Enabling Limits for '#{target_name} #{packet_name} #{item_name}'"
430
- elsif action == :disable
431
- enabled = false
432
- item['limits']['enabled'] = false
433
- message = "Disabling Limits for '#{target_name} #{packet_name} #{item_name}'"
434
- end
435
- Logger.info(message, scope: scope)
436
-
437
- event = { type: :LIMITS_ENABLE_STATE, target_name: target_name, packet_name: packet_name,
438
- item_name: item_name, enabled: enabled, time_nsec: Time.now.to_nsec_from_epoch, message: message }
439
- LimitsEventTopic.write(event, scope: scope)
440
- break
414
+ item_names_hash = item_names.to_h { |name| [name, true] }
415
+ packet['items'].each do |item|
416
+ item['limits']['enabled'] = enabled if item_names_hash[item['name']]
441
417
  end
418
+ TargetModel.set_packet(target_name, packet_name, packet, scope: scope)
442
419
  end
443
- last_target_name = target_name
444
- last_packet_name = packet_name
445
- end
446
- if last_target_name && last_packet_name
447
- TargetModel.set_packet(last_target_name, last_packet_name, packet, scope: scope)
448
420
  end
421
+
422
+ # Write a single event for the entire group. Writing one event per item
423
+ # floods the limits event stream, which is trimmed to ~1000 entries, so
424
+ # decom microservices could miss events and leave limits enabled.
425
+ # The event only names the group (readers look up the items) so it stays
426
+ # small no matter how many items the group contains.
427
+ event = { type: :LIMITS_GROUP_ENABLE_STATE, group_name: group_name,
428
+ enabled: enabled, time_nsec: Time.now.to_nsec_from_epoch, message: message }
429
+ LimitsEventTopic.write(event, scope: scope)
449
430
  end
450
431
 
451
- # Gets an item. The code below is mostly duplicated from tlm_process_args in tlm_api.rb.
452
- #
453
- # @param target_name [String] target name
454
- # @param packet_name [String] packet name
455
- # @param item_name [String] item name
456
- # @param scope [String] scope
457
- # @return Hash The requested item based on the packet name
458
- def _get_item(target_name, packet_name, item_name, cache_timeout: nil, scope:)
459
- # Determine if this item exists, it will raise appropriate errors if not
460
- packet_name = CvtModel.determine_latest_packet_for_item(target_name, item_name, cache_timeout: cache_timeout, scope: scope) if packet_name == 'LATEST'
461
- return TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
432
+ def _set_limits_enabled(target_name, packet_name, item_name, enabled, scope:)
433
+ packet = TargetModel.packet(target_name, packet_name, scope: scope)
434
+ item = packet['items'].find { |packet_item| packet_item['name'] == item_name }
435
+ action = enabled ? 'enabling' : 'disabling'
436
+ raise "Item '#{target_name} #{packet_name} #{item_name}' does not exist while #{action} limits" unless item
437
+
438
+ item['limits']['enabled'] = enabled
439
+ TargetModel.set_packet(target_name, packet_name, packet, scope: scope)
440
+
441
+ if enabled
442
+ message = "Enabling Limits For '#{target_name} #{packet_name} #{item_name}'"
443
+ else
444
+ message = "Disabling Limits for '#{target_name} #{packet_name} #{item_name}'"
445
+ end
446
+ Logger.info(message, scope: scope)
447
+
448
+ event = { type: :LIMITS_ENABLE_STATE, target_name: target_name, packet_name: packet_name,
449
+ item_name: item_name, enabled: enabled, time_nsec: Time.now.to_nsec_from_epoch, message: message }
450
+ LimitsEventTopic.write(event, scope: scope)
462
451
  end
463
452
  end
464
453
  end