openc3 5.18.0 → 5.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -4
  3. data/bin/cstol_converter +14 -14
  4. data/bin/openc3cli +189 -7
  5. data/data/config/_interfaces.yaml +1 -1
  6. data/data/config/command_modifiers.yaml +55 -0
  7. data/data/config/interface_modifiers.yaml +1 -1
  8. data/data/config/param_item_modifiers.yaml +1 -1
  9. data/data/config/parameter_modifiers.yaml +1 -1
  10. data/data/config/plugins.yaml +6 -2
  11. data/data/config/screen.yaml +2 -2
  12. data/data/config/table_manager.yaml +2 -2
  13. data/data/config/tool.yaml +4 -1
  14. data/data/config/widgets.yaml +3 -3
  15. data/ext/openc3/ext/config_parser/config_parser.c +1 -1
  16. data/ext/openc3/ext/packet/packet.c +1 -1
  17. data/ext/openc3/ext/platform/platform.c +3 -3
  18. data/ext/openc3/ext/structure/structure.c +56 -76
  19. data/lib/openc3/accessors/binary_accessor.rb +4 -4
  20. data/lib/openc3/accessors/form_accessor.rb +2 -2
  21. data/lib/openc3/accessors/http_accessor.rb +1 -1
  22. data/lib/openc3/accessors/json_accessor.rb +6 -4
  23. data/lib/openc3/accessors/template_accessor.rb +6 -9
  24. data/lib/openc3/accessors/xml_accessor.rb +1 -1
  25. data/lib/openc3/api/cmd_api.rb +35 -11
  26. data/lib/openc3/api/limits_api.rb +1 -1
  27. data/lib/openc3/config/config_parser.rb +1 -1
  28. data/lib/openc3/conversions/segmented_polynomial_conversion.rb +7 -7
  29. data/lib/openc3/core_ext/array.rb +5 -5
  30. data/lib/openc3/core_ext/exception.rb +9 -2
  31. data/lib/openc3/core_ext/string.rb +2 -2
  32. data/lib/openc3/interfaces/http_server_interface.rb +1 -0
  33. data/lib/openc3/interfaces/interface.rb +1 -1
  34. data/lib/openc3/interfaces/linc_interface.rb +3 -3
  35. data/lib/openc3/io/json_api.rb +11 -6
  36. data/lib/openc3/io/json_rpc.rb +1 -1
  37. data/lib/openc3/logs/buffered_packet_log_writer.rb +3 -3
  38. data/lib/openc3/logs/log_writer.rb +7 -8
  39. data/lib/openc3/logs/packet_log_writer.rb +7 -7
  40. data/lib/openc3/logs/text_log_writer.rb +4 -4
  41. data/lib/openc3/microservices/decom_microservice.rb +19 -4
  42. data/lib/openc3/microservices/interface_microservice.rb +41 -3
  43. data/lib/openc3/microservices/reaction_microservice.rb +2 -2
  44. data/lib/openc3/microservices/trigger_group_microservice.rb +3 -3
  45. data/lib/openc3/migrations/20240915000000_activity_uuid.rb +28 -0
  46. data/lib/openc3/models/activity_model.rb +109 -80
  47. data/lib/openc3/models/auth_model.rb +31 -2
  48. data/lib/openc3/models/cvt_model.rb +11 -5
  49. data/lib/openc3/models/gem_model.rb +8 -8
  50. data/lib/openc3/models/plugin_model.rb +3 -3
  51. data/lib/openc3/models/reducer_model.rb +2 -2
  52. data/lib/openc3/models/scope_model.rb +1 -1
  53. data/lib/openc3/models/sorted_model.rb +4 -4
  54. data/lib/openc3/models/target_model.rb +3 -3
  55. data/lib/openc3/models/tool_config_model.rb +1 -1
  56. data/lib/openc3/models/tool_model.rb +4 -4
  57. data/lib/openc3/models/widget_model.rb +11 -5
  58. data/lib/openc3/operators/operator.rb +5 -3
  59. data/lib/openc3/packets/command_validator.rb +48 -0
  60. data/lib/openc3/packets/commands.rb +6 -14
  61. data/lib/openc3/packets/packet.rb +31 -15
  62. data/lib/openc3/packets/packet_config.rb +10 -9
  63. data/lib/openc3/packets/parsers/packet_parser.rb +3 -3
  64. data/lib/openc3/packets/structure.rb +21 -13
  65. data/lib/openc3/packets/structure_item.rb +33 -47
  66. data/lib/openc3/packets/telemetry.rb +6 -27
  67. data/lib/openc3/script/api_shared.rb +7 -5
  68. data/lib/openc3/script/calendar.rb +2 -2
  69. data/lib/openc3/script/commands.rb +6 -4
  70. data/lib/openc3/script/metadata.rb +2 -2
  71. data/lib/openc3/script/suite.rb +17 -17
  72. data/lib/openc3/streams/serial_stream.rb +2 -3
  73. data/lib/openc3/streams/stream.rb +2 -2
  74. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +10 -10
  75. data/lib/openc3/tools/table_manager/table_manager_core.rb +11 -11
  76. data/lib/openc3/tools/table_manager/table_parser.rb +2 -3
  77. data/lib/openc3/topics/command_decom_topic.rb +2 -1
  78. data/lib/openc3/topics/command_topic.rb +3 -3
  79. data/lib/openc3/topics/decom_interface_topic.rb +2 -2
  80. data/lib/openc3/topics/telemetry_decom_topic.rb +1 -1
  81. data/lib/openc3/utilities/authorization.rb +2 -1
  82. data/lib/openc3/utilities/cli_generator.rb +15 -8
  83. data/lib/openc3/utilities/cosmos_rails_formatter.rb +60 -0
  84. data/lib/openc3/utilities/crc.rb +6 -6
  85. data/lib/openc3/utilities/local_mode.rb +2 -1
  86. data/lib/openc3/utilities/logger.rb +44 -34
  87. data/lib/openc3/utilities/metric.rb +1 -2
  88. data/lib/openc3/utilities/quaternion.rb +18 -18
  89. data/lib/openc3/utilities/target_file.rb +4 -4
  90. data/lib/openc3/version.rb +5 -5
  91. data/lib/openc3/win32/win32_main.rb +2 -2
  92. data/templates/tool_angular/package.json +21 -21
  93. data/templates/tool_react/package.json +10 -10
  94. data/templates/tool_svelte/package.json +11 -11
  95. data/templates/tool_svelte/src/services/openc3-api.js +17 -17
  96. data/templates/tool_vue/package.json +9 -9
  97. data/templates/widget/package.json +6 -7
  98. metadata +5 -2
