openc3 5.5.0.pre.beta0 → 5.5.1

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.

Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +58 -74
  3. data/data/config/_canvas_values.yaml +41 -0
  4. data/data/config/_graph_params.yaml +25 -0
  5. data/data/config/graph_settings.yaml +52 -0
  6. data/data/config/interface_modifiers.yaml +4 -0
  7. data/data/config/item_modifiers.yaml +1 -1
  8. data/data/config/microservice.yaml +5 -1
  9. data/data/config/plugins.yaml +5 -0
  10. data/data/config/screen.yaml +44 -50
  11. data/data/config/settings.yaml +144 -0
  12. data/data/config/widgets.yaml +1744 -1491
  13. data/lib/openc3/api/cmd_api.rb +43 -17
  14. data/lib/openc3/api/tlm_api.rb +37 -4
  15. data/lib/openc3/bridge/bridge.rb +3 -3
  16. data/lib/openc3/bridge/bridge_config.rb +68 -20
  17. data/lib/openc3/interfaces/interface.rb +8 -0
  18. data/lib/openc3/microservices/decom_microservice.rb +0 -3
  19. data/lib/openc3/microservices/interface_microservice.rb +2 -0
  20. data/lib/openc3/microservices/reaction_microservice.rb +0 -1
  21. data/lib/openc3/models/cvt_model.rb +1 -2
  22. data/lib/openc3/models/interface_model.rb +5 -2
  23. data/lib/openc3/models/metadata_model.rb +1 -1
  24. data/lib/openc3/models/microservice_model.rb +7 -6
  25. data/lib/openc3/models/note_model.rb +1 -1
  26. data/lib/openc3/models/plugin_model.rb +17 -8
  27. data/lib/openc3/models/timeline_model.rb +1 -1
  28. data/lib/openc3/models/trigger_group_model.rb +1 -1
  29. data/lib/openc3/operators/microservice_operator.rb +2 -2
  30. data/lib/openc3/packets/packet_item.rb +1 -1
  31. data/lib/openc3/script/storage.rb +5 -5
  32. data/lib/openc3/streams/web_socket_client_stream.rb +0 -1
  33. data/lib/openc3/tools/table_manager/table_manager_core.rb +1 -2
  34. data/lib/openc3/utilities/aws_bucket.rb +22 -4
  35. data/lib/openc3/utilities/bucket_file_cache.rb +3 -2
  36. data/lib/openc3/utilities/bucket_utilities.rb +1 -1
  37. data/lib/openc3/utilities/cli_generator.rb +210 -0
  38. data/lib/openc3/utilities/local_mode.rb +2 -2
  39. data/lib/openc3/utilities/redis_secrets.rb +4 -4
  40. data/lib/openc3/utilities/secrets.rb +5 -5
  41. data/lib/openc3/utilities/target_file.rb +43 -32
  42. data/lib/openc3/version.rb +6 -6
  43. data/templates/conversion/conversion.rb +43 -0
  44. data/templates/limits_response/response.rb +51 -0
  45. data/templates/microservice/microservices/TEMPLATE/microservice.rb +62 -0
  46. data/templates/plugin/plugin.txt +1 -0
  47. data/templates/{plugin-template → target}/targets/TARGET/screens/status.txt +1 -2
  48. metadata +23 -16
  49. data/lib/openc3/models/traefik_model.rb +0 -47
  50. data/templates/plugin-template/plugin.txt +0 -9
  51. /data/templates/{plugin-template → plugin}/LICENSE.txt +0 -0
  52. /data/templates/{plugin-template → plugin}/README.md +0 -0
  53. /data/templates/{plugin-template → plugin}/Rakefile +0 -0
  54. /data/templates/{plugin-template → plugin}/plugin.gemspec +0 -0
  55. /data/templates/{plugin-template → target}/targets/TARGET/cmd_tlm/cmd.txt +0 -0
  56. /data/templates/{plugin-template → target}/targets/TARGET/cmd_tlm/tlm.txt +0 -0
  57. /data/templates/{plugin-template → target}/targets/TARGET/lib/target.rb +0 -0
  58. /data/templates/{plugin-template → target}/targets/TARGET/procedures/procedure.rb +0 -0
  59. /data/templates/{plugin-template → target}/targets/TARGET/target.txt +0 -0
@@ -99,6 +99,7 @@ module OpenC3
99
99
  # @param interface_name [String] The interface to send the raw binary
100
100
  # @param data [String] The raw binary data
101
101
  def send_raw(interface_name, data, scope: $openc3_scope, token: $openc3_token)
102
+ interface_name = interface_name.upcase
102
103
  authorize(permission: 'cmd_raw', interface_name: interface_name, scope: scope, token: token)
103
104
  get_interface(interface_name, scope: scope, token: token) # Check to make sure the interface exists
104
105
  InterfaceTopic.write_raw(interface_name, data, scope: scope)
