cosmos 3.1.2 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/Manifest.txt +17 -1
  4. data/autohotkey/tools/test_runner2.ahk +1 -0
  5. data/autohotkey/tools/tlm_grapher.ahk +13 -1
  6. data/data/crc.txt +39 -30
  7. data/demo/config/data/crc.txt +3 -3
  8. data/demo/config/targets/TEMPLATED/lib/templated_interface.rb +3 -1
  9. data/demo/config/tools/cmd_tlm_server/cmd_tlm_server.txt +7 -1
  10. data/demo/config/tools/cmd_tlm_server/cmd_tlm_server2.txt +6 -1
  11. data/lib/cosmos.rb +2 -2
  12. data/lib/cosmos/gui/dialogs/about_dialog.rb +18 -5
  13. data/lib/cosmos/gui/dialogs/tlm_details_dialog.rb +0 -7
  14. data/lib/cosmos/gui/line_graph/overview_graph.rb +12 -2
  15. data/lib/cosmos/gui/utilities/script_module_gui.rb +11 -3
  16. data/lib/cosmos/interfaces/interface.rb +12 -0
  17. data/lib/cosmos/interfaces/stream_interface.rb +1 -21
  18. data/lib/cosmos/interfaces/tcpip_server_interface.rb +10 -0
  19. data/lib/cosmos/io/json_drb_object.rb +75 -56
  20. data/lib/cosmos/io/tcpip_server.rb +1 -11
  21. data/lib/cosmos/packet_logs.rb +1 -0
  22. data/lib/cosmos/packet_logs/ccsds_log_reader.rb +103 -0
  23. data/lib/cosmos/packets/packet.rb +70 -1
  24. data/lib/cosmos/packets/packet_config.rb +59 -611
  25. data/lib/cosmos/packets/parsers/format_string_parser.rb +58 -0
  26. data/lib/cosmos/packets/parsers/limits_parser.rb +146 -0
  27. data/lib/cosmos/packets/parsers/limits_response_parser.rb +52 -0
  28. data/lib/cosmos/packets/parsers/macro_parser.rb +116 -0
  29. data/lib/cosmos/packets/parsers/packet_item_parser.rb +215 -0
  30. data/lib/cosmos/packets/parsers/packet_parser.rb +123 -0
  31. data/lib/cosmos/packets/parsers/processor_parser.rb +63 -0
  32. data/lib/cosmos/packets/parsers/state_parser.rb +116 -0
  33. data/lib/cosmos/packets/structure.rb +59 -22
  34. data/lib/cosmos/packets/structure_item.rb +1 -1
  35. data/lib/cosmos/script/script.rb +4 -5
  36. data/lib/cosmos/streams/serial_stream.rb +5 -0
  37. data/lib/cosmos/streams/stream.rb +8 -2
  38. data/lib/cosmos/streams/stream_protocol.rb +1 -0
  39. data/lib/cosmos/streams/tcpip_client_stream.rb +37 -7
  40. data/lib/cosmos/streams/tcpip_socket_stream.rb +9 -6
  41. data/lib/cosmos/system/target.rb +3 -6
  42. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_config.rb +57 -48
  43. data/lib/cosmos/tools/cmd_tlm_server/interface_thread.rb +7 -3
  44. data/lib/cosmos/tools/limits_monitor/limits_monitor.rb +1 -1
  45. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_realtime_thread.rb +7 -1
  46. data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +1 -2
  47. data/lib/cosmos/top_level.rb +22 -11
  48. data/lib/cosmos/utilities/message_log.rb +14 -9
  49. data/lib/cosmos/version.rb +5 -5
  50. data/spec/interfaces/cmd_tlm_server_interface_spec.rb +16 -16
  51. data/spec/interfaces/linc_interface_spec.rb +3 -0
  52. data/spec/interfaces/tcpip_client_interface_spec.rb +1 -0
  53. data/spec/interfaces/tcpip_server_interface_spec.rb +9 -0
  54. data/spec/io/json_drb_object_spec.rb +1 -1
  55. data/spec/io/serial_driver_spec.rb +0 -1
  56. data/spec/packet_logs/packet_log_writer_spec.rb +5 -3
  57. data/spec/packets/packet_config_spec.rb +22 -837
  58. data/spec/packets/packet_item_spec.rb +10 -10
  59. data/spec/packets/packet_spec.rb +239 -1
  60. data/spec/packets/parsers/format_string_parser_spec.rb +122 -0
  61. data/spec/packets/parsers/limits_parser_spec.rb +282 -0
  62. data/spec/packets/parsers/limits_response_parser_spec.rb +149 -0
  63. data/spec/packets/parsers/macro_parser_spec.rb +184 -0
  64. data/spec/packets/parsers/packet_item_parser_spec.rb +306 -0
  65. data/spec/packets/parsers/packet_parser_spec.rb +99 -0
  66. data/spec/packets/parsers/processor_parser_spec.rb +114 -0
  67. data/spec/packets/parsers/state_parser_spec.rb +156 -0
  68. data/spec/packets/structure_item_spec.rb +14 -14
  69. data/spec/packets/structure_spec.rb +162 -16
  70. data/spec/streams/fixed_stream_protocol_spec.rb +7 -4
  71. data/spec/streams/length_stream_protocol_spec.rb +3 -0
  72. data/spec/streams/preidentified_stream_protocol_spec.rb +3 -0
  73. data/spec/streams/serial_stream_spec.rb +12 -0
  74. data/spec/streams/stream_protocol_spec.rb +14 -0
  75. data/spec/streams/stream_spec.rb +1 -0
  76. data/spec/streams/tcpip_client_stream_spec.rb +3 -0
  77. data/spec/streams/tcpip_socket_stream_spec.rb +15 -3
  78. data/spec/streams/template_stream_protocol_spec.rb +5 -0
  79. data/spec/streams/terminated_stream_protocol_spec.rb +4 -0
  80. data/spec/tools/cmd_tlm_server/cmd_tlm_server_config_spec.rb +21 -1
  81. data/spec/tools/cmd_tlm_server/interface_thread_spec.rb +1 -1
  82. data/spec/tools/cmd_tlm_server/interfaces_spec.rb +1 -1
  83. metadata +19 -3
@@ -165,6 +165,53 @@ module Cosmos
165
165
  end
166
166
  end
167
167
 