@@ -19,7 +19,7 @@
19
19
  require 'openc3/logs/packet_log_writer'
20
20
 
21
21
  module OpenC3
22
- # Creates a packet log. Can automatically cycle the log based on an elasped
22
+ # Creates a packet log. Can automatically cycle the log based on an elapsed
23
23
  # time period or when the log file reaches a predefined size.
24
24
  class BufferedPacketLogWriter < PacketLogWriter
25
25
  # @param remote_log_directory [String] The path to store the log files
@@ -123,8 +123,8 @@ module OpenC3
123
123
  @buffer.each do |entry|
124
124
  write(*entry[0..-3], allow_new_file: false, take_mutex: false, received_time_nsec_since_epoch: entry[-2], extra: entry[-1])
125
125
  end
126
- rescue => err
127
- Logger.instance.error "Error writing out buffer : #{err.formatted}"
126
+ rescue => e
127
+ Logger.instance.error "Error writing out buffer : #{e.formatted}"
128
128
  end
129
129
  @buffer = []
130
130
  end
@@ -20,13 +20,12 @@
20
20
  # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
- require 'thread'
24
23
  require 'openc3/config/config_parser'
25
24
  require 'openc3/topics/topic'
26
25
  require 'openc3/utilities/bucket_utilities'
27
26
 
28
27
  module OpenC3
29
- # Creates a log. Can automatically cycle the log based on an elasped
28
+ # Creates a log. Can automatically cycle the log based on an elapsed
30
29
  # time period or when the log file reaches a predefined size.
31
30
  class LogWriter
32
31
  # @return [String] The filename of the packet log
@@ -275,10 +274,10 @@ module OpenC3
275
274
  @last_time = nil