@@ -110,6 +111,8 @@ module OpenC3
110
111
  # @param command_name [String] Packet name of the command
111
112
  # @return [Hash] command hash with last command buffer
112
113
  def get_cmd_buffer(target_name, command_name, scope: $openc3_scope, token: $openc3_token)
114
+ target_name = target_name.upcase
115
+ command_name = command_name.upcase
113
116
  authorize(permission: 'cmd_info', target_name: target_name, packet_name: command_name, scope: scope, token: token)
114
117
  TargetModel.packet(target_name, command_name, type: :CMD, scope: scope)
115
118
  topic = "#{scope}__COMMAND__{#{target_name}}__#{command_name}"
@@ -127,6 +130,7 @@ module OpenC3
127
130
  # @param target_name [String] Name of the target
128
131
  # @return [Array<Hash>] Array of all commands as a hash
129
132
  def get_all_commands(target_name, scope: $openc3_scope, token: $openc3_token)
133
+ target_name = target_name.upcase
130
134
  authorize(permission: 'cmd_info', target_name: target_name, scope: scope, token: token)
131
135
  TargetModel.packets(target_name, type: :CMD, scope: scope)
132
136
  end
@@ -137,6 +141,7 @@ module OpenC3
137
141
  # @param target_name [String] Name of the target
138
142
  # @return [Array<String>] Array of all command packet names
139
143
  def get_all_command_names(target_name, scope: $openc3_scope, token: $openc3_token)
144
+ target_name = target_name.upcase
140
145
  authorize(permission: 'cmd_info', target_name: target_name, scope: scope, token: token)
141
146
  TargetModel.packet_names(target_name, type: :CMD, scope: scope)
142
147
  end
@@ -145,23 +150,28 @@ module OpenC3
145
150
  #
146
151
  # @since 5.0.0
147
152
  # @param target_name [String] Name of the target
148
- # @param packet_name [String] Name of the packet
153
+ # @param command_name [String] Name of the packet
149
154
  # @return [Hash] Command as a hash
150
- def get_command(target_name, packet_name, scope: $openc3_scope, token: $openc3_token)
155
+ def get_command(target_name, command_name, scope: $openc3_scope, token: $openc3_token)
156
+ target_name = target_name.upcase
157
+ command_name = command_name.upcase
151
158
  authorize(permission: 'cmd_info', target_name: target_name, scope: scope, token: token)
152
- TargetModel.packet(target_name, packet_name, type: :CMD, scope: scope)
159
+ TargetModel.packet(target_name, command_name, type: :CMD, scope: scope)
153
160
  end
154
161
 
155
162
  # Returns a hash of the given command parameter
156
163
  #
157
164
  # @since 5.0.0
158
165
  # @param target_name [String] Name of the target
159
- # @param packet_name [String] Name of the packet
160
- # @param param_name [String] Name of the parameter
166
+ # @param command_name [String] Name of the packet
167
+ # @param parameter_name [String] Name of the parameter
161
168
  # @return [Hash] Command parameter as a hash