168
+ # Review bit offset to look for overlapping definitions. This will allow
169
+ # gaps in the packet, but not allow the same bits to be used for multiple
170
+ # variables.
171
+ #
172
+ # @return [Array<String>] Warning messages for big definition overlaps
173
+ def check_bit_offsets
174
+ expected_next_offset = nil
175
+ previous_item = nil
176
+ warnings = []
177
+ @sorted_items.each do |item|
178
+ if expected_next_offset and item.bit_offset < expected_next_offset
179
+ msg = "Bit definition overlap at bit offset #{item.bit_offset} for packet #{@target_name} #{@packet_name} items #{item.name} and #{previous_item.name}"
180
+ Logger.instance.warn(msg)
181
+ warnings << msg
182
+ end
183
+ if item.array_size
184
+ if item.array_size > 0
185
+ expected_next_offset = item.bit_offset + item.array_size
186
+ else
187
+ expected_next_offset = item.array_size
188
+ end
189
+ else
190
+ expected_next_offset = nil
191
+ if item.bit_offset > 0
192
+ # Handle little-endian bit fields
193
+ byte_aligned = ((item.bit_offset % 8) == 0)
194
+ if item.endianness == :LITTLE_ENDIAN and (item.data_type == :INT or item.data_type == :UINT) and !(byte_aligned and (item.bit_size == 8 or item.bit_size == 16 or item.bit_size == 32 or item.bit_size == 64))
195
+ # Bit offset always refers to the most significant bit of a bitfield
196
+ bits_remaining_in_last_byte = 8 - (item.bit_offset % 8)
197
+ if item.bit_size > bits_remaining_in_last_byte
198
+ expected_next_offset = item.bit_offset + bits_remaining_in_last_byte
199
+ end
200
+ end
201
+ end
202
+ unless expected_next_offset
203
+ if item.bit_size > 0
204
+ expected_next_offset = item.bit_offset + item.bit_size
205
+ else
206
+ expected_next_offset = item.bit_size
207
+ end
208
+ end
209
+ end
210
+ previous_item = item
211
+ end
212
+ warnings
213
+ end
214
+
168
215
  # Id items are used by the identify? method to determine if a raw buffer of
169
216
  # data represents this packet.
170
217
  # @return [Array<PacketItem>] Packet item identifiers
@@ -218,11 +265,24 @@ module Cosmos
218
265
  # @param id_value [Object] Set to something other than nil to indicate that
219
266
  # this item should be used to identify a buffer as this packet. The
220
267
  # id_value should make sense according to the data_type.
268
+ # @return [PacketItem] The new packet item
221
269
  def define_item(name, bit_offset, bit_size, data_type, array_size = nil, endianness = @default_endianness, overflow = :ERROR, format_string = nil, read_conversion = nil, write_conversion = nil, id_value = nil)
222
270
  item = super(name, bit_offset, bit_size, data_type, array_size, endianness, overflow)
223
271
  packet_define_item(item, format_string, read_conversion, write_conversion, id_value)
224
272
  end
225
273
 
274
+ # Add an item to the packet by adding it to the items hash. It also
275
+ # resizes the buffer to accomodate the new item.
276
+ #
277
+ # @param item [PacketItem] Item to add to the packet
278
+ # @return [PacketItem] The same packet item
279
+ def define(item)
280
+ item = super(item)
281
+ update_id_items(item)
282
+ update_limits_items_cache()
283
+ item
284
+ end
285
+
226
286
  # Define an item at the end of the packet. This creates a new instance of the
227
287
  # item_class as given in the constructor and adds it to the items hash. It
228
288
  # also resizes the buffer to accomodate the new item.
@@ -237,6 +297,7 @@ module Cosmos
237
297
  # @param read_conversion (see #define_item)
238
298
  # @param write_conversion (see #define_item)
239
299
  # @param id_value (see #define_item)
300
+ # @return (see #define_item)
240
301
  def append_item(name, bit_size, data_type, array_size = nil, endianness = @default_endianness, overflow = :ERROR, format_string = nil, read_conversion = nil, write_conversion = nil, id_value = nil)
241
302
  item = super(name, bit_size, data_type, array_size, endianness, overflow)
242
303
  packet_define_item(item, format_string, read_conversion, write_conversion, id_value)
@@ -569,6 +630,7 @@ module Cosmos
569
630
  def clone
570
631
  packet = super()
571
632
  if packet.instance_variable_get("@processors")
633
+ packet.instance_variable_set("@processors", packet.processors.clone)
572
634
  packet.processors.each do |processor_name, processor|
573
635
  packet.processors[processor_name] = processor.clone
574
636
  end
@@ -703,12 +765,19 @@ module Cosmos
703
765
  # Change id_value to the correct type
704
766
  if id_value
705
767
  item.id_value = id_value
768
+ update_id_items(item)
769
+ end
770
+ item
771
+ end
772
+
773
+ def update_id_items(item)
774
+ if item.id_value
706
775
  @id_items ||= []
707
776
  @id_items << item
708
777
  end
709
-
710
778
  item
711
779
  end
780
+
712
781
  end # class Packet
713
782
 
714
783
  end # module Cosmos
@@ -10,9 +10,16 @@
10
10
 
11
11
  require 'cosmos/config/config_parser'
12
12
  require 'cosmos/packets/packet'
13
+ require 'cosmos/packets/parsers/packet_parser'
14
+ require 'cosmos/packets/parsers/packet_item_parser'
15
+ require 'cosmos/packets/parsers/macro_parser'
16
+ require 'cosmos/packets/parsers/limits_parser'
17
+ require 'cosmos/packets/parsers/limits_response_parser'
18
+ require 'cosmos/packets/parsers/state_parser'
19
+ require 'cosmos/packets/parsers/format_string_parser'
20
+ require 'cosmos/packets/parsers/processor_parser'
13
21
  require 'cosmos/conversions'
14
22
  require 'cosmos/processors'
15
- require 'ostruct'
16
23
 
17
24
  module Cosmos
18
25
 
@@ -49,6 +56,9 @@ module Cosmos
49
56
  # packet is not.
50
57
  attr_reader :latest_data
51
58
 
59
+ COMMAND = "Command"
60
+ TELEMETRY = "Telemetry"
61
+
52
62
  def initialize
53
63
  @name = nil
54
64
  @telemetry = {}