276
275
  @previous_time_nsec_since_epoch = nil
277
276
  Logger.debug "Log File Opened : #{@filename}"
278
- rescue => err
279
- Logger.error "Error starting new log file: #{err.formatted}"
277
+ rescue => e
278
+ Logger.error "Error starting new log file: #{e.formatted}"
280
279
  @logging_enabled = false
281
- OpenC3.handle_critical_exception(err)
280
+ OpenC3.handle_critical_exception(e)
282
281
  end
283
282
 
284
283
  # @enforce_time_order requires the timestamps on each write to be greater than the previous
@@ -328,10 +327,10 @@ module OpenC3
328
327
  @last_offsets.each do |redis_topic, last_offset|
329
328
  @cleanup_offsets[-1][redis_topic] = last_offset
330
329
  end
331
- @cleanup_times << Time.now + CLEANUP_DELAY
330
+ @cleanup_times << (Time.now + CLEANUP_DELAY)
332
331
  @last_offsets.clear
333
- rescue Exception => err
334
- Logger.error "Error closing #{@filename} : #{err.formatted}"
332
+ rescue Exception => e
333
+ Logger.error "Error closing #{@filename} : #{e.formatted}"
335
334
  end
336
335
 
337
336
  @file = nil
@@ -26,7 +26,7 @@ require 'openc3/models/target_model'
26
26
  require 'cbor'
27
27
 
28
28
  module OpenC3
29
- # Creates a packet log. Can automatically cycle the log based on an elasped
29
+ # Creates a packet log. Can automatically cycle the log based on an elapsed
30
30
  # time period or when the log file reaches a predefined size.
31
31
  class PacketLogWriter < LogWriter
32
32
  include PacketLogConstants
@@ -112,9 +112,9 @@ module OpenC3
112
112
  ensure
113
113
  @mutex.unlock if take_mutex
114
114
  end
115
- rescue => err
116
- Logger.instance.error "Error writing #{@filename} : #{err.formatted}"
117
- OpenC3.handle_critical_exception(err)
115
+ rescue => e
116
+ Logger.instance.error "Error writing #{@filename} : #{e.formatted}"
117
+ OpenC3.handle_critical_exception(e)
118
118
  end
119
119
 
120
120
  # Starting a new file is a critical operation so the entire method is
@@ -133,10 +133,10 @@ module OpenC3
133
133
  @next_target_index = 0
134
134
  @target_dec_entries = []
135
135
  @packet_dec_entries = []
136
- rescue => err
137
- Logger.error "Error starting new log file: #{err.formatted}"
136
+ rescue => e
137
+ Logger.error "Error starting new log file: #{e.formatted}"
138
138
  @logging_enabled = false
139
- OpenC3.handle_critical_exception(err)
139
+ OpenC3.handle_critical_exception(e)
140
140
  end
141
141
 
142
142
  # Closing a log file isn't critical so we just log an error
@@ -24,7 +24,7 @@ require 'openc3/logs/log_writer'
24
24
  require 'socket'
25
25
 
26
26
  module OpenC3
27
- # Creates a text log. Can automatically cycle the log based on an elasped
27
+ # Creates a text log. Can automatically cycle the log based on an elapsed
28
28
  # time period or when the log file reaches a predefined size.
29
29
  class TextLogWriter < LogWriter
30
30
  NEWLINE = "\n".freeze
@@ -48,9 +48,9 @@ module OpenC3
48
48
  prepare_write(time_nsec_since_epoch, data.length, redis_topic, redis_offset)
49
49
  write_entry(time_nsec_since_epoch, data) if @file
50
50
  end
51
- rescue => err
52
- Logger.instance.error "Error writing #{@filename} : #{err.formatted}"
53
- OpenC3.handle_critical_exception(err)
51
+ rescue => e
52
+ Logger.instance.error "Error writing #{@filename} : #{e.formatted}"
53
+ OpenC3.handle_critical_exception(e)
54
54
  end
55
55
 
56
56
  def write_entry(time_nsec_since_epoch, data)
@@ -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 2022, OpenC3, Inc.
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
@@ -102,9 +102,22 @@ module OpenC3
102
102
  packet.extra = extra
103
103
  end
