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