@@ -69,8 +79,6 @@ module Cosmos
69
79
  @telemetry['UNKNOWN']['UNKNOWN'] = Packet.new('UNKNOWN', 'UNKNOWN', :BIG_ENDIAN)
70
80
 
71
81
  # Used during packet processing
72
- @current_target_name = nil
73
- @current_packet_name = nil
74
82
  @current_cmd_or_tlm = nil
75
83
  @current_packet = nil
76
84
  @current_item = nil
@@ -86,20 +94,13 @@ module Cosmos
86
94
  #
87
95
  # @param filename [String] The name of the configuration file
88
96
  # @param target_name [String] The target name
89
- def process_file(filename, target_name)
97
+ def process_file(filename, process_target_name)
90
98
  @converted_type = nil
91
99
  @converted_bit_size = nil
92
100
  @proc_text = ''
93
101
  @building_generic_conversion = false
94
- @macro_append = OpenStruct.new
95
- @macro_append.building = false
96
- @macro_append.list = []
97
- @macro_append.indices = []
98
- @macro_append.format = ''
99
- @macro_append.format_order = ''
100
-
101
- target_name = target_name.upcase
102
102
 
103
+ process_target_name = process_target_name.upcase
103
104
  parser = ConfigParser.new("https://github.com/BallAerospace/COSMOS/wiki/Command-and-Telemetry-Configuration")
104
105
  parser.parse_file(filename) do |keyword, params|
105
106
 
@@ -127,28 +128,35 @@ module Cosmos
127
128
  case keyword
128
129
 
129
130
  # Start a new packet
130
- when 'COMMAND', 'TELEMETRY'
131
- process_packet(parser, keyword, params, target_name)
131
+ when 'COMMAND'
132
+ finish_packet()
133
+ @current_packet = PacketParser.parse_command(parser, process_target_name, @commands, @warnings)
134
+ @current_cmd_or_tlm = COMMAND
135
+
136
+ when 'TELEMETRY'
137
+ finish_packet()
138
+ @current_packet = PacketParser.parse_telemetry(parser, process_target_name, @telemetry, @latest_data, @warnings)
139
+ @current_cmd_or_tlm = TELEMETRY
132
140
 
133
141
  # Select an existing packet for editing
134
142
  when 'SELECT_COMMAND', 'SELECT_TELEMETRY'
135
143
  usage = "#{keyword} <TARGET NAME> <PACKET NAME>"
136
144
  finish_packet()
137
145
  parser.verify_num_parameters(2, 2, usage)
138
- @current_target_name = target_name
139
- @current_target_name = params[0].upcase if target_name == 'SYSTEM'
140
- @current_packet_name = params[1].upcase
146
+ target_name = process_target_name
147
+ target_name = params[0].upcase if target_name == 'SYSTEM'
148
+ packet_name = params[1].upcase
141
149
 
142
150
  @current_packet = nil
143
151
  if keyword.include?('COMMAND')
144
- @current_cmd_or_tlm = 'Command'
145
- if @commands[@current_target_name]
146
- @current_packet = @commands[@current_target_name][@current_packet_name]
152
+ @current_cmd_or_tlm = COMMAND
153
+ if @commands[target_name]
154
+ @current_packet = @commands[target_name][packet_name]
147
155
  end
148
156
  else
149
- @current_cmd_or_tlm = 'Telemetry'
150
- if @telemetry[@current_target_name]
151
- @current_packet = @telemetry[@current_target_name][@current_packet_name]
157
+ @current_cmd_or_tlm = TELEMETRY
158
+ if @telemetry[target_name]
159
+ @current_packet = @telemetry[target_name][packet_name]
152
160
  end
153
161
  end
154
162
  raise parser.error("Packet not found", usage) unless @current_packet
@@ -199,10 +207,10 @@ module Cosmos
199
207
 
200
208
  # Select an item in the current telemetry packet for editing
201
209
  when 'SELECT_PARAMETER', 'SELECT_ITEM'
202
- if (@current_cmd_or_tlm == 'Command') && (keyword.split('_')[1] == 'ITEM')
210
+ if (@current_cmd_or_tlm == COMMAND) && (keyword.split('_')[1] == 'ITEM')
203
211
  raise parser.error("SELECT_ITEM only applies to telemetry packets")
204
212
  end
205
- if (@current_cmd_or_tlm == 'Telemetry') && (keyword.split('_')[1] == 'PARAMETER')
213
+ if (@current_cmd_or_tlm == TELEMETRY) && (keyword.split('_')[1] == 'PARAMETER')
206
214
  raise parser.error("SELECT_PARAMETER only applies to command packets")
207
215
  end
208
216
  usage = "#{keyword} <#{keyword.split('_')[1]} NAME>"
@@ -211,22 +219,23 @@ module Cosmos
211
219
  begin
212
220
  @current_item = @current_packet.get_item(params[0])
213
221
  rescue # Rescue the default execption to provide a nicer error message
214
- raise parser.error("#{params[0]} not found in #{@current_cmd_or_tlm.downcase} packet #{@current_target_name} #{@current_packet_name}", usage)
222
+ raise parser.error("#{params[0]} not found in #{@current_cmd_or_tlm.downcase} packet #{@current_packet.target_name} #{@current_packet.packet_name}", usage)
215
223
  end
216
224
 
217
225
  # Start a new telemetry item in the current packet
218
226
  when 'ITEM', 'PARAMETER', 'ID_ITEM', 'ID_PARAMETER', 'ARRAY_ITEM', 'ARRAY_PARAMETER', 'APPEND_ITEM', 'APPEND_PARAMETER', 'APPEND_ID_ITEM', 'APPEND_ID_PARAMETER', 'APPEND_ARRAY_ITEM', 'APPEND_ARRAY_PARAMETER'
219
- start_item(parser, keyword, params)
227
+ start_item(parser)
220
228
 
221
229
  # Start the creation of a macro-expanded list of items
222
230
  # This simulates an array of structures of multiple items in the packet by repeating
223
231
  # each item in the list multiple times with a different "index" added to the name.
224
232
  when 'MACRO_APPEND_START'
225
- process_macro_append_start(parser, keyword, params)
233
+ MacroParser.start(parser)
226
234
 
227
235
  # End the creation of a macro-expanded list of items
228
236
  when 'MACRO_APPEND_END'
229
- process_macro_append_end(parser, keyword) # no params for END
237
+ finish_item()
238
+ MacroParser.end(parser, @current_packet)
230
239
 