104
104
  packet.buffer = msg_hash["buffer"]
105
- packet.process # Run processors
106
- packet.check_limits(System.limits_set) # Process all the limits and call the limits_change_callback (as necessary)
105
+ # Processors are user code points which must be rescued
106
+ # so the TelemetryDecomTopic can write the packet
107
+ begin
108
+ packet.process # Run processors
109
+ rescue Exception => e
110
+ @error_count += 1
111
+ @metric.set(name: 'decom_error_total', value: @error_count, type: 'counter')
112
+ @error = e
113
+ @logger.error e.message
114
+ end
115
+ # Process all the limits and call the limits_change_callback (as necessary)
116
+ # check_limits also can call user code in the limits response
117
+ # but that is rescued separately in the limits_change_callback
118
+ packet.check_limits(System.limits_set)
107
119
 
120
+ # This is what updates the CVT
108
121
  TelemetryDecomTopic.write_packet(packet, scope: @scope)
109
122
  diff = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start # seconds as a float
110
123
  @metric.set(name: 'decom_duration_seconds', value: diff, type: 'gauge', unit: 'seconds')
@@ -150,12 +163,14 @@ module OpenC3
150
163
 
151
164
  if item.limits.response
152
165
  begin
166
+ # TODO: The limits response is user code and should be run as a separate thread / process
167
+ # If this code blocks it will delay TelemetryDecomTopic.write_packet
153
168
  item.limits.response.call(packet, item, old_limits_state)
154
169
  rescue Exception => e
155
170
  @error = e
156
171
  @logger.error "#{packet.target_name} #{packet.packet_name} #{item.name} Limits Response Exception!"
157
172
  @logger.error "Called with old_state = #{old_limits_state}, new_state = #{item.limits.state}"
158
- @logger.error e.formatted
173
+ @logger.error e.filtered
159
174
  end
160
175
  end
161
176
  end
@@ -31,6 +31,7 @@ require 'openc3/topics/command_topic'
31
31
  require 'openc3/topics/command_decom_topic'
32
32
  require 'openc3/topics/interface_topic'
33
33
  require 'openc3/topics/router_topic'
34
+ require 'openc3/interfaces/interface'
34
35
 
35
36
  module OpenC3
36
37
  class InterfaceCmdHandlerThread
@@ -191,22 +192,59 @@ module OpenC3
191
192
  next e.message
192
193
  end
193
194
 
195
+ command.extra ||= {}
196
+ command.extra['cmd_string'] = msg_hash['cmd_string']
197
+ command.extra['username'] = msg_hash['username']
194
198
  if hazardous_check
195
199
  hazardous, hazardous_description = System.commands.cmd_pkt_hazardous?(command)
196
200
  # Return back the error, description, and the formatted command
197
201
  # This allows the error handler to simply re-send the command
198
- next "HazardousError\n#{hazardous_description}\n#{System.commands.format(command)}" if hazardous
202
+ next "HazardousError\n#{hazardous_description}\n#{msg_hash['cmd_string']}" if hazardous
199
203
  end
200
204
 
205
+ validate = ConfigParser.handle_true_false(msg_hash['validate'])
201
206
  begin
202
207
  if @interface.connected?
208
+ result = true
209
+ reason = nil
210
+ if command.validator and validate
211
+ begin
212
+ result, reason = command.validator.pre_check(command)
213
+ rescue => e
214
+ result = false
215
+ reason = e.message
216
+ end
217
+ # Explicitly check for false to allow nil to represent unknown
218
+ if result == false
219
+ message = "pre_check returned false for #{msg_hash['cmd_string']} due to #{reason}"
220
+ raise WriteRejectError.new(message)
221
+ end
222
+ end
223
+
203
224
  @count += 1
204
225
  @metric.set(name: 'interface_cmd_total', value: @count, type: 'counter') if @metric
205
-
206
226
  @interface.write(command)
207
- CommandTopic.write_packet(command, scope: @scope)
227
+
228
+ if command.validator and validate
229
+ begin
230
+ result, reason = command.validator.post_check(command)
231
+ rescue => e
232
+ result = false
233
+ reason = e.message
234
+ end
235
+ command.extra['cmd_success'] = result
236
+ command.extra['cmd_reason'] = reason if reason
237
+ end
238
+
208
239
  CommandDecomTopic.write_packet(command, scope: @scope)