162
- def get_parameter(target_name, packet_name, param_name, scope: $openc3_scope, token: $openc3_token)
163
- authorize(permission: 'cmd_info', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
164
- TargetModel.packet_item(target_name, packet_name, param_name, type: :CMD, scope: scope)
169
+ def get_parameter(target_name, command_name, parameter_name, scope: $openc3_scope, token: $openc3_token)
170
+ target_name = target_name.upcase
171
+ command_name = command_name.upcase
172
+ parameter_name = parameter_name.upcase
173
+ authorize(permission: 'cmd_info', target_name: target_name, packet_name: command_name, scope: scope, token: token)
174
+ TargetModel.packet_item(target_name, command_name, parameter_name, type: :CMD, scope: scope)
165
175
  end
166
176
 
167
177
  # Returns whether the specified command is hazardous
@@ -176,35 +186,38 @@ module OpenC3
176
186
  extract_string_kwargs_to_args(args, kwargs)
177
187
  case args.length
178
188
  when 1
179
- target_name, command_name, params = extract_fields_from_cmd_text(args[0], scope: scope)
189
+ target_name, command_name, parameters = extract_fields_from_cmd_text(args[0], scope: scope)
180
190
  when 2, 3
181
191
  target_name = args[0]
182
192
  command_name = args[1]
183
193
  if args.length == 2
184
- params = {}
194
+ parameters = {}
185
195
  else
186
- params = args[2]
196
+ parameters = args[2]
187
197
  end
188
198
  else
189
199
  # Invalid number of arguments
190
200
  raise "ERROR: Invalid number of arguments (#{args.length}) passed to get_cmd_hazardous()"
191
201
  end
202
+ target_name = target_name.upcase
203
+ command_name = command_name.upcase
204
+ parameters = parameters.transform_keys(&:upcase)
192
205
 
193
206
  authorize(permission: 'cmd_info', target_name: target_name, packet_name: command_name, scope: scope, token: token)
194
207
  packet = TargetModel.packet(target_name, command_name, type: :CMD, scope: scope)
195
208
  return true if packet['hazardous']
196
209
 
197
210
  packet['items'].each do |item|
198
- next unless params.keys.include?(item['name']) && item['states']
211
+ next unless parameters.keys.include?(item['name']) && item['states']
199
212
 
200
213
  # States are an array of the name followed by a hash of 'value' and sometimes 'hazardous'
201
214
  item['states'].each do |name, hash|
202
- param_name = params[item['name']]
215
+ parameter_name = parameters[item['name']]
203
216
  # Remove quotes from string parameters
204
- param_name = param_name.gsub('"', '').gsub("'", '') if param_name.is_a?(String)
217
+ parameter_name = parameter_name.gsub('"', '').gsub("'", '') if parameter_name.is_a?(String)
205
218
  # To be hazardous the state must be marked hazardous
206
219
  # Check if either the state name or value matches the param passed
207
- if hash['hazardous'] && (name == param_name || hash['value'] == param_name)
220
+ if hash['hazardous'] && (name == parameter_name || hash['value'] == parameter_name)
208
221
  return true
209
222
  end
210
223
  end
@@ -222,6 +235,9 @@ module OpenC3
222
235
  # one of {Packet::VALUE_TYPES}
223
236
  # @return [Varies] value
224
237
  def get_cmd_value(target_name, command_name, parameter_name, value_type = :CONVERTED, scope: $openc3_scope, token: $openc3_token)
238
+ target_name = target_name.upcase
239
+ command_name = command_name.upcase
240
+ parameter_name = parameter_name.upcase
225
241
  authorize(permission: 'cmd_info', target_name: target_name, packet_name: command_name, scope: scope, token: token)
226
242
  CommandDecomTopic.get_cmd_item(target_name, command_name, parameter_name, type: value_type, scope: scope)
227
243
  end
@@ -236,12 +252,15 @@ module OpenC3
236
252
  def get_cmd_time(target_name = nil, command_name = nil, scope: $openc3_scope, token: $openc3_token)
237
253
  authorize(permission: 'cmd_info', target_name: target_name, packet_name: command_name, scope: scope, token: token)
238
254
  if target_name and command_name
255
+ target_name = target_name.upcase
256
+ command_name = command_name.upcase
239
257
  time = CommandDecomTopic.get_cmd_item(target_name, command_name, 'RECEIVED_TIMESECONDS', type: :CONVERTED, scope: scope)
240
258
  [target_name, command_name, time.to_i, ((time.to_f - time.to_i) * 1_000_000).to_i]
241
259
  else
242
260
  if target_name.nil?
243
261
  targets = TargetModel.names(scope: scope)
244
262
  else
263
+ target_name = target_name.upcase
245
264
  targets = [target_name]
246
265
  end
247
266
  targets.each do |target_name|
@@ -268,6 +287,8 @@ module OpenC3
268
287
  # @param command_name [String] Packet name of the command
269
288
  # @return [Numeric] Transmit count for the command
270
289
  def get_cmd_cnt(target_name, command_name, scope: $openc3_scope, token: $openc3_token)
290
+ target_name = target_name.upcase
291
+ command_name = command_name.upcase
271
292
  authorize(permission: 'system', target_name: target_name, packet_name: command_name, scope: scope, token: token)
272
293
  TargetModel.packet(target_name, command_name, type: :CMD, scope: scope)
273
294
  Topic.get_cnt("#{scope}__COMMAND__{#{target_name}}__#{command_name}")
@@ -281,6 +302,8 @@ module OpenC3
281
302
  authorize(permission: 'system', scope: scope, token: token)
282
303
  counts = []
283
304
  target_commands.each do |target_name, command_name|
305
+ target_name = target_name.upcase
306
+ command_name = command_name.upcase
284
307
  counts << Topic.get_cnt("#{scope}__COMMAND__{#{target_name}}__#{command_name}")
285
308
  end
286
309
  counts
@@ -298,8 +321,8 @@ module OpenC3
298
321
  when 1
299
322
  target_name, cmd_name, cmd_params = extract_fields_from_cmd_text(args[0], scope: scope)
300
323
  when 2, 3
301
- target_name = args[0]
302
- cmd_name = args[1]
324
+ target_name = args[0]
325
+ cmd_name = args[1]
303
326
  if args.length == 2
304
327
  cmd_params = {}
305
328
  else
@@ -309,6 +332,9 @@ module OpenC3
309
332
  # Invalid number of arguments
310
333
  raise "ERROR: Invalid number of arguments (#{args.length}) passed to #{method_name}()"
311
334
  end
335
+ target_name = target_name.upcase
336
+ cmd_name = cmd_name.upcase
337
+ cmd_params = cmd_params.transform_keys(&:upcase)
312
338
  authorize(permission: 'cmd', target_name: target_name, packet_name: cmd_name, scope: scope, token: token)
313
339
  packet = TargetModel.packet(target_name, cmd_name, type: :CMD, scope: scope)
314
340
 
@@ -120,11 +120,14 @@ module OpenC3
120
120
  def inject_tlm(target_name, packet_name, item_hash = nil, type: :CONVERTED, scope: $openc3_scope, token: $openc3_token)
121
121
  authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
122
122
  type = type.to_s.intern
123
+ target_name = target_name.upcase
124
+ packet_name = packet_name.upcase
123
125
  unless CvtModel::VALUE_TYPES.include?(type)
124
126
  raise "Unknown type '#{type}' for #{target_name} #{packet_name}"
125
127
  end
126
128
 
127
129
  if item_hash
130
+ item_hash = item_hash.transform_keys(&:upcase)
128
131
  # Check that the items exist ... exceptions are raised if not
129
132
  TargetModel.packet_items(target_name, packet_name, item_hash.keys, scope: scope)
130
133
  else
@@ -201,6 +204,8 @@ module OpenC3
201
204
  # @param packet_name [String] Name of the packet
202
205
  # @return [Hash] telemetry hash with last telemetry buffer
203
206
  def get_tlm_buffer(target_name, packet_name, scope: $openc3_scope, token: $openc3_token)
207
+ target_name = target_name.upcase
208
+ packet_name = packet_name.upcase
204
209
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
205
210
  TargetModel.packet(target_name, packet_name, scope: scope)
206
211
  topic = "#{scope}__TELEMETRY__{#{target_name}}__#{packet_name}"
@@ -222,11 +227,13 @@ module OpenC3
222
227
  # of [item name, item value, item limits state] where the item limits
223
228
  # state can be one of {OpenC3::Limits::LIMITS_STATES}
224
229
  def get_tlm_packet(target_name, packet_name, stale_time: 30, type: :CONVERTED, scope: $openc3_scope, token: $openc3_token)
230
+ target_name = target_name.upcase
231
+ packet_name = packet_name.upcase
225
232
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
226
233
  packet = TargetModel.packet(target_name, packet_name, scope: scope)
227
234
  t = _validate_tlm_type(type)
228
235
  raise ArgumentError, "Unknown type '#{type}' for #{target_name} #{packet_name}" if t.nil?
229
- items = packet['items'].map { | item | item['name'] }
236
+ items = packet['items'].map { | item | item['name'].upcase }
230
237
  cvt_items = items.map { | item | "#{target_name}__#{packet_name}__#{item}__#{type}" }
231
238
  current_values = CvtModel.get_tlm_values(cvt_items, stale_time: stale_time, scope: scope)
232
239
  items.zip(current_values).map { | item , values | [item, values[0], values[1]]}
@@ -246,13 +253,18 @@ module OpenC3
246
253
  if !items.is_a?(Array) || !items[0].is_a?(String)
247
254
  raise ArgumentError, "items must be array of strings: ['TGT__PKT__ITEM__TYPE', ...]"
248
255
  end
249
-
250
256
  items.each_with_index do |item, index|
251
- target_name, packet_name, item_name, item_type = item.split('__')
257
+ target_name, packet_name, item_name, value_type = item.split('__')
258
+ raise ArgumentError, "items must be formatted as TGT__PKT__ITEM__TYPE" if target_name.nil? || packet_name.nil? || item_name.nil? || value_type.nil?
259
+ target_name = target_name.upcase
260
+ packet_name = packet_name.upcase
261
+ item_name = item_name.upcase
262
+ value_type = value_type.upcase
252
263
  if packet_name == 'LATEST'
253
264
  _, packet_name, _ = tlm_process_args([target_name, packet_name, item_name], 'get_tlm_values', scope: scope) # Figure out which packet is LATEST
254
- items[index] = "#{target_name}__#{packet_name}__#{item_name}__#{item_type}" # Replace LATEST with the real packet name
255
265
  end
266
+ # Change packet_name in case of LATEST and ensure upcase
267
+ items[index] = "#{target_name}__#{packet_name}__#{item_name}__#{value_type}"
256
268
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
257
269
  end
258
270
  CvtModel.get_tlm_values(items, stale_time: stale_time, scope: scope)
@@ -264,6 +276,7 @@ module OpenC3
264
276
  # @param target_name [String] Name of the target
265
277
  # @return [Array<Hash>] Array of all telemetry packet hashes
266
278
  def get_all_telemetry(target_name, scope: $openc3_scope, token: $openc3_token)
279
+ target_name = target_name.upcase
267
280
  authorize(permission: 'tlm', target_name: target_name, scope: scope, token: token)
268
281
  TargetModel.packets(target_name, type: :TLM, scope: scope)
269
282
  end
@@ -274,6 +287,7 @@ module OpenC3
274
287
  # @param target_name [String] Name of the target
275
288
  # @return [Array<String>] Array of all telemetry packet names
276
289
  def get_all_telemetry_names(target_name, scope: $openc3_scope, token: $openc3_token)
290
+ target_name = target_name.upcase
277
291
  authorize(permission: 'cmd_info', target_name: target_name, scope: scope, token: token)
278
292
  TargetModel.packet_names(target_name, type: :TLM, scope: scope)
279
293
  end
@@ -285,6 +299,8 @@ module OpenC3
285
299
  # @param packet_name [String] Name of the packet
286
300
  # @return [Hash] Telemetry packet hash
287
301
  def get_telemetry(target_name, packet_name, scope: $openc3_scope, token: $openc3_token)
302
+ target_name = target_name.upcase
303
+ packet_name = packet_name.upcase
288
304
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
289
305
  TargetModel.packet(target_name, packet_name, scope: scope)
290
306
  end
@@ -297,6 +313,9 @@ module OpenC3
297
313
  # @param item_name [String] Name of the packet
298
314
  # @return [Hash] Telemetry packet item hash
299
315
  def get_item(target_name, packet_name, item_name, scope: $openc3_scope, token: $openc3_token)
316
+ target_name = target_name.upcase
317
+ packet_name = packet_name.upcase
318
+ item_name = item_name.upcase
300
319
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
301
320
  TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
302
321
  end
@@ -316,6 +335,8 @@ module OpenC3
316
335
 
317
336
  result = {}
318
337
  packets.each do |target_name, packet_name|
338
+ target_name = target_name.upcase
339
+ packet_name = packet_name.upcase
319
340
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
320
341
  topic = "#{scope}__DECOM__{#{target_name}}__#{packet_name}"
321
342
  id, _ = Topic.get_newest_message(topic)
@@ -356,6 +377,8 @@ module OpenC3
356
377
  # @param packet_name [String] Name of the packet
357
378
  # @return [Numeric] Receive count for the telemetry packet
358
379
  def get_tlm_cnt(target_name, packet_name, scope: $openc3_scope, token: $openc3_token)
380
+ target_name = target_name.upcase
381
+ packet_name = packet_name.upcase
359
382
  authorize(permission: 'system', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
360
383
  TargetModel.packet(target_name, packet_name, scope: scope)
361
384
  Topic.get_cnt("#{scope}__TELEMETRY__{#{target_name}}__#{packet_name}")
@@ -369,6 +392,8 @@ module OpenC3
369
392
  authorize(permission: 'system', scope: scope, token: token)
370
393
  counts = []
371
394
  target_packets.each do |target_name, packet_name|
395
+ target_name = target_name.upcase
396
+ packet_name = packet_name.upcase
372
397
  counts << Topic.get_cnt("#{scope}__TELEMETRY__{#{target_name}}__#{packet_name}")
373
398
  end
374
399
  counts
@@ -380,6 +405,8 @@ module OpenC3
380
405
  # @param packet_name [String] Packet name
381
406
  # @return [Array<String>] All of the ignored telemetry items for a packet.
382
407
  def get_packet_derived_items(target_name, packet_name, scope: $openc3_scope, token: $openc3_token)
408
+ target_name = target_name.upcase
409
+ packet_name = packet_name.upcase
383
410
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
384
411
  packet = TargetModel.packet(target_name, packet_name, scope: scope)
385
412
  return packet['items'].select { |item| item['data_type'] == 'DERIVED' }.map { |item| item['name'] }
@@ -413,6 +440,9 @@ module OpenC3
413
440
  # Invalid number of arguments
414
441
  raise "ERROR: Invalid number of arguments (#{args.length}) passed to #{method_name}()"
415
442
  end
443
+ target_name = target_name.upcase
444
+ packet_name = packet_name.upcase
445
+ item_name = item_name.upcase
416
446
  if packet_name == 'LATEST'
417
447
  latest = -1
418
448
  TargetModel.packets(target_name, scope: scope).each do |packet|
@@ -449,6 +479,9 @@ module OpenC3
449
479
  # Invalid number of arguments
450
480
  raise "ERROR: Invalid number of arguments (#{args.length}) passed to #{method_name}()"
451
481
  end
482
+ target_name = target_name.upcase
483
+ packet_name = packet_name.upcase
484
+ item_name = item_name.upcase
452
485
  # Determine if this item exists, it will raise appropriate errors if not
453
486
  TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
454
487
 
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, 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'
@@ -27,8 +27,8 @@ require 'openc3/bridge/bridge_router_thread'
27
27
 
28
28
  module OpenC3
29
29
  class Bridge
30
- def initialize(filename)
31
- @config = BridgeConfig.new(filename)
30
+ def initialize(filename, existing_variables = {})
31
+ @config = BridgeConfig.new(filename, existing_variables)
32
32
  @threads = []
33
33
 
34
34
  # Start Interface Threads
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, 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/utilities/logger'
@@ -30,28 +30,55 @@ module OpenC3
30
30
  # @return [Hash<String, Interface>] Routers hash
31
31
  attr_accessor :routers
32
32
 
33
- def initialize(filename)
33
+ def initialize(filename, existing_variables = {})
34
34
  @interfaces = {}
35
35
  @routers = {}
36
- process_file(filename)
36
+ process_file(filename, existing_variables)
37
37
  end
38
38
 
39
39
  def self.generate_default(filename)
40
40
  default_config = <<~EOF
41
- # Example Host Bridge Configuration for a Serial Port
42
- #
43
- # INTERFACE <Interface Name> <Interface File> <Interface Params...>
44
- # INTERFACE <Interface Name> serial_interface.rb <Write Port> <Read Port> <Baud Rate> <Parity ODD/EVEN/NONE> <Stop Bits> <Write Timeout> <Read Timeout> <Protocol Name> <Protocol Params>
45
- # INTERFACE <Interface Name> serial_interface.rb <Write Port> <Read Port> <Baud Rate> <Parity ODD/EVEN/NONE> <Stop Bits> <Write Timeout> <Read Timeout> BURST <Discard Leading Bytes> <Sync Pattern> <Add Sync On Write>
46
- # INTERFACE SERIAL_INT serial_interface.rb /dev/ttyS1 /dev/ttyS1 38400 ODD 1 10.0 nil BURST 4 0xDEADBEEF
47
- INTERFACE SERIAL_INT serial_interface.rb COM1 COM1 9600 NONE 1 10.0 nil BURST
41
+ # Write serial port name
42
+ VARIABLE write_port_name COM1
48
43
  #{' '}
49
- # ROUTER <Router Name> <Interface File> <Interface Params...>
50
- # ROUTER SERIAL_ROUTER tcpip_server_interface.rb <Write Port> <Read Port> <Write Timeout> <Read Timeout> <Protocol Name> <Protocol Params>
51
- # ROUTER SERIAL_ROUTER tcpip_server_interface.rb <Write Port> <Read Port> <Write Timeout> <Read Timeout> BURST <Discard Leading Bytes> <Sync Pattern> <Add Sync On Write>
52
- ROUTER SERIAL_ROUTER tcpip_server_interface.rb 2950 2950 10.0 nil BURST
53
- # ROUTE <Interface Name>
54
- ROUTE SERIAL_INT
44
+ # Read serial port name
45
+ VARIABLE read_port_name COM1
46
+ #{' '}
47
+ # Baud Rate
48
+ VARIABLE baud_rate 115200
49
+ #{' '}
50
+ # Parity - NONE, ODD, or EVEN
51
+ VARIABLE parity NONE
52
+ #{' '}
53
+ # Stop bits - 0, 1, or 2
54
+ VARIABLE stop_bits 1
55
+ #{' '}
56
+ # Write Timeout
57
+ VARIABLE write_timeout 10.0
58
+ #{' '}
59
+ # Read Timeout
60
+ VARIABLE read_timeout nil
61
+ #{' '}
62
+ # Flow Control - NONE, or RTSCTS
63
+ VARIABLE flow_control NONE
64
+ #{' '}
65
+ # Data bits per word - Typically 8
66
+ VARIABLE data_bits 8
67
+ #{' '}
68
+ # Port to listen for connections from COSMOS - Plugin must match
69
+ VARIABLE router_port 2950
70
+ #{' '}
71
+ # Port to listen on for connections from COSMOS. Defaults to localhost for security. Will need to be opened
72
+ # if COSMOS is on another machine.
73
+ VARIABLE router_listen_address 127.0.0.1
74
+ #{' '}
75
+ INTERFACE SERIAL_INT serial_interface.rb <%= write_port_name %> <%= read_port_name %> <%= baud_rate %> <%= parity %> <%= stop_bits %> <%= write_timeout %> <%= read_timeout %>
76
+ OPTION FLOW_CONTROL <%= flow_control %>
77
+ OPTION DATA_BITS <%= data_bits %>
78
+ #{' '}
79
+ ROUTER SERIAL_ROUTER tcpip_server_interface.rb <%= router_port %> <%= router_port %> 10.0 nil BURST
80
+ ROUTE SERIAL_INT
81
+ OPTION LISTEN_ADDRESS <%= router_listen_address %>
55
82
  #{' '}
56
83
  EOF
57
84
 
@@ -66,18 +93,39 @@ module OpenC3
66
93
  # Processes a file and adds in the configuration defined in the file
67
94
  #
68
95
  # @param filename [String] The name of the configuration file to parse
69
- # @param recursive [Boolean] Whether process_file is being called
70
- # recursively
71
- def process_file(filename, recursive = false)
96
+ def process_file(filename, existing_variables = {})
72
97
  current_interface_or_router = nil
73
98
  current_type = nil
74
99
  current_interface_log_added = false
75
100
 
76
101
  Logger.info "Processing Bridge configuration in file: #{File.expand_path(filename)}"
77
102
 
103
+ variables = {}
78
104
  parser = ConfigParser.new
79
- parser.parse_file(filename) do |keyword, params|
105
+ parser.parse_file(filename,
106
+ false,
107
+ true,
108
+ false) do |keyword, params|
80
109
  case keyword
110
+ when 'VARIABLE'
111
+ usage = "#{keyword} <Variable Name> <Default Value>"
112
+ parser.verify_num_parameters(2, nil, usage)
113
+ variable_name = params[0]
114
+ value = params[1..-1].join(" ")
115
+ variables[variable_name] = value
116
+ if existing_variables && existing_variables.key?(variable_name)
117
+ variables[variable_name] = existing_variables[variable_name]
118
+ end
119
+ # Ignore everything else during phase 1
120
+ end
121
+ end
122
+
123
+ parser = ConfigParser.new
124
+ parser.parse_file(filename, false, true, true, variables) do |keyword, params|
125
+ case keyword
126
+
127
+ when 'VARIABLE'
128
+ # Ignore during this pass
81
129
 
82
130
  when 'INTERFACE'
83
131
  usage = "INTERFACE <Name> <Filename> <Specific Parameters>"
@@ -25,6 +25,11 @@ require 'openc3/io/raw_logger_pair'
25
25
  require 'openc3/utilities/secrets'
26
26
 
27
27
  module OpenC3
28
+ # Define a class to allow interfaces and protocols to reject commands without
29
+ # disconnecting the interface
30
+ class WriteRejectError < StandardError
31
+ end
32
+
28
33
  # Defines all the attributes and methods common to all interface classes
29
34
  # used by OpenC3.
30
35
  class Interface
@@ -330,6 +335,9 @@ module OpenC3
330
335
  else
331
336
  @write_mutex.synchronize { yield }
332
337
  end
338
+ rescue WriteRejectError => err
339
+ Logger.error("#{@name}: Write rejected by interface: #{err.message}")
340
+ raise err
333
341
  rescue Exception => err
334
342
  Logger.error("#{@name}: Error writing to interface")
335
343
  disconnect()
@@ -95,7 +95,6 @@ module OpenC3
95
95
  # @param log_change [Boolean] Whether to log this limits change event
96
96
  def limits_change_callback(packet, item, old_limits_state, value, log_change)
97
97
  return if @cancel_thread
98
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
99
98
  packet_time = packet.packet_time
100
99
  message = "#{packet.target_name} #{packet.packet_name} #{item.name} = #{value} is #{item.limits.state}"
101
100
  message << " (#{packet.packet_time.sys.formatted})" if packet_time
@@ -136,8 +135,6 @@ module OpenC3
136
135
  @logger.error e.formatted
137
136
  end
138
137
  end
139
-
140
- diff = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start # seconds as a float
141
138
  end
142
139
  end
143
140
  end
@@ -202,6 +202,8 @@ module OpenC3
202
202
  else
203
203
  next "Interface not connected: #{@interface.name}"
204
204
  end
205
+ rescue WriteRejectError => e
206
+ next e.message
205
207
  rescue => e
206
208
  @logger.error "#{@interface.name}: #{e.formatted}"
207
209
  next e.message
@@ -264,7 +264,6 @@ module OpenC3
264
264
  when 'trigger'
265
265
  process_enabled_trigger(data: data)
266
266
  end
267
- current_time = Time.now.to_i
268
267
  rescue StandardError => e
269
268
  @logger.error "ReactionWorker-#{@ident} failed to evaluate kind: #{kind} data: #{data}\n#{e.formatted}"
270
269
  end
@@ -240,11 +240,10 @@ module OpenC3
240
240
  # return an ordered array of hash with keys
241
241
  def self._parse_item(lookups, overrides, item, scope:)
242
242
  target_name, packet_name, item_name, value_type = item.split('__')
243
- raise ArgumentError, "items must be formatted as TGT__PKT__ITEM__TYPE" if target_name.nil? || packet_name.nil? || item_name.nil? || value_type.nil?
244
243
 
245
244
  # We build lookup keys by including all the less formatted types to gracefully degrade lookups
246
245
  # This allows the user to specify WITH_UNITS and if there is no conversions it will simply return the RAW value
247
- case value_type.upcase
246
+ case value_type
248
247
  when 'RAW'
249
248
  keys = [item_name]
250
249
  when 'CONVERTED'
@@ -257,12 +257,15 @@ module OpenC3
257
257
  @log_raw = true
258
258
 
259
259
  when 'SECRET'
260
- parser.verify_num_parameters(3, 4, "#{keyword} <Secret Type: ENV or FILE> <Secret Name> <Environment Variable Name or File Path> <Option Name (Optional)>")
260
+ parser.verify_num_parameters(3, 5, "#{keyword} <Secret Type: ENV or FILE> <Secret Name> <Environment Variable Name or File Path> <Option Name (Optional)> <Secret Store Name (Optional)>")
261
261
  @secrets << parameters[0..2]
262
- if parameters[3]
262
+ if ConfigParser.handle_nil(parameters[3])
263
263
  # Option Name, Secret Name
264
264
  @secret_options << [parameters[3], parameters[1]]
265
265
  end
266
+ if ConfigParser.handle_nil(parameters[4])
267
+ @secrets[-1] << parameters[4]
268
+ end
266
269
 
267
270
  else
268
271
  raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Interface/Router: #{keyword} #{parameters.join(" ")}")
@@ -69,7 +69,7 @@ module OpenC3
69
69
  if @color.nil?
70
70
  @color = '#%06x' % (rand * 0xffffff)
71
71
  end
72
- unless @color =~ /(#*)([0-9,a-f,A-F]{6})/
72
+ unless @color =~ /(#*)([0-9a-fA-F]{6})/
73
73
  raise SortedInputError.new "invalid color, must be in hex format, e.g. #FF0000"
74
74
  end
75
75
  @color = "##{@color}" unless @color.start_with?('#')
@@ -23,7 +23,6 @@
23
23
  require 'openc3/top_level'
24
24
  require 'openc3/models/model'
25
25
  require 'openc3/models/metric_model'
26
- require 'openc3/models/traefik_model'
27
26
  require 'openc3/utilities/bucket'
28
27
 
29
28
  module OpenC3
@@ -163,7 +162,7 @@ module OpenC3
163
162
  parser.verify_num_parameters(1, 2, usage)
164
163
  begin
165
164
  @ports << [Integer(parameters[0])]
166
- rescue => err # In case Integer fails
165
+ rescue # In case Integer fails
167
166
  raise ConfigParser::Error.new(parser, "Port must be an integer: #{parameters[0]}", usage)
168
167
  end
169
168
  protocol = ConfigParser.handle_nil(parameters[1])
@@ -193,8 +192,12 @@ module OpenC3
193
192
  parser.verify_num_parameters(1, 1, "#{keyword} <Container Image Name>")
194
193
  @container = parameters[0]
195
194
  when 'SECRET'
196
- parser.verify_num_parameters(3, 3, "#{keyword} <Secret Type: ENV or FILE> <Secret Name> <Environment Variable Name or File Path>")
197
- @secrets << parameters.dup
195
+ parser.verify_num_parameters(3, 4, "#{keyword} <Secret Type: ENV or FILE> <Secret Name> <Environment Variable Name or File Path> <Secret Store Name (Optional)>")
196
+ if ConfigParser.handle_nil(parameters[3])
197
+ @secrets << parameters.dup
198
+ else
199
+ @secrets << parameters[0..2]
200
+ end
198
201
  when 'ROUTE_PREFIX'
199
202
  parser.verify_num_parameters(1, 1, "#{keyword} <Route Prefix>")
200
203
  @prefix = parameters[0]
@@ -225,7 +228,6 @@ module OpenC3
225
228
  end
226
229
  end
227
230
  unless validate_only
228
- TraefikModel.register_route(microservice_name: @name, port: @ports[0][0], prefix: @prefix) if @ports[0] and ports[0][0] and @prefix
229
231
  ConfigTopic.write({ kind: 'created', type: 'microservice', name: @name, plugin: @plugin }, scope: @scope)
230
232
  end
231
233
  end
@@ -235,7 +237,6 @@ module OpenC3
235
237
  @bucket.list_objects(bucket: ENV['OPENC3_CONFIG_BUCKET'], prefix: prefix).each do |object|
236
238
  @bucket.delete_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: object.key)
237
239
  end
238
- TraefikModel.unregister_route(microservice_name: @name, port: @ports[0][0], prefix: @prefix) if @ports[0] and ports[0][0] and @prefix
239
240
  ConfigTopic.write({ kind: 'deleted', type: 'microservice', name: @name, plugin: @plugin }, scope: @scope)
240
241
  rescue Exception => error
241
242
  Logger.error("Error undeploying microservice model #{@name} in scope #{@scope} due to #{error}")
@@ -78,7 +78,7 @@ module OpenC3
78
78
  if @color.nil?
79
79
  @color = '#%06x' % (rand * 0xffffff)
80
80
  end
81
- unless @color =~ /(#*)([0-9,a-f,A-F]{6})/
81
+ unless @color =~ /(#*)([0-9a-fA-F]{6})/
82
82
  raise SortedInputError.new "invalid color, must be in hex format, e.g. #FF0000"
83
83
  end
84
84
  @color = "##{@color}" unless @color.start_with?('#')