231
240
  # Allow this packet to be received with less data than the defined length
232
241
  # without generating a warning.
@@ -242,7 +251,7 @@ module Cosmos
242
251
 
243
252
  # Define a processor class that will be called once when a packet is received
244
253
  when 'PROCESSOR'
245
- process_processor(parser, keyword, params)
254
+ ProcessorParser.parse(parser, @current_packet, @current_cmd_or_tlm)
246
255
 
247
256
  when 'DISABLE_MESSAGES'
248
257
  usage = "#{keyword}"
@@ -285,7 +294,7 @@ module Cosmos
285
294
 
286
295
  # Add a state to the current telemety item
287
296
  when 'STATE'
288
- process_state(parser, keyword, params)
297
+ StateParser.parse(parser, @current_packet, @current_cmd_or_tlm, @current_item, @warnings)
289
298
 
290
299
  # Apply a conversion to the current item after it is read to or
291
300
  # written from the packet
@@ -349,16 +358,17 @@ module Cosmos
349
358
 
350
359
  # Define a set of limits for the current telemetry item
351
360
  when 'LIMITS'
352
- process_limits(parser, keyword, params)
361
+ @limits_sets << LimitsParser.parse(parser, @current_packet, @current_cmd_or_tlm, @current_item, @warnings)
362
+ @limits_sets.uniq!
353
363
 
354
364
  # Define a response class that will be called when the limits state of the
355
365
  # current item changes.
356
366
  when 'LIMITS_RESPONSE'
357
- process_limits_response(parser, keyword, params)
367
+ LimitsResponseParser.parse(parser, @current_item, @current_cmd_or_tlm)
358
368
 
359
369
  # Define a printf style formatting string for the current telemetry item
360
370
  when 'FORMAT_STRING'
361
- process_format_string(parser, keyword, params)
371
+ FormatStringParser.parse(parser, @current_item)
362
372
 
363
373
  # Define the units of the current telemetry item
364
374
  when 'UNITS'
@@ -378,7 +388,7 @@ module Cosmos
378
388
  when 'REQUIRED'
379
389
  usage = "REQUIRED"
380
390
  parser.verify_num_parameters(0, 0, usage)
381
- if @current_cmd_or_tlm == 'Command'
391
+ if @current_cmd_or_tlm == COMMAND
382
392
  @current_item.required = true
383
393
  else
384
394
  raise parser.error("#{keyword} only applies to command parameters")
@@ -386,7 +396,7 @@ module Cosmos
386
396
 
387
397
  # Update the mimimum value for the current command parameter
388
398
  when 'MINIMUM_VALUE'
389
- if @current_cmd_or_tlm == 'Telemetry'
399
+ if @current_cmd_or_tlm == TELEMETRY
390
400
  raise parser.error("#{keyword} only applies to command parameters")
391
401
  end
392
402
  usage = "MINIMUM_VALUE <MINIMUM VALUE>"
@@ -397,7 +407,7 @@ module Cosmos
397
407
 
398
408
  # Update the maximum value for the current command parameter
399
409
  when 'MAXIMUM_VALUE'
400
- if @current_cmd_or_tlm == 'Telemetry'
410
+ if @current_cmd_or_tlm == TELEMETRY
401
411
  raise parser.error("#{keyword} only applies to command parameters")
402
412
  end
403
413
  usage = "MAXIMUM_VALUE <MAXIMUM VALUE>"
@@ -408,7 +418,7 @@ module Cosmos
408
418
 
409
419
  # Update the default value for the current command parameter
410
420
  when 'DEFAULT_VALUE'
411
- if @current_cmd_or_tlm == 'Telemetry'
421
+ if @current_cmd_or_tlm == TELEMETRY
412
422
  raise parser.error("#{keyword} only applies to command parameters")
413
423
  end
414
424
  usage = "DEFAULT_VALUE <DEFAULT VALUE>"
@@ -430,597 +440,35 @@ module Cosmos
430
440
  end
431
441
  end
432
442
 