240
+ CommandTopic.write_packet(command, scope: @scope)
209
241
  InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), queued: true, scope: @scope)
242
+
243
+ # Explicitly check for false to allow nil to represent unknown
244
+ if result == false
245
+ message = "post_check returned false for #{msg_hash['cmd_string']} due to #{reason}"
246
+ raise WriteRejectError.new(message)
247
+ end
210
248
  next 'SUCCESS'
211
249
  else
212
250
  next "Interface not connected: #{@interface.name}"
@@ -368,7 +368,7 @@ module OpenC3
368
368
  end
369
369
 
370
370
  # The reaction snooze manager starts a thread pool and keeps track of when a
371
- # reaction is activated and to evalute triggers when the snooze is complete.
371
+ # reaction is activated and to evaluate triggers when the snooze is complete.
372
372
  class ReactionSnoozeManager
373
373
  attr_reader :name, :scope, :share, :thread_pool
374
374
 
@@ -432,7 +432,7 @@ module OpenC3
432
432
 
433
433
  def shutdown
434
434
  @cancel_thread = true
435
- @worker_count.times do |i|
435
+ @worker_count.times do |_i|
436
436
  @share.queue_base.enqueue(kind: nil, data: nil)
437
437
  end
438
438
  end
@@ -445,7 +445,7 @@ module OpenC3
445
445
  visited["#{trigger.name}__P"] = Hash.new
446
446
  end
447
447
  if visited["#{head.name}__P"][trigger.name]
448
- # Not sure if this is posible as on create it validates that the dependents are already created
448
+ # Not sure if this is possible as on create it validates that the dependents are already created
449
449
  message = "loop detected from #{head.name} -> #{trigger.name} path: #{visited["#{head.name}__P"]}"
450
450
  notify(name: trigger.name, severity: 'error', message: message)
451
451
  return visited["#{trigger.name}__R"] = -1
@@ -494,7 +494,7 @@ module OpenC3
494
494
  end
495
495
 
496
496
  # The trigger manager starts a thread pool and subscribes
497
- # to the telemtry decom topic. It adds the "packet" to the thread pool queue
497
+ # to the telemetry decom topic. It adds the "packet" to the thread pool queue
498
498
  # and the thread will evaluate the "trigger".
499
499
  class TriggerGroupManager
500
500
  attr_reader :name, :scope, :share, :group, :topics, :thread_pool
@@ -621,7 +621,7 @@ module OpenC3
621
621
  loop do
622
622
  triggers = TriggerModel.all(scope: @scope, group: @group)
623
623
  @share.trigger_base.rebuild(triggers: triggers)
624
- @manager.refresh() # Everytime we do a full base update we refesh the manager
624
+ @manager.refresh() # Every time we do a full base update we refresh the manager
625
625
  break if @cancel_thread
626
626
  block_for_updates()
627
627
  break if @cancel_thread
@@ -0,0 +1,28 @@
1
+ require 'openc3/utilities/migration'
2
+ require 'openc3/models/scope_model'
3
+ require 'openc3/models/timeline_model'
4
+
5
+ module OpenC3
6
+ class ActivityUuid < Migration
7
+ def self.run
8
+ ScopeModel.names.each do |scope|
9
+ TimelineModel.names.each do |key|
10
+ name = key.split('__').last
11
+ json = Store.zrange("#{scope}#{ActivityModel::PRIMARY_KEY}__#{name}", 0, -1)
12
+ parsed = json.map { |value| JSON.parse(value, :allow_nan => true, :create_additions => true) }
13
+ parsed.each_with_index do |activity, index|
14
+ if activity['uuid'].nil?
15
+ activity['uuid'] = SecureRandom.uuid
16
+ Store.zrem("#{scope}#{ActivityModel::PRIMARY_KEY}__#{name}", json[index])
17
+ Store.zadd("#{scope}#{ActivityModel::PRIMARY_KEY}__#{name}", activity['start'], JSON.generate(activity))
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ unless ENV['OPENC3_NO_MIGRATE']
27
+ OpenC3::ActivityUuid.run
28
+ end