openc3 5.10.1 → 5.11.0

Sign up to get free protection for your applications and to get access to all the features.
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)