433
- ####################################################
434
- # The following methods process a particular keyword
435
-
436
- def process_macro_append_start(parser, keyword, params)
437
- @macro_append.building = true
438
-
439
- usage = '#{keyword} <FIRST INDEX> <LAST INDEX> [NAME FORMAT]'
440
- parser.verify_num_parameters(2, 3, usage)
441
-
442
- # Store the params
443
- first_index = params[0].to_i
444
- last_index = params[1].to_i
445
- @macro_append.indices = [first_index, last_index].sort
446
- @macro_append.indices = (@macro_append.indices[0]..@macro_append.indices[1]).to_a
447
- @macro_append.indices.reverse! if first_index > last_index
448
- @macro_append.format = params[2] ? params[2] : '%s%d'
449
- spos = @macro_append.format.index(/%\d*s/)
450
- dpos = @macro_append.format.index(/%\d*d/)
451
- raise parser.error("Invalid NAME FORMAT (#{@macro_append.format}) for MACRO_APPEND_START", usage) unless spos and dpos
452
- if spos < dpos
453
- @macro_append.format_order = 'sd'
454
- else
455
- @macro_append.format_order = 'ds'
456
- end
457
- end
458
-
459
- def process_macro_append_end(parser, keyword)
460
- update_cache = false
461
- finish_item()
462
- parser.verify_num_parameters(0, 0, keyword)
463
- raise parser.error("Missing MACRO_APPEND_START before this config.line.", keyword) unless @macro_append.building
464
- raise parser.error("No items appended in MACRO_APPEND list", keyword) unless @macro_append.list.length > 0
465
-
466
- # Get first index, remove from array
467
- first = @macro_append.indices.shift
468
-
469
- # Rename the items in the list using the first index
470
- items = @current_packet.items
471
- @macro_append.list.each do |name|
472
- item = items[name]
473
- items.delete name
474
- if @macro_append.format_order == 'sd'
475
- first_name = sprintf(@macro_append.format, name, first)
476
- else
477
- first_name = sprintf(@macro_append.format, first, name)
478
- end
479
- item.name = first_name
480
- items[first_name] = item
481
- end
482
-
483
- # Append multiple copies of the items in the list
484
- @macro_append.indices.each do |index|
485
- @macro_append.list.each do |name|
486
- if @macro_append.format_order == 'sd'
487
- first_name = sprintf(@macro_append.format, name, first)
488
- this_name = sprintf(@macro_append.format, name, index)
489
- else
490
- first_name = sprintf(@macro_append.format, first, name)
491
- this_name = sprintf(@macro_append.format, index, name)
492
- end
493
- first_item = items[first_name]
494
- format_string = nil
495
- format_string = first_item.format_string if first_item.format_string
496
- this_item = @current_packet.append_item(this_name,
497
- first_item.bit_size,
498
- first_item.data_type,
499
- first_item.array_size,
500
- first_item.endianness,
501
- first_item.overflow,
502
- format_string,
503
- first_item.read_conversion,
504
- first_item.write_conversion,
505
- first_item.id_value)
506
- this_item.states = first_item.states if first_item.states
507
- this_item.description = first_item.description if first_item.description
508
- this_item.units_full = first_item.units_full if first_item.units_full
509
- this_item.units = first_item.units if first_item.units
510
- this_item.default = first_item.default
511
- this_item.range = first_item.range if first_item.range
512
- this_item.required = first_item.required
513
- this_item.hazardous = first_item.hazardous
514
- if first_item.state_colors
515
- this_item.state_colors = first_item.state_colors
516
- update_cache = true
517
- end
518
- if first_item.limits
519
- this_item.limits = first_item.limits
520
- update_cache = true
521
- end
522
- end
523
- end
524
- @current_packet.update_limits_items_cache if update_cache
525
-
526
- @macro_append.building = false
527
- @macro_append.indices = []
528
- @macro_append.list = []
529
- end
530
-
531
- def process_state(parser, keyword, params)
532
- if @current_cmd_or_tlm == 'Command'
533
- usage = "#{keyword} <STATE NAME> <STATE VALUE> <HAZARDOUS (Optional)> <Hazardous Description (Optional)>"
534
- parser.verify_num_parameters(2, 4, usage)
535
- else
536
- usage = "#{keyword} <STATE NAME> <STATE VALUE> <COLOR: GREEN/YELLOW/RED (Optional)>"
537
- parser.verify_num_parameters(2, 3, usage)
538
- end
539
- @current_item.states ||= {}
540
- if @current_item.states[params[0].upcase]
541
- msg = "Duplicate state defined on line #{parser.line_number}: #{parser.line}"
542
- Logger.instance.warn(msg)
543
- @warnings << msg
544
- end
545
- if @current_item.data_type == :STRING or @current_item.data_type == :BLOCK
546
- @current_item.states[params[0].upcase] = params[1]
547
- else
548
- @current_item.states[params[0].upcase] = params[1].convert_to_value
549
- end
550
- if params[2]
551
- if @current_cmd_or_tlm == 'Command'
552
- if params[2].upcase == 'HAZARDOUS'
553
- @current_item.hazardous ||= {}
554
- if params[3]
555
- @current_item.hazardous[params[0].upcase] = params[3]
556
- else
557
- @current_item.hazardous[params[0].upcase] = ""
558
- end
559
- else
560
- raise parser.error("HAZARDOUS expected as third parameter for this line.", usage)
561
- end
562
- else
563
- if params[2]
564
- color = params[2].upcase.to_sym
565
- unless PacketItem::STATE_COLORS.include? color
566
- raise parser.error("Invalid state color #{color}. Must be one of #{PacketItem::STATE_COLORS.join(' ')}.", usage)
567
- end
568
- @current_item.limits ||= Limits.new
569
- @current_item.limits.enabled = true
570
- @current_item.state_colors ||= {}
571
- @current_item.state_colors[params[0].upcase] = color
572
- @current_packet.update_limits_items_cache
573
- end
574
- end
575
- end
576
- end
577
-
578
- def process_limits(parser, keyword, params)
579
- if @current_cmd_or_tlm == 'Command'
580
- raise parser.error("#{keyword} only applies to telemetry items")
581
- end
582
- usage = "#{keyword} <LIMITS SET> <PERSISTENCE> <ENABLED/DISABLED> <RED LOW LIMIT> <YELLOW LOW LIMIT> <YELLOW HIGH LIMIT> <RED HIGH LIMIT> <GREEN LOW LIMIT (Optional)> <GREEN HIGH LIMIT (Optional)>"
583
- parser.verify_num_parameters(7, 9, usage)
584
-
585
- begin
586
- persistence = Integer(params[1])
587
- red_low = Float(params[3])
588
- yellow_low = Float(params[4])
589
- yellow_high = Float(params[5])
590
- red_high = Float(params[6])
591
- rescue
592
- raise parser.error("Invalid persistence or limits values. Ensure persistence is an integer. Limits can be integers or floats.", usage)
593
- end
594
-
595
- enabled = params[2].upcase
596
- if enabled != 'ENABLED' and enabled != 'DISABLED'
597
- raise parser.error("Initial state must be ENABLED or DISABLED.", usage)
598
- end
599
-
600
- # Verify valid limits are specified
601
- if (red_low > yellow_low) or (yellow_low >= yellow_high) or (yellow_high > red_high)
602
- raise parser.error("Invalid limits specified. Ensure yellow limits are within red limits.", usage)
603
- end
604
- if params.length != 7
605
- begin
606
- green_low = Float(params[7])
607
- green_high = Float(params[8])
608
- rescue
609
- raise parser.error("Invalid green limits values. Limits can be integers or floats.", usage)
610
- end
611
-
612
- if (yellow_low > green_low) or (green_low >= green_high) or (green_high > yellow_high)
613
- raise parser.error("Invalid limits specified. Ensure green limits are within yellow limits.", usage)
614
- end
615
- end
616
-
617
- limits_set = params[0].upcase.to_sym
618
- @limits_sets << limits_set
619
- @limits_sets.uniq!
620
- # Initialize the limits values. Values must be initialized with a :DEFAULT key
621
- if !@current_item.limits.values
622
- if limits_set == :DEFAULT
623
- @current_item.limits.values = {:DEFAULT => []}
624
- else
625
- raise parser.error("DEFAULT limits must be defined for #{@current_packet.target_name} #{@current_packet.packet_name} #{@current_item.name} before setting limits set #{limits_set}")
626
- end
627
- end
628
- if limits_set != :DEFAULT
629
- msg = nil
630
- if (enabled == 'ENABLED' and @current_item.limits.enabled != true) or (enabled != 'ENABLED' and @current_item.limits.enabled != false)
631
- msg = "#{@current_cmd_or_tlm} Item #{@current_target_name} #{@current_packet_name} #{@current_item.name} #{limits_set} limits enable setting conflict with DEFAULT"
632
- end
633
- if @current_item.limits.persistence_setting != persistence
634
- msg = "#{@current_cmd_or_tlm} Item #{@current_target_name} #{@current_packet_name} #{@current_item.name} #{limits_set} limits persistence setting conflict with DEFAULT"
635
- end
636
- if msg
637
- Logger.instance.warn msg
638
- @warnings << msg
639
- end
640
- end
641
- @current_item.limits.enabled = true if enabled == 'ENABLED'
642
- values = @current_item.limits.values
643
- if params.length == 7
644
- values[limits_set] = [red_low, yellow_low, yellow_high, red_high]
645
- else
646
- values[limits_set] = [red_low, yellow_low, yellow_high, red_high, green_low, green_high]
647
- end
648
- @current_item.limits.values = values
649
- @current_item.limits.persistence_setting = persistence
650
- @current_item.limits.persistence_count = 0
651
- @current_packet.update_limits_items_cache
652
- end
653
-
654
- def process_limits_response(parser, keyword, params)
655
- if @current_cmd_or_tlm == 'Command'
656
- raise parser.error("#{keyword} only applies to telemetry items")
657
- end
658
- usage = "#{keyword} <RESPONSE CLASS FILENAME> <RESPONSE SPECIFIC OPTIONS>"
659
- parser.verify_num_parameters(1, nil, usage)
660
-
661
- begin
662
- # require should be performed in target.txt
663
- klass = params[0].filename_to_class_name.to_class
664
- raise parser.error("#{params[0].filename_to_class_name} class not found. Did you require the file in target.txt?", usage) unless klass
665
- if params[1]
666
- @current_item.limits.response = klass.new(*params[1..(params.length - 1)])
667
- else
668
- @current_item.limits.response = klass.new
669
- end
670
- rescue Exception => err
671
- raise parser.error(err, usage)
672
- end
673
- end
674
-
675
- def process_processor(parser, keyword, params)
676
- if @current_cmd_or_tlm == 'Command'
677
- raise parser.error("#{keyword} only applies to telemetry packets")
678
- end
679
- usage = "#{keyword} <PROCESSOR NAME> <PROCESSOR CLASS FILENAME> <PROCESSOR SPECIFIC OPTIONS>"
680
- parser.verify_num_parameters(2, nil, usage)
681
-
682
- begin
683
- # require should be performed in target.txt
684
- klass = params[1].filename_to_class_name.to_class
685
- raise parser.error("#{params[1].filename_to_class_name} class not found. Did you require the file in target.txt?", usage) unless klass
686
- if params[2]
687
- processor = klass.new(*params[2..(params.length - 1)])
688
- else
689
- processor = klass.new
690
- end
691
- raise ArgumentError, "processor must be a Cosmos::Processor but is a #{processor.class}" unless Cosmos::Processor === processor
692
- processor.name = params[0]
693
- @current_packet.processors[params[0].to_s.upcase] = processor
694
- rescue Exception => err
695
- raise parser.error(err, usage)
696
- end
697
- end
698
-
699
- def process_format_string(parser, keyword, params)
700
- usage = "#{keyword} <PRINTF STYLE STRING>"
701
- parser.verify_num_parameters(1, 1, usage)
702
- @current_item.format_string = params[0]
703
- unless @current_item.read_conversion
704
- # Check format string as long as a read conversion has not been defined
705
- begin
706
- case @current_item.data_type
707
- when :INT, :UINT
708
- sprintf(@current_item.format_string, 0)
709
- when :FLOAT
710
- sprintf(@current_item.format_string, 0.0)
711
- when :STRING, :BLOCK
712
- sprintf(@current_item.format_string, 'Hello')
713
- else
714
- # Nothing to do
715
- end
716
- rescue Exception
717
- raise parser.error("Invalid #{keyword} specified for type #{@current_item.data_type}: #{params[0]}", usage)
718
- end
719
- end
720
- end
721
-
722
- def process_packet(parser, keyword, params, target_name)
723
- finish_packet()
724
-
725
- usage = "#{keyword} <TARGET NAME> <PACKET NAME> <ENDIANNESS: BIG_ENDIAN/LITTLE_ENDIAN> <DESCRIPTION (Optional)>"
726
- parser.verify_num_parameters(3, 4, usage)
727
- target_name = params[0].to_s.upcase if target_name == 'SYSTEM'
728
- packet_name = params[1].to_s.upcase
729
- endianness = params[2].to_s.upcase.intern
730
- description = params[3].to_s
731
- if endianness != :BIG_ENDIAN and endianness != :LITTLE_ENDIAN
732
- raise parser.error("Invalid endianness #{params[2]}. Must be BIG_ENDIAN or LITTLE_ENDIAN.", usage)
733
- end
734
-
735
- @current_target_name = target_name
736
- @current_packet_name = packet_name
737
- @current_cmd_or_tlm = keyword.capitalize
738
-
739
- # Be sure there is not already a packet by this name
740
- if @current_cmd_or_tlm == 'Command'
741
- if @commands[@current_target_name]
742
- if @commands[@current_target_name][@current_packet_name]
743
- msg = "#{@current_cmd_or_tlm} Packet #{@current_target_name} #{@current_packet_name} redefined."
744
- Logger.instance.warn msg
745
- @warnings << msg
746
- end
747
- end
748
- else
749
- if @telemetry[@current_target_name]
750
- if @telemetry[@current_target_name][@current_packet_name]
751
- msg = "#{@current_cmd_or_tlm} Packet #{@current_target_name} #{@current_packet_name} redefined."
752
- Logger.instance.warn msg
753
- @warnings << msg
754
- end
755
- end
756
- end
757
-
758
- @current_packet = Packet.new(@current_target_name, @current_packet_name, endianness, description)
759
-
760
- # Add received time packet items
761
- if @current_cmd_or_tlm == 'Telemetry'
762
- item = @current_packet.define_item('RECEIVED_TIMESECONDS', 0, 0, :DERIVED, nil, @current_packet.default_endianness, :ERROR, '%0.6f', ReceivedTimeSecondsConversion.new)
763
- item.description = 'COSMOS Received Time (UTC, Floating point, Unix epoch)'
764
- item = @current_packet.define_item('RECEIVED_TIMEFORMATTED', 0, 0, :DERIVED, nil, @current_packet.default_endianness, :ERROR, nil, ReceivedTimeFormattedConversion.new)
765
- item.description = 'COSMOS Received Time (Local time zone, Formatted string)'
766
- item = @current_packet.define_item('RECEIVED_COUNT', 0, 0, :DERIVED, nil, @current_packet.default_endianness, :ERROR, nil, ReceivedCountConversion.new)
767
- item.description = 'COSMOS packet received count'
768
-
769
- unless @telemetry[@current_target_name]
770
- @telemetry[@current_target_name] = {}
771
- @latest_data[@current_target_name] = {}
772
- end
773
- else
774
- @commands[@current_target_name] ||= {}
775
- end
776
- end
777
-
778
443
  # Add current packet into hash if it exists
779
444
  def finish_packet
780
445
  finish_item()
781
446
  if @current_packet
782
- # Review bit offset to look for overlapping definitions
783
- # This will allow gaps in the packet, but not allow the same bits to be
784
- # used for multiple variables.
785
- expected_next_offset = nil
786
- previous_item = nil
787
- @current_packet.sorted_items.each do |item|
788
- if expected_next_offset and item.bit_offset < expected_next_offset
789
- msg = "Bit definition overlap at bit offset #{item.bit_offset} for #{@current_cmd_or_tlm} packet #{@current_target_name} #{@current_packet_name} items #{item.name} and #{previous_item.name}"
790
- Logger.instance.warn(msg)
791
- @warnings << msg
792
- end
793
- if item.array_size
794
- if item.array_size > 0
795
- expected_next_offset = item.bit_offset + item.array_size
796
- else
797
- expected_next_offset = item.array_size
798
- end
799
- else
800
- expected_next_offset = nil
801
- if item.bit_offset > 0
802
- # Handle little-endian bit fields
803
- byte_aligned = ((item.bit_offset % 8) == 0)
804
- if item.endianness == :LITTLE_ENDIAN and (item.data_type == :INT or item.data_type == :UINT) and !(byte_aligned and (item.bit_size == 8 or item.bit_size == 16 or item.bit_size == 32 or item.bit_size == 64))
805
- # Bitoffset always refers to the most significant bit of a bitfield
806
- bits_remaining_in_last_byte = 8 - (item.bit_offset % 8)
807
- if item.bit_size > bits_remaining_in_last_byte
808
- expected_next_offset = item.bit_offset + bits_remaining_in_last_byte
809
- end
810
- end
811
- end
812
- unless expected_next_offset
813
- if item.bit_size > 0
814
- expected_next_offset = item.bit_offset + item.bit_size
815
- else
816
- expected_next_offset = item.bit_size
817
- end
818
- end
819
- end
820
- previous_item = item
821
-
822
- # Check command default and range data types if no write conversion is present
823
- item.check_default_and_range_data_types if @current_cmd_or_tlm == 'Command'
824
- end
447
+ @warnings += @current_packet.check_bit_offsets
825
448
 
826
- # commit packet to memory
827
- if @current_cmd_or_tlm == 'Command'
828
- @commands[@current_target_name][@current_packet_name] = @current_packet
449
+ if @current_cmd_or_tlm == COMMAND
450
+ PacketParser.check_item_data_types(@current_packet)
451
+ @commands[@current_packet.target_name][@current_packet.packet_name] = @current_packet
829
452
  else
830
- @telemetry[@current_target_name][@current_packet_name] = @current_packet
453
+ @telemetry[@current_packet.target_name][@current_packet.packet_name] = @current_packet
831
454
  end
832
455
  @current_packet = nil
833
456
  @current_item = nil
834
457
  end
835
458
  end
836
459
 
837
- # There are many different usages of the ITEM keword so parse the keyword
838
- # and parameters to generate the correct usage information.
839
- def generate_item_usage(keyword, params)
840
- usage = "#{keyword} <ITEM NAME> "
841
- usage << "<BIT OFFSET> " unless keyword.include?("APPEND")
842
- if keyword.include?("ARRAY")
843
- usage << "<ARRAY ITEM BIT SIZE> "
844
- else
845
- usage << "<BIT SIZE> "
846
- end
847
- if keyword.include?("PARAMETER")
848
- if keyword.include?("ARRAY")
849
- usage << "<TYPE: INT/UINT/FLOAT/STRING/BLOCK> "
850
- else
851
- if keyword.include?("APPEND")
852
- data_type = params[2].upcase.to_sym
853
- else
854
- data_type = params[3].upcase.to_sym
855
- end
856
- if data_type == :STRING or data_type == :BLOCK
857
- if keyword.include?("ID")
858
- usage << "<TYPE: STRING/BLOCK> "
859
- else
860
- usage << "<TYPE: STRING/BLOCK> <DEFAULT VALUE>"
861
- end
862
- else
863
- if keyword.include?("ID")
864
- usage << "<TYPE: INT/UINT/FLOAT> <MIN VALUE> <MAX VALUE> "
865
- else
866
- usage << "<TYPE: INT/UINT/FLOAT/DERIVED> <MIN VALUE> <MAX VALUE> <DEFAULT VALUE>"
867
- end
868
- end
869
- end
870
- else
871
- usage << "<TYPE: INT/UINT/FLOAT/STRING/BLOCK/DERIVED> "
872
- end
873
- usage << "<TOTAL ARRAY BIT SIZE> " if keyword.include?("ARRAY")
874
- if keyword.include?("ID")
875
- if keyword.include?("PARAMETER")
876
- usage << "<DEFAULT AND ID VALUE> "
877
- else
878
- usage << "<ID VALUE> "
879
- end
880
- end
881
- usage << "<DESCRIPTION (Optional)> <ENDIANNESS (Optional)>"
882
- return usage
883
- end
884
-
885
- def start_item(parser, keyword, params)
460
+ def start_item(parser)
886
461
  finish_item()
887
-
888
- usage = generate_item_usage(keyword, params)
889
- max_options = usage.count("<")
890
- parser.verify_num_parameters(max_options-2, max_options, usage)
891
- begin
892
- if params[max_options-1]
893
- endianness = params[max_options-1].to_s.upcase.intern
894
- if endianness != :BIG_ENDIAN and endianness != :LITTLE_ENDIAN
895
- raise parser.error("Invalid endianness #{params[2]}. Must be BIG_ENDIAN or LITTLE_ENDIAN.", usage)
896
- end
897
- else
898
- endianness = @current_packet.default_endianness
899
- end
900
-
901
- case keyword
902
- when /ITEM/
903
- raise parser.error("ITEM types are only valid with TELEMETRY", usage) if @current_cmd_or_tlm == 'Command'
904
- # If this is an APPEND we don't have a bit offset so the index
905
- # into the parameters changes
906
- index = (keyword =~ /APPEND/) ? 3 : 4
907
- id_value = (keyword =~ /ID_ITEM/) ? params[index] : nil
908
- array_size = (keyword =~ /ARRAY_ITEM/) ? Integer(params[index]) : nil
909
- case keyword
910
- when 'ITEM', 'ID_ITEM', 'ARRAY_ITEM'
911
- @current_item = @current_packet.define_item(params[0], # name
912
- Integer(params[1]), # bit offset
913
- Integer(params[2]), # bit size
914
- params[3].upcase.to_sym, # data_type
915
- array_size, # array size
916
- endianness, # endianness
917
- :ERROR, # overflow
918
- nil, # format string
919
- nil, # read conversion
920
- nil, # write conversion
921
- id_value) # id value
922
- when 'APPEND_ITEM', 'APPEND_ID_ITEM', 'APPEND_ARRAY_ITEM'
923
- @current_item = @current_packet.append_item(params[0], # name
924
- Integer(params[1]), # bit size
925
- params[2].upcase.to_sym, # data_type
926
- array_size, # array size
927
- endianness, # endianness
928
- :ERROR, # overflow
929
- nil, # format string
930
- nil, # read conversion
931
- nil, # write conversion
932
- id_value) # id value
933
- end
934
- when 'PARAMETER', 'ID_PARAMETER', 'ARRAY_PARAMETER'
935
- raise parser.error("PARAMETER types are only valid with COMMAND", usage) if @current_cmd_or_tlm == 'Telemetry'
936
- data_type = params[3].upcase.to_sym
937
- id_value = nil
938
- if keyword == 'ID_PARAMETER'
939
- if data_type == :DERIVED
940
- raise "DERIVED data type not allowed"
941
- elsif data_type == :STRING or data_type == :BLOCK
942
- id_value = params[4]
943
- else
944
- id_value = params[6]
945
- end
946
- end
947
- array_size = (keyword == 'ARRAY_PARAMETER') ? Integer(params[4]) : nil
948
- @current_item = @current_packet.define_item(params[0], # name
949
- Integer(params[1]), # bit offset
950
- Integer(params[2]), # bit size
951
- data_type, # data_type
952
- array_size, # array size
953
- endianness, # endianness
954
- :ERROR, # overflow
955
- nil, # format string
956
- nil, # read conversion
957
- nil, # write conversion
958
- id_value) # id value
959
- if keyword == 'ARRAY_PARAMETER'
960
- @current_item.default = []
961
- else
962
- if data_type == :STRING or data_type == :BLOCK
963
- @current_item.default = params[4]
964
- else
965
- @current_item.range =
966
- (ConfigParser.handle_defined_constants(params[4].convert_to_value))..(ConfigParser.handle_defined_constants(params[5].convert_to_value))
967
- @current_item.default = ConfigParser.handle_defined_constants(params[6].convert_to_value)
968
- end
969
- end
970
- when 'APPEND_PARAMETER', 'APPEND_ID_PARAMETER', 'APPEND_ARRAY_PARAMETER'
971
- raise parser.error("PARAMETER types are only valid with COMMAND", usage) if @current_cmd_or_tlm == 'Telemetry'
972
- data_type = params[2].upcase.to_sym
973
- id_value = nil
974
- if keyword == 'APPEND_ID_PARAMETER'
975
- if data_type == :DERIVED
976
- raise "DERIVED data type not allowed"
977
- elsif data_type == :STRING or data_type == :BLOCK
978
- id_value = params[3]
979
- else
980
- id_value = params[5]
981
- end
982
- end
983
- array_size = (keyword == 'APPEND_ARRAY_PARAMETER') ? Integer(params[3]) : nil
984
- @current_item = @current_packet.append_item(params[0], # name
985
- Integer(params[1]), # bit size
986
- data_type, # data_type
987
- array_size, # array size
988
- endianness, # endianness
989
- :ERROR, # overflow
990
- nil, # format string
991
- nil, # read conversion
992
- nil, # write conversion
993
- id_value) # id value
994
- if keyword == 'APPEND_ARRAY_PARAMETER'
995
- @current_item.default = []
996
- else
997
- if data_type == :STRING or data_type == :BLOCK
998
- @current_item.default = params[3]
999
- else
1000
- @current_item.range =
1001
- (ConfigParser.handle_defined_constants(params[3].convert_to_value))..(ConfigParser.handle_defined_constants(params[4].convert_to_value))
1002
- @current_item.default = ConfigParser.handle_defined_constants(params[5].convert_to_value)
1003
- end
1004
- end
1005
- end
1006
- @current_item.description = params[max_options-2] if params[max_options-2]
1007
-
1008
- if keyword.include?('APPEND') && @macro_append.building
1009
- @macro_append.list << params[0].upcase
1010
- end
1011
-
1012
- # Rescue the item processing since they could also throw configuration errors
1013
- rescue => err
1014
- raise parser.error(err, usage)
1015
- end
462
+ @current_item = PacketItemParser.parse(parser, @current_packet, @current_cmd_or_tlm)
463
+ MacroParser.new_item()
1016
464
  end
1017
465
 
1018
- # Finish updating item in packet
466
+ # Finish updating packet item
1019
467
  def finish_item
1020
468
  if @current_item
1021
469
  @current_packet.set_item(@current_item)
1022
- if @current_cmd_or_tlm == 'Telemetry'
1023
- target_latest_data = @latest_data[@current_target_name]
470
+ if @current_cmd_or_tlm == TELEMETRY
471
+ target_latest_data = @latest_data[@current_packet.target_name]
1024
472
  target_latest_data[@current_item.name] ||= []
1025
473
  latest_data_packets = target_latest_data[@current_item.name]
1026
474
  latest_data_packets << @current_packet unless latest_data_packets.include?(@current_packet)