openc3 5.0.9 → 5.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/data/config/_id_items.yaml +2 -1
  3. data/data/config/_id_params.yaml +2 -1
  4. data/data/config/_items.yaml +2 -1
  5. data/data/config/_params.yaml +2 -1
  6. data/data/config/command_modifiers.yaml +30 -0
  7. data/data/config/item_modifiers.yaml +10 -0
  8. data/data/config/microservice.yaml +12 -0
  9. data/data/config/param_item_modifiers.yaml +10 -0
  10. data/data/config/plugins.yaml +0 -9
  11. data/data/config/telemetry_modifiers.yaml +10 -0
  12. data/ext/openc3/ext/packet/packet.c +20 -2
  13. data/ext/openc3/ext/structure/structure.c +12 -17
  14. data/lib/openc3/accessors/accessor.rb +71 -0
  15. data/lib/openc3/accessors/binary_accessor.rb +1226 -0
  16. data/lib/openc3/accessors/cbor_accessor.rb +83 -0
  17. data/lib/openc3/accessors/html_accessor.rb +28 -0
  18. data/lib/openc3/accessors/json_accessor.rb +131 -0
  19. data/lib/openc3/accessors/xml_accessor.rb +67 -0
  20. data/lib/openc3/accessors.rb +23 -0
  21. data/lib/openc3/config/config_parser.rb +10 -4
  22. data/lib/openc3/core_ext/tempfile.rb +20 -0
  23. data/lib/openc3/models/cvt_model.rb +1 -10
  24. data/lib/openc3/models/microservice_model.rb +26 -0
  25. data/lib/openc3/packets/binary_accessor.rb +2 -1207
  26. data/lib/openc3/packets/packet.rb +106 -6
  27. data/lib/openc3/packets/packet_config.rb +30 -7
  28. data/lib/openc3/packets/parsers/limits_response_parser.rb +1 -3
  29. data/lib/openc3/packets/parsers/processor_parser.rb +1 -2
  30. data/lib/openc3/packets/structure.rb +39 -14
  31. data/lib/openc3/packets/structure_item.rb +15 -1
  32. data/lib/openc3/script/storage.rb +1 -0
  33. data/lib/openc3/utilities/local_mode.rb +3 -0
  34. data/lib/openc3/utilities/simulated_target.rb +3 -2
  35. data/lib/openc3/version.rb +5 -5
  36. data/lib/openc3.rb +1 -0
  37. metadata +38 -2
@@ -1,1207 +1,2 @@
1
- # encoding: ascii-8bit
2
-
3
- # Copyright 2022 Ball Aerospace & Technologies Corp.
4
- # All Rights Reserved.
5
- #
6
- # This program is free software; you can modify and/or redistribute it
7
- # under the terms of the GNU Affero General Public License
8
- # as published by the Free Software Foundation; version 3 with
9
- # attribution addendums as found in the LICENSE.txt
10
- #
11
- # This program is distributed in the hope that it will be useful,
12
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
- # GNU Affero General Public License for more details.
15
-
16
- # Modified by OpenC3, Inc.
17
- # All changes Copyright 2022, OpenC3, Inc.
18
- # All Rights Reserved
19
-
20
- # This file contains the implementation of the BinaryAccessor class.
21
- # This class allows for easy reading and writing of binary data in Ruby
22
-
23
- require 'openc3/ext/packet' if RUBY_ENGINE == 'ruby' and !ENV['OPENC3_NO_EXT']
24
-
25
- module OpenC3
26
- # Provides methods for binary reading and writing
27
- class BinaryAccessor
28
- # Constants for ruby packing directives
29
- PACK_8_BIT_INT = 'c'
30
- PACK_NATIVE_16_BIT_INT = 's'
31
- PACK_LITTLE_ENDIAN_16_BIT_UINT = 'v'
32
- PACK_BIG_ENDIAN_16_BIT_UINT = 'n'
33
- PACK_NATIVE_32_BIT_INT = 'l'
34
- PACK_NATIVE_32_BIT_UINT = 'L'
35
- PACK_NATIVE_64_BIT_INT = 'q'
36
- PACK_NATIVE_64_BIT_UINT = 'Q'
37
- PACK_LITTLE_ENDIAN_32_BIT_UINT = 'V'
38
- PACK_BIG_ENDIAN_32_BIT_UINT = 'N'
39
- PACK_LITTLE_ENDIAN_32_BIT_FLOAT = 'e'
40
- PACK_LITTLE_ENDIAN_64_BIT_FLOAT = 'E'
41
- PACK_BIG_ENDIAN_32_BIT_FLOAT = 'g'
42
- PACK_BIG_ENDIAN_64_BIT_FLOAT = 'G'
43
- PACK_NULL_TERMINATED_STRING = 'Z*'
44
- PACK_BLOCK = 'a*'
45
- PACK_8_BIT_INT_ARRAY = 'c*'
46
- PACK_8_BIT_UINT_ARRAY = 'C*'
47
- PACK_NATIVE_16_BIT_INT_ARRAY = 's*'
48
- PACK_BIG_ENDIAN_16_BIT_UINT_ARRAY = 'n*'
49
- PACK_LITTLE_ENDIAN_16_BIT_UINT_ARRAY = 'v*'
50
- PACK_NATIVE_32_BIT_INT_ARRAY = 'l*'
51
- PACK_BIG_ENDIAN_32_BIT_UINT_ARRAY = 'N*'
52
- PACK_LITTLE_ENDIAN_32_BIT_UINT_ARRAY = 'V*'
53
- PACK_NATIVE_64_BIT_INT_ARRAY = 'q*'
54
- PACK_NATIVE_64_BIT_UINT_ARRAY = 'Q*'
55
- PACK_LITTLE_ENDIAN_32_BIT_FLOAT_ARRAY = 'e*'
56
- PACK_LITTLE_ENDIAN_64_BIT_FLOAT_ARRAY = 'E*'
57
- PACK_BIG_ENDIAN_32_BIT_FLOAT_ARRAY = 'g*'
58
- PACK_BIG_ENDIAN_64_BIT_FLOAT_ARRAY = 'G*'
59
-
60
- if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
61
- MIN_INT8 = -128
62
- MAX_INT8 = 127
63
- MAX_UINT8 = 255
64
- MIN_INT16 = -32768
65
- MAX_INT16 = 32767
66
- MAX_UINT16 = 65535
67
- MIN_INT32 = -(2**31)
68
- MAX_INT32 = (2**31) - 1
69
- MAX_UINT32 = (2**32) - 1
70
- MIN_INT64 = -(2**63)
71
- MAX_INT64 = (2**63) - 1
72
- MAX_UINT64 = (2**64) - 1
73
- end
74
-
75
- # Additional Constants
76
- ZERO_STRING = "\000"
77
-
78
- # Valid data types
79
- DATA_TYPES = [:INT, :UINT, :FLOAT, :STRING, :BLOCK]
80
-
81
- # Valid overflow types
82
- OVERFLOW_TYPES = [:TRUNCATE, :SATURATE, :ERROR, :ERROR_ALLOW_HEX]
83
-
84
- protected
85
-
86
- # Determines the endianness of the host running this code
87
- #
88
- # This method is protected to force the use of the constant
89
- # HOST_ENDIANNESS rather than this method
90
- #
91
- # @return [Symbol] :BIG_ENDIAN or :LITTLE_ENDIAN
92
- def self.get_host_endianness
93
- value = 0x01020304
94
- packed = [value].pack(PACK_NATIVE_32_BIT_UINT)
95
- unpacked = packed.unpack(PACK_LITTLE_ENDIAN_32_BIT_UINT)[0]
96
- if unpacked == value
97
- :LITTLE_ENDIAN
98
- else
99
- :BIG_ENDIAN
100
- end
101
- end
102
-
103
- def self.raise_buffer_error(read_write, buffer, data_type, given_bit_offset, given_bit_size)
104
- raise ArgumentError, "#{buffer.length} byte buffer insufficient to #{read_write} #{data_type} at bit_offset #{given_bit_offset} with bit_size #{given_bit_size}"
105
- end
106
-
107
- public
108
-
109
- # Store the host endianness so that it only has to be determined once
110
- HOST_ENDIANNESS = get_host_endianness()
111
- # Valid endianess
112
- ENDIANNESS = [:BIG_ENDIAN, :LITTLE_ENDIAN]
113
-
114
- if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
115
- # Reads binary data of any data type from a buffer
116
- #
117
- # @param bit_offset [Integer] Bit offset to the start of the item. A
118
- # negative number means to offset from the end of the buffer.
119
- # @param bit_size [Integer] Size of the item in bits
120
- # @param data_type [Symbol] {DATA_TYPES}
121
- # @param buffer [String] Binary string buffer to read from
122
- # @param endianness [Symbol] {ENDIANNESS}
123
- # @return [Integer] value read from the buffer
124
- def self.read(bit_offset, bit_size, data_type, buffer, endianness)
125
- given_bit_offset = bit_offset
126
- given_bit_size = bit_size
127
-
128
- bit_offset = check_bit_offset_and_size(:read, given_bit_offset, given_bit_size, data_type, buffer)
129
-
130
- # If passed a negative bit size with strings or blocks
131
- # recalculate based on the buffer length
132
- if (bit_size <= 0) && ((data_type == :STRING) || (data_type == :BLOCK))
133
- bit_size = (buffer.length * 8) - bit_offset + bit_size
134
- if bit_size == 0
135
- return ""
136
- elsif bit_size < 0
137
- raise_buffer_error(:read, buffer, data_type, given_bit_offset, given_bit_size)
138
- end
139
- end
140
-
141
- result, lower_bound, upper_bound = check_bounds_and_buffer_size(bit_offset, bit_size, buffer.length, endianness, data_type)
142
- raise_buffer_error(:read, buffer, data_type, given_bit_offset, given_bit_size) unless result
143
-
144
- if (data_type == :STRING) || (data_type == :BLOCK)
145
- #######################################
146
- # Handle :STRING and :BLOCK data types
147
- #######################################
148
-
149
- if byte_aligned(bit_offset)
150
- if data_type == :STRING
151
- return buffer[lower_bound..upper_bound].unpack('Z*')[0]
152
- else
153
- return buffer[lower_bound..upper_bound].unpack('a*')[0]
154
- end
155
- else
156
- raise(ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}")
157
- end
158
-
159
- elsif (data_type == :INT) || (data_type == :UINT)
160
- ###################################
161
- # Handle :INT and :UINT data types
162
- ###################################
163
-
164
- if byte_aligned(bit_offset) && even_bit_size(bit_size)
165
-
166
- if data_type == :INT
167
- ###########################################################
168
- # Handle byte-aligned 8, 16, 32, and 64 bit :INT
169
- ###########################################################
170
-
171
- case bit_size
172
- when 8
173
- return buffer[lower_bound].unpack(PACK_8_BIT_INT)[0]
174
- when 16
175
- if endianness == HOST_ENDIANNESS
176
- return buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_16_BIT_INT)[0]
177
- else # endianness != HOST_ENDIANNESS
178
- temp = buffer[lower_bound..upper_bound].reverse
179
- return temp.unpack(PACK_NATIVE_16_BIT_INT)[0]
180
- end
181
- when 32
182
- if endianness == HOST_ENDIANNESS
183
- return buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_32_BIT_INT)[0]
184
- else # endianness != HOST_ENDIANNESS
185
- temp = buffer[lower_bound..upper_bound].reverse
186
- return temp.unpack(PACK_NATIVE_32_BIT_INT)[0]
187
- end
188
- when 64
189
- if endianness == HOST_ENDIANNESS
190
- return buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_64_BIT_INT)[0]
191
- else # endianness != HOST_ENDIANNESS
192
- temp = buffer[lower_bound..upper_bound].reverse
193
- return temp.unpack(PACK_NATIVE_64_BIT_INT)[0]
194
- end
195
- end
196
- else # data_type == :UINT
197
- ###########################################################
198
- # Handle byte-aligned 8, 16, 32, and 64 bit :UINT
199
- ###########################################################
200
-
201
- case bit_size
202
- when 8
203
- return buffer.getbyte(lower_bound)
204
- when 16
205
- if endianness == :BIG_ENDIAN
206
- return buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_16_BIT_UINT)[0]
207
- else # endianness == :LITTLE_ENDIAN
208
- return buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_16_BIT_UINT)[0]
209
- end
210
- when 32
211
- if endianness == :BIG_ENDIAN
212
- return buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_32_BIT_UINT)[0]
213
- else # endianness == :LITTLE_ENDIAN
214
- return buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_32_BIT_UINT)[0]
215
- end
216
- when 64
217
- if endianness == HOST_ENDIANNESS
218
- return buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_64_BIT_UINT)[0]
219
- else # endianness != HOST_ENDIANNESS
220
- temp = buffer[lower_bound..upper_bound].reverse
221
- return temp.unpack(PACK_NATIVE_64_BIT_UINT)[0]
222
- end
223
- end
224
- end
225
-
226
- else
227
- ##########################
228
- # Handle :INT and :UINT Bitfields
229
- ##########################
230
-
231
- # Extract Data for Bitfield
232
- if endianness == :LITTLE_ENDIAN
233
- # Bitoffset always refers to the most significant bit of a bitfield
234
- num_bytes = (((bit_offset % 8) + bit_size - 1) / 8) + 1
235
- upper_bound = bit_offset / 8
236
- lower_bound = upper_bound - num_bytes + 1
237
-
238
- if lower_bound < 0
239
- raise(ArgumentError, "LITTLE_ENDIAN bitfield with bit_offset #{given_bit_offset} and bit_size #{given_bit_size} is invalid")
240
- end
241
-
242
- temp_data = buffer[lower_bound..upper_bound].reverse
243
- else
244
- temp_data = buffer[lower_bound..upper_bound]
245
- end
246
-
247
- # Determine temp upper bound
248
- temp_upper = upper_bound - lower_bound
249
-
250
- # Handle Bitfield
251
- start_bits = bit_offset % 8
252
- start_mask = ~(0xFF << (8 - start_bits))
253
- total_bits = (temp_upper + 1) * 8
254
- right_shift = total_bits - start_bits - bit_size
255
-
256
- # Mask off unwanted bits at beginning
257
- temp = temp_data.getbyte(0) & start_mask
258
-
259
- if upper_bound > lower_bound
260
- # Combine bytes into a FixNum
261
- temp_data[1..temp_upper].each_byte { |temp_value| temp = temp << 8; temp = temp + temp_value }
262
- end
263
-
264
- # Shift off unwanted bits at end
265
- temp = temp >> right_shift
266
-
267
- if data_type == :INT
268
- # Convert to negative if necessary
269
- if (bit_size > 1) && (temp[bit_size - 1] == 1)
270
- temp = -((1 << bit_size) - temp)
271
- end
272
- end
273
-
274
- return temp
275
- end
276
-
277
- elsif data_type == :FLOAT
278
- ##########################
279
- # Handle :FLOAT data type
280
- ##########################
281
-
282
- if byte_aligned(bit_offset)
283
- case bit_size
284
- when 32
285
- if endianness == :BIG_ENDIAN
286
- return buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_32_BIT_FLOAT)[0]
287
- else # endianness == :LITTLE_ENDIAN
288
- return buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_32_BIT_FLOAT)[0]
289
- end
290
- when 64
291
- if endianness == :BIG_ENDIAN
292
- return buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_64_BIT_FLOAT)[0]
293
- else # endianness == :LITTLE_ENDIAN
294
- return buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_64_BIT_FLOAT)[0]
295
- end
296
- else
297
- raise(ArgumentError, "bit_size is #{given_bit_size} but must be 32 or 64 for data_type #{data_type}")
298
- end
299
- else
300
- raise(ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}")
301
- end
302
-
303
- else
304
- ############################
305
- # Handle Unknown data types
306
- ############################
307
-
308
- raise(ArgumentError, "data_type #{data_type} is not recognized")
309
- end
310
-
311
- return return_value
312
- end
313
-
314
- # Writes binary data of any data type to a buffer
315
- #
316
- # @param value [Varies] Value to write into the buffer
317
- # @param bit_offset [Integer] Bit offset to the start of the item. A
318
- # negative number means to offset from the end of the buffer.
319
- # @param bit_size [Integer] Size of the item in bits
320
- # @param data_type [Symbol] {DATA_TYPES}
321
- # @param buffer [String] Binary string buffer to write to
322
- # @param endianness [Symbol] {ENDIANNESS}
323
- # @param overflow [Symbol] {OVERFLOW_TYPES}
324
- # @return [Integer] value passed in as a parameter
325
- def self.write(value, bit_offset, bit_size, data_type, buffer, endianness, overflow)
326
- given_bit_offset = bit_offset
327
- given_bit_size = bit_size
328
-
329
- bit_offset = check_bit_offset_and_size(:write, given_bit_offset, given_bit_size, data_type, buffer)
330
-
331
- # If passed a negative bit size with strings or blocks
332
- # recalculate based on the value length in bytes
333
- if (bit_size <= 0) && ((data_type == :STRING) || (data_type == :BLOCK))
334
- value = value.to_s
335
- bit_size = value.length * 8
336
- end
337
-
338
- result, lower_bound, upper_bound = check_bounds_and_buffer_size(bit_offset, bit_size, buffer.length, endianness, data_type)
339
- raise_buffer_error(:write, buffer, data_type, given_bit_offset, given_bit_size) if !result && (given_bit_size > 0)
340
-
341
- # Check overflow type
342
- if (overflow != :TRUNCATE) && (overflow != :SATURATE) && (overflow != :ERROR) && (overflow != :ERROR_ALLOW_HEX)
343
- raise(ArgumentError, "unknown overflow type #{overflow}")
344
- end
345
-
346
- if (data_type == :STRING) || (data_type == :BLOCK)
347
- #######################################
348
- # Handle :STRING and :BLOCK data types
349
- #######################################
350
- value = value.to_s
351
-
352
- if byte_aligned(bit_offset)
353
- temp = value
354
- if given_bit_size <= 0
355
- end_bytes = -(given_bit_size / 8)
356
- old_upper_bound = buffer.length - 1 - end_bytes
357
- # Lower bound + end_bytes can never be more than 1 byte outside of the given buffer
358
- if (lower_bound + end_bytes) > buffer.length
359
- raise_buffer_error(:write, buffer, data_type, given_bit_offset, given_bit_size)
360
- end
361
-
362
- if old_upper_bound < lower_bound
363
- # String was completely empty
364
- if end_bytes > 0
365
- # Preserve bytes at end of buffer
366
- buffer << "\000" * value.length
367
- buffer[lower_bound + value.length, end_bytes] = buffer[lower_bound, end_bytes]
368
- end
369
- elsif bit_size == 0
370
- # Remove entire string
371
- buffer[lower_bound, old_upper_bound - lower_bound + 1] = ''
372
- elsif upper_bound < old_upper_bound
373
- # Remove extra bytes from old string
374
- buffer[upper_bound + 1, old_upper_bound - upper_bound] = ''
375
- elsif (upper_bound > old_upper_bound) && (end_bytes > 0)
376
- # Preserve bytes at end of buffer
377
- diff = upper_bound - old_upper_bound
378
- buffer << "\000" * diff
379
- buffer[upper_bound + 1, end_bytes] = buffer[old_upper_bound + 1, end_bytes]
380
- end
381
- else # given_bit_size > 0
382
- byte_size = bit_size / 8
383
- if value.length < byte_size
384
- # Pad the requested size with zeros
385
- temp = value.ljust(byte_size, "\000")
386
- elsif value.length > byte_size
387
- if overflow == :TRUNCATE
388
- # Resize the value to fit the field
389
- value[byte_size, value.length - byte_size] = ''
390
- else
391
- raise(ArgumentError, "value of #{value.length} bytes does not fit into #{byte_size} bytes for data_type #{data_type}")
392
- end
393
- end
394
- end
395
- if bit_size != 0
396
- buffer[lower_bound, temp.length] = temp
397
- end
398
- else
399
- raise(ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}")
400
- end
401
-
402
- elsif (data_type == :INT) || (data_type == :UINT)
403
- ###################################
404
- # Handle :INT data type
405
- ###################################
406
- value = Integer(value)
407
- min_value, max_value, hex_max_value = get_check_overflow_ranges(bit_size, data_type)
408
- value = check_overflow(value, min_value, max_value, hex_max_value, bit_size, data_type, overflow)
409
-
410
- if byte_aligned(bit_offset) && even_bit_size(bit_size)
411
- ###########################################################
412
- # Handle byte-aligned 8, 16, 32, and 64 bit
413
- ###########################################################
414
-
415
- if data_type == :INT
416
- ###########################################################
417
- # Handle byte-aligned 8, 16, 32, and 64 bit :INT
418
- ###########################################################
419
-
420
- case bit_size
421
- when 8
422
- buffer.setbyte(lower_bound, value)
423
- when 16
424
- if endianness == HOST_ENDIANNESS
425
- buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_16_BIT_INT)
426
- else # endianness != HOST_ENDIANNESS
427
- buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_16_BIT_INT).reverse
428
- end
429
- when 32
430
- if endianness == HOST_ENDIANNESS
431
- buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_32_BIT_INT)
432
- else # endianness != HOST_ENDIANNESS
433
- buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_32_BIT_INT).reverse
434
- end
435
- when 64
436
- if endianness == HOST_ENDIANNESS
437
- buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_64_BIT_INT)
438
- else # endianness != HOST_ENDIANNESS
439
- buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_64_BIT_INT).reverse
440
- end
441
- end
442
- else # data_type == :UINT
443
- ###########################################################
444
- # Handle byte-aligned 8, 16, 32, and 64 bit :UINT
445
- ###########################################################
446
-
447
- case bit_size
448
- when 8
449
- buffer.setbyte(lower_bound, value)
450
- when 16
451
- if endianness == :BIG_ENDIAN
452
- buffer[lower_bound..upper_bound] = [value].pack(PACK_BIG_ENDIAN_16_BIT_UINT)
453
- else # endianness == :LITTLE_ENDIAN
454
- buffer[lower_bound..upper_bound] = [value].pack(PACK_LITTLE_ENDIAN_16_BIT_UINT)
455
- end
456
- when 32
457
- if endianness == :BIG_ENDIAN
458
- buffer[lower_bound..upper_bound] = [value].pack(PACK_BIG_ENDIAN_32_BIT_UINT)
459
- else # endianness == :LITTLE_ENDIAN
460
- buffer[lower_bound..upper_bound] = [value].pack(PACK_LITTLE_ENDIAN_32_BIT_UINT)
461
- end
462
- when 64
463
- if endianness == HOST_ENDIANNESS
464
- buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_64_BIT_UINT)
465
- else # endianness != HOST_ENDIANNESS
466
- buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_64_BIT_UINT).reverse
467
- end
468
- end
469
- end
470
-
471
- else
472
- ###########################################################
473
- # Handle bit fields
474
- ###########################################################
475
-
476
- # Extract Existing Data
477
- if endianness == :LITTLE_ENDIAN
478
- # Bitoffset always refers to the most significant bit of a bitfield
479
- num_bytes = (((bit_offset % 8) + bit_size - 1) / 8) + 1
480
- upper_bound = bit_offset / 8
481
- lower_bound = upper_bound - num_bytes + 1
482
- if lower_bound < 0
483
- raise(ArgumentError, "LITTLE_ENDIAN bitfield with bit_offset #{given_bit_offset} and bit_size #{given_bit_size} is invalid")
484
- end
485
-
486
- temp_data = buffer[lower_bound..upper_bound].reverse
487
- else
488
- temp_data = buffer[lower_bound..upper_bound]
489
- end
490
-
491
- # Determine temp upper bound
492
- temp_upper = upper_bound - lower_bound
493
-
494
- # Determine Values needed to Handle Bitfield
495
- start_bits = bit_offset % 8
496
- start_mask = (0xFF << (8 - start_bits))
497
- total_bits = (temp_upper + 1) * 8
498
- end_bits = total_bits - start_bits - bit_size
499
- end_mask = ~(0xFF << end_bits)
500
-
501
- # Add in Start Bits
502
- temp = temp_data.getbyte(0) & start_mask
503
-
504
- # Adjust value to correct number of bits
505
- temp_mask = (2**bit_size) - 1
506
- temp_value = value & temp_mask
507
-
508
- # Add in New Data
509
- temp = (temp << (bit_size - (8 - start_bits))) + temp_value
510
-
511
- # Add in Remainder of Existing Data
512
- temp = (temp << end_bits) + (temp_data.getbyte(temp_upper) & end_mask)
513
-
514
- # Extract into an array of bytes
515
- temp_array = []
516
- (0..temp_upper).each { temp_array.insert(0, (temp & 0xFF)); temp = temp >> 8 }
517
-
518
- # Store into data
519
- if endianness == :LITTLE_ENDIAN
520
- buffer[lower_bound..upper_bound] = temp_array.pack(PACK_8_BIT_UINT_ARRAY).reverse
521
- else
522
- buffer[lower_bound..upper_bound] = temp_array.pack(PACK_8_BIT_UINT_ARRAY)
523
- end
524
-
525
- end
526
-
527
- elsif data_type == :FLOAT
528
- ##########################
529
- # Handle :FLOAT data type
530
- ##########################
531
- value = Float(value)
532
-
533
- if byte_aligned(bit_offset)
534
- case bit_size
535
- when 32
536
- if endianness == :BIG_ENDIAN
537
- buffer[lower_bound..upper_bound] = [value].pack(PACK_BIG_ENDIAN_32_BIT_FLOAT)
538
- else # endianness == :LITTLE_ENDIAN
539
- buffer[lower_bound..upper_bound] = [value].pack(PACK_LITTLE_ENDIAN_32_BIT_FLOAT)
540
- end
541
- when 64
542
- if endianness == :BIG_ENDIAN
543
- buffer[lower_bound..upper_bound] = [value].pack(PACK_BIG_ENDIAN_64_BIT_FLOAT)
544
- else # endianness == :LITTLE_ENDIAN
545
- buffer[lower_bound..upper_bound] = [value].pack(PACK_LITTLE_ENDIAN_64_BIT_FLOAT)
546
- end
547
- else
548
- raise(ArgumentError, "bit_size is #{given_bit_size} but must be 32 or 64 for data_type #{data_type}")
549
- end
550
- else
551
- raise(ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}")
552
- end
553
-
554
- else
555
- ############################
556
- # Handle Unknown data types
557
- ############################
558
-
559
- raise(ArgumentError, "data_type #{data_type} is not recognized")
560
- end
561
-
562
- return value
563
- end
564
-
565
- protected
566
-
567
- # Check the bit size and bit offset for problems. Recalulate the bit offset
568
- # and return back through the passed in pointer.
569
- def self.check_bit_offset_and_size(read_or_write, given_bit_offset, given_bit_size, data_type, buffer)
570
- bit_offset = given_bit_offset
571
-
572
- if (given_bit_size <= 0) && (data_type != :STRING) && (data_type != :BLOCK)
573
- raise(ArgumentError, "bit_size #{given_bit_size} must be positive for data types other than :STRING and :BLOCK")
574
- end
575
-
576
- if (given_bit_size <= 0) && (given_bit_offset < 0)
577
- raise(ArgumentError, "negative or zero bit_sizes (#{given_bit_size}) cannot be given with negative bit_offsets (#{given_bit_offset})")
578
- end
579
-
580
- if given_bit_offset < 0
581
- bit_offset = (buffer.length * 8) + bit_offset
582
- if bit_offset < 0
583
- raise_buffer_error(read_or_write, buffer, data_type, given_bit_offset, given_bit_size)
584
- end
585
- end
586
-
587
- return bit_offset
588
- end
589
-
590
- # Calculate the bounds of the string to access the item based on the bit_offset and bit_size.
591
- # Also determine if the buffer size is sufficient.
592
- def self.check_bounds_and_buffer_size(bit_offset, bit_size, buffer_length, endianness, data_type)
593
- result = true # Assume ok
594
-
595
- # Define bounds of string to access this item
596
- lower_bound = bit_offset / 8
597
- upper_bound = (bit_offset + bit_size - 1) / 8
598
-
599
- # Sanity check buffer size
600
- if upper_bound >= buffer_length
601
- # If it's not the special case of little endian bit field then we fail and return false
602
- if !((endianness == :LITTLE_ENDIAN) &&
603
- ((data_type == :INT) || (data_type == :UINT)) &&
604
- # Not byte aligned with an even bit size
605
- (!((byte_aligned(bit_offset)) && (even_bit_size(bit_size)))) &&
606
- (lower_bound < buffer_length)
607
- )
608
- result = false
609
- end
610
- end
611
- return result, lower_bound, upper_bound
612
- end
613
-
614
- def self.get_check_overflow_ranges(bit_size, data_type)
615
- min_value = 0 # Default for UINT cases
616
-
617
- case bit_size
618
- when 8
619
- hex_max_value = MAX_UINT8
620
- if data_type == :INT
621
- min_value = MIN_INT8
622
- max_value = MAX_INT8
623
- else
624
- max_value = MAX_UINT8
625
- end
626
- when 16
627
- hex_max_value = MAX_UINT16
628
- if data_type == :INT
629
- min_value = MIN_INT16
630
- max_value = MAX_INT16
631
- else
632
- max_value = MAX_UINT16
633
- end
634
- when 32
635
- hex_max_value = MAX_UINT32
636
- if data_type == :INT
637
- min_value = MIN_INT32
638
- max_value = MAX_INT32
639
- else
640
- max_value = MAX_UINT32
641
- end
642
- when 64
643
- hex_max_value = MAX_UINT64
644
- if data_type == :INT
645
- min_value = MIN_INT64
646
- max_value = MAX_INT64
647
- else
648
- max_value = MAX_UINT64
649
- end
650
- else # Bitfield
651
- if data_type == :INT
652
- # Note signed integers must allow up to the maximum unsigned value to support values given in hex
653
- if bit_size > 1
654
- max_value = 2**(bit_size - 1)
655
- # min_value = -(2 ** bit_size - 1)
656
- min_value = -max_value
657
- # max_value = (2 ** bit_size - 1) - 1
658
- max_value -= 1
659
- # hex_max_value = (2 ** bit_size) - 1
660
- hex_max_value = (2**bit_size) - 1
661
- else # 1-bit signed
662
- min_value = -1
663
- max_value = 1
664
- hex_max_value = 1
665
- end
666
- else
667
- max_value = (2**bit_size) - 1
668
- hex_max_value = max_value
669
- end
670
- end
671
-
672
- return min_value, max_value, hex_max_value
673
- end
674
-
675
- def self.byte_aligned(value)
676
- (value % 8) == 0
677
- end
678
-
679
- def self.even_bit_size(bit_size)
680
- (bit_size == 8) || (bit_size == 16) || (bit_size == 32) || (bit_size == 64)
681
- end
682
-
683
- public
684
-
685
- end
686
-
687
- # Reads an array of binary data of any data type from a buffer
688
- #
689
- # @param bit_offset [Integer] Bit offset to the start of the array. A
690
- # negative number means to offset from the end of the buffer.
691
- # @param bit_size [Integer] Size of each item in the array in bits
692
- # @param data_type [Symbol] {DATA_TYPES}
693
- # @param array_size [Integer] Size in bits of the array. 0 or negative means
694
- # fill the array with as many bit_size number of items that exist (negative
695
- # means excluding the final X number of bits).
696
- # @param buffer [String] Binary string buffer to read from
697
- # @param endianness [Symbol] {ENDIANNESS}
698
- # @return [Array] Array created from reading the buffer
699
- def self.read_array(bit_offset, bit_size, data_type, array_size, buffer, endianness)
700
- # Save given values of bit offset, bit size, and array_size
701
- given_bit_offset = bit_offset
702
- given_bit_size = bit_size
703
- given_array_size = array_size
704
-
705
- # Handle negative and zero bit sizes
706
- raise ArgumentError, "bit_size #{given_bit_size} must be positive for arrays" if bit_size <= 0
707
-
708
- # Handle negative bit offsets
709
- if bit_offset < 0
710
- bit_offset = ((buffer.length * 8) + bit_offset)
711
- raise_buffer_error(:read, buffer, data_type, given_bit_offset, given_bit_size) if bit_offset < 0
712
- end
713
-
714
- # Handle negative and zero array sizes
715
- if array_size <= 0
716
- if given_bit_offset < 0
717
- raise ArgumentError, "negative or zero array_size (#{given_array_size}) cannot be given with negative bit_offset (#{given_bit_offset})"
718
- else
719
- array_size = ((buffer.length * 8) - bit_offset + array_size)
720
- if array_size == 0
721
- return []
722
- elsif array_size < 0
723
- raise_buffer_error(:read, buffer, data_type, given_bit_offset, given_bit_size)
724
- end
725
- end
726
- end
727
-
728
- # Calculate number of items in the array
729
- # If there is a remainder then we have a problem
730
- raise ArgumentError, "array_size #{given_array_size} not a multiple of bit_size #{given_bit_size}" if array_size % bit_size != 0
731
-
732
- num_items = array_size / bit_size
733
-
734
- # Define bounds of string to access this item
735
- lower_bound = bit_offset / 8
736
- upper_bound = (bit_offset + array_size - 1) / 8
737
-
738
- # Check for byte alignment
739
- byte_aligned = ((bit_offset % 8) == 0)
740
-
741
- case data_type
742
- when :STRING, :BLOCK
743
- #######################################
744
- # Handle :STRING and :BLOCK data types
745
- #######################################
746
-
747
- if byte_aligned
748
- value = []
749
- num_items.times do
750
- value << self.read(bit_offset, bit_size, data_type, buffer, endianness)
751
- bit_offset += bit_size
752
- end
753
- else
754
- raise ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}"
755
- end
756
-
757
- when :INT, :UINT
758
- ###################################
759
- # Handle :INT and :UINT data types
760
- ###################################
761
-
762
- if byte_aligned and (bit_size == 8 or bit_size == 16 or bit_size == 32 or bit_size == 64)
763
- ###########################################################
764
- # Handle byte-aligned 8, 16, 32, and 64 bit :INT and :UINT
765
- ###########################################################
766
-
767
- case bit_size
768
- when 8
769
- if data_type == :INT
770
- value = buffer[lower_bound..upper_bound].unpack(PACK_8_BIT_INT_ARRAY)
771
- else # data_type == :UINT
772
- value = buffer[lower_bound..upper_bound].unpack(PACK_8_BIT_UINT_ARRAY)
773
- end
774
-
775
- when 16
776
- if data_type == :INT
777
- if endianness == HOST_ENDIANNESS
778
- value = buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_16_BIT_INT_ARRAY)
779
- else # endianness != HOST_ENDIANNESS
780
- temp = self.byte_swap_buffer(buffer[lower_bound..upper_bound], 2)
781
- value = temp.to_s.unpack(PACK_NATIVE_16_BIT_INT_ARRAY)
782
- end
783
- else # data_type == :UINT
784
- if endianness == :BIG_ENDIAN
785
- value = buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_16_BIT_UINT_ARRAY)
786
- else # endianness == :LITTLE_ENDIAN
787
- value = buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_16_BIT_UINT_ARRAY)
788
- end
789
- end
790
-
791
- when 32
792
- if data_type == :INT
793
- if endianness == HOST_ENDIANNESS
794
- value = buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_32_BIT_INT_ARRAY)
795
- else # endianness != HOST_ENDIANNESS
796
- temp = self.byte_swap_buffer(buffer[lower_bound..upper_bound], 4)
797
- value = temp.to_s.unpack(PACK_NATIVE_32_BIT_INT_ARRAY)
798
- end
799
- else # data_type == :UINT
800
- if endianness == :BIG_ENDIAN
801
- value = buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_32_BIT_UINT_ARRAY)
802
- else # endianness == :LITTLE_ENDIAN
803
- value = buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_32_BIT_UINT_ARRAY)
804
- end
805
- end
806
-
807
- when 64
808
- if data_type == :INT
809
- if endianness == HOST_ENDIANNESS
810
- value = buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_64_BIT_INT_ARRAY)
811
- else # endianness != HOST_ENDIANNESS
812
- temp = self.byte_swap_buffer(buffer[lower_bound..upper_bound], 8)
813
- value = temp.to_s.unpack(PACK_NATIVE_64_BIT_INT_ARRAY)
814
- end
815
- else # data_type == :UINT
816
- if endianness == HOST_ENDIANNESS
817
- value = buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_64_BIT_UINT_ARRAY)
818
- else # endianness != HOST_ENDIANNESS
819
- temp = self.byte_swap_buffer(buffer[lower_bound..upper_bound], 8)
820
- value = temp.to_s.unpack(PACK_NATIVE_64_BIT_UINT_ARRAY)
821
- end
822
- end
823
- end
824
-
825
- else
826
- ##################################
827
- # Handle :INT and :UINT Bitfields
828
- ##################################
829
- raise ArgumentError, "read_array does not support little endian bit fields with bit_size greater than 1-bit" if endianness == :LITTLE_ENDIAN and bit_size > 1
830
-
831
- value = []
832
- num_items.times do
833
- value << self.read(bit_offset, bit_size, data_type, buffer, endianness)
834
- bit_offset += bit_size
835
- end
836
- end
837
-
838
- when :FLOAT
839
- ##########################
840
- # Handle :FLOAT data type
841
- ##########################
842
-
843
- if byte_aligned
844
- case bit_size
845
- when 32
846
- if endianness == :BIG_ENDIAN
847
- value = buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_32_BIT_FLOAT_ARRAY)
848
- else # endianness == :LITTLE_ENDIAN
849
- value = buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_32_BIT_FLOAT_ARRAY)
850
- end
851
-
852
- when 64
853
- if endianness == :BIG_ENDIAN
854
- value = buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_64_BIT_FLOAT_ARRAY)
855
- else # endianness == :LITTLE_ENDIAN
856
- value = buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_64_BIT_FLOAT_ARRAY)
857
- end
858
-
859
- else
860
- raise ArgumentError, "bit_size is #{given_bit_size} but must be 32 or 64 for data_type #{data_type}"
861
- end
862
-
863
- else
864
- raise ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}"
865
- end
866
-
867
- else
868
- ############################
869
- # Handle Unknown data types
870
- ############################
871
-
872
- raise ArgumentError, "data_type #{data_type} is not recognized"
873
- end
874
-
875
- value
876
- end # def read_array
877
-
878
- # Writes an array of binary data of any data type to a buffer
879
- #
880
- # @param values [Array] Values to write into the buffer
881
- # @param bit_offset [Integer] Bit offset to the start of the array. A
882
- # negative number means to offset from the end of the buffer.
883
- # @param bit_size [Integer] Size of each item in the array in bits
884
- # @param data_type [Symbol] {DATA_TYPES}
885
- # @param array_size [Integer] Size in bits of the array as represented in the buffer.
886
- # Size 0 means to fill the buffer with as many bit_size number of items that exist
887
- # (negative means excluding the final X number of bits).
888
- # @param buffer [String] Binary string buffer to write to
889
- # @param endianness [Symbol] {ENDIANNESS}
890
- # @return [Array] values passed in as a parameter
891
- def self.write_array(values, bit_offset, bit_size, data_type, array_size, buffer, endianness, overflow)
892
- # Save given values of bit offset, bit size, and array_size
893
- given_bit_offset = bit_offset
894
- given_bit_size = bit_size
895
- given_array_size = array_size
896
-
897
- # Verify an array was given
898
- raise ArgumentError, "values must be an Array type class is #{values.class}" unless values.kind_of? Array
899
-
900
- # Handle negative and zero bit sizes
901
- raise ArgumentError, "bit_size #{given_bit_size} must be positive for arrays" if bit_size <= 0
902
-
903
- # Handle negative bit offsets
904
- if bit_offset < 0
905
- bit_offset = ((buffer.length * 8) + bit_offset)
906
- raise_buffer_error(:write, buffer, data_type, given_bit_offset, given_bit_size) if bit_offset < 0
907
- end
908
-
909
- # Handle negative and zero array sizes
910
- if array_size <= 0
911
- if given_bit_offset < 0
912
- raise ArgumentError, "negative or zero array_size (#{given_array_size}) cannot be given with negative bit_offset (#{given_bit_offset})"
913
- else
914
- end_bytes = -(given_array_size / 8)
915
- lower_bound = bit_offset / 8
916
- upper_bound = (bit_offset + (bit_size * values.length) - 1) / 8
917
- old_upper_bound = buffer.length - 1 - end_bytes
918
-
919
- if upper_bound < old_upper_bound
920
- # Remove extra bytes from old buffer
921
- buffer[(upper_bound + 1)..old_upper_bound] = ''
922
- elsif upper_bound > old_upper_bound
923
- # Grow buffer and preserve bytes at end of buffer if necesssary
924
- buffer_length = buffer.length
925
- diff = upper_bound - old_upper_bound
926
- buffer << ZERO_STRING * diff
927
- if end_bytes > 0
928
- buffer[(upper_bound + 1)..(buffer.length - 1)] = buffer[(old_upper_bound + 1)..(buffer_length - 1)]
929
- end
930
- end
931
-
932
- array_size = ((buffer.length * 8) - bit_offset + array_size)
933
- end
934
- end
935
-
936
- # Get data bounds for this array
937
- lower_bound = bit_offset / 8
938
- upper_bound = (bit_offset + array_size - 1) / 8
939
- num_bytes = upper_bound - lower_bound + 1
940
-
941
- # Check for byte alignment
942
- byte_aligned = ((bit_offset % 8) == 0)
943
-
944
- # Calculate the number of writes
945
- num_writes = array_size / bit_size
946
- # Check for a negative array_size and adjust the number of writes
947
- # to simply be the number of values in the passed in array
948
- if given_array_size <= 0
949
- num_writes = values.length
950
- end
951
-
952
- # Ensure the buffer has enough room
953
- if bit_offset + num_writes * bit_size > buffer.length * 8
954
- raise_buffer_error(:write, buffer, data_type, given_bit_offset, given_bit_size)
955
- end
956
-
957
- # Ensure the given_array_size is an even multiple of bit_size
958
- raise ArgumentError, "array_size #{given_array_size} not a multiple of bit_size #{given_bit_size}" if array_size % bit_size != 0
959
-
960
- raise ArgumentError, "too many values #{values.length} for given array_size #{given_array_size} and bit_size #{given_bit_size}" if num_writes < values.length
961
-
962
- # Check overflow type
963
- raise "unknown overflow type #{overflow}" unless OVERFLOW_TYPES.include?(overflow)
964
-
965
- case data_type
966
- when :STRING, :BLOCK
967
- #######################################
968
- # Handle :STRING and :BLOCK data types
969
- #######################################
970
-
971
- if byte_aligned
972
- num_writes.times do |index|
973
- self.write(values[index], bit_offset, bit_size, data_type, buffer, endianness, overflow)
974
- bit_offset += bit_size
975
- end
976
- else
977
- raise ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}"
978
- end
979
-
980
- when :INT, :UINT
981
- ###################################
982
- # Handle :INT and :UINT data types
983
- ###################################
984
-
985
- if byte_aligned and (bit_size == 8 or bit_size == 16 or bit_size == 32 or bit_size == 64)
986
- ###########################################################
987
- # Handle byte-aligned 8, 16, 32, and 64 bit :INT and :UINT
988
- ###########################################################
989
-
990
- case bit_size
991
- when 8
992
- if data_type == :INT
993
- values = self.check_overflow_array(values, MIN_INT8, MAX_INT8, MAX_UINT8, bit_size, data_type, overflow)
994
- packed = values.pack(PACK_8_BIT_INT_ARRAY)
995
- else # data_type == :UINT
996
- values = self.check_overflow_array(values, 0, MAX_UINT8, MAX_UINT8, bit_size, data_type, overflow)
997
- packed = values.pack(PACK_8_BIT_UINT_ARRAY)
998
- end
999
-
1000
- when 16
1001
- if data_type == :INT
1002
- values = self.check_overflow_array(values, MIN_INT16, MAX_INT16, MAX_UINT16, bit_size, data_type, overflow)
1003
- if endianness == HOST_ENDIANNESS
1004
- packed = values.pack(PACK_NATIVE_16_BIT_INT_ARRAY)
1005
- else # endianness != HOST_ENDIANNESS
1006
- packed = values.pack(PACK_NATIVE_16_BIT_INT_ARRAY)
1007
- self.byte_swap_buffer!(packed, 2)
1008
- end
1009
- else # data_type == :UINT
1010
- values = self.check_overflow_array(values, 0, MAX_UINT16, MAX_UINT16, bit_size, data_type, overflow)
1011
- if endianness == :BIG_ENDIAN
1012
- packed = values.pack(PACK_BIG_ENDIAN_16_BIT_UINT_ARRAY)
1013
- else # endianness == :LITTLE_ENDIAN
1014
- packed = values.pack(PACK_LITTLE_ENDIAN_16_BIT_UINT_ARRAY)
1015
- end
1016
- end
1017
-
1018
- when 32
1019
- if data_type == :INT
1020
- values = self.check_overflow_array(values, MIN_INT32, MAX_INT32, MAX_UINT32, bit_size, data_type, overflow)
1021
- if endianness == HOST_ENDIANNESS
1022
- packed = values.pack(PACK_NATIVE_32_BIT_INT_ARRAY)
1023
- else # endianness != HOST_ENDIANNESS
1024
- packed = values.pack(PACK_NATIVE_32_BIT_INT_ARRAY)
1025
- self.byte_swap_buffer!(packed, 4)
1026
- end
1027
- else # data_type == :UINT
1028
- values = self.check_overflow_array(values, 0, MAX_UINT32, MAX_UINT32, bit_size, data_type, overflow)
1029
- if endianness == :BIG_ENDIAN
1030
- packed = values.pack(PACK_BIG_ENDIAN_32_BIT_UINT_ARRAY)
1031
- else # endianness == :LITTLE_ENDIAN
1032
- packed = values.pack(PACK_LITTLE_ENDIAN_32_BIT_UINT_ARRAY)
1033
- end
1034
- end
1035
-
1036
- when 64
1037
- if data_type == :INT
1038
- values = self.check_overflow_array(values, MIN_INT64, MAX_INT64, MAX_UINT64, bit_size, data_type, overflow)
1039
- if endianness == HOST_ENDIANNESS
1040
- packed = values.pack(PACK_NATIVE_64_BIT_INT_ARRAY)
1041
- else # endianness != HOST_ENDIANNESS
1042
- packed = values.pack(PACK_NATIVE_64_BIT_INT_ARRAY)
1043
- self.byte_swap_buffer!(packed, 8)
1044
- end
1045
- else # data_type == :UINT
1046
- values = self.check_overflow_array(values, 0, MAX_UINT64, MAX_UINT64, bit_size, data_type, overflow)
1047
- if endianness == HOST_ENDIANNESS
1048
- packed = values.pack(PACK_NATIVE_64_BIT_UINT_ARRAY)
1049
- else # endianness != HOST_ENDIANNESS
1050
- packed = values.pack(PACK_NATIVE_64_BIT_UINT_ARRAY)
1051
- self.byte_swap_buffer!(packed, 8)
1052
- end
1053
- end
1054
- end
1055
-
1056
- # Adjust packed size to hold number of items written
1057
- buffer[lower_bound..upper_bound] = adjust_packed_size(num_bytes, packed) if num_bytes > 0
1058
-
1059
- else
1060
- ##################################
1061
- # Handle :INT and :UINT Bitfields
1062
- ##################################
1063
-
1064
- raise ArgumentError, "write_array does not support little endian bit fields with bit_size greater than 1-bit" if endianness == :LITTLE_ENDIAN and bit_size > 1
1065
-
1066
- num_writes.times do |index|
1067
- self.write(values[index], bit_offset, bit_size, data_type, buffer, endianness, overflow)
1068
- bit_offset += bit_size
1069
- end
1070
- end
1071
-
1072
- when :FLOAT
1073
- ##########################
1074
- # Handle :FLOAT data type
1075
- ##########################
1076
-
1077
- if byte_aligned
1078
- case bit_size
1079
- when 32
1080
- if endianness == :BIG_ENDIAN
1081
- packed = values.pack(PACK_BIG_ENDIAN_32_BIT_FLOAT_ARRAY)
1082
- else # endianness == :LITTLE_ENDIAN
1083
- packed = values.pack(PACK_LITTLE_ENDIAN_32_BIT_FLOAT_ARRAY)
1084
- end
1085
-
1086
- when 64
1087
- if endianness == :BIG_ENDIAN
1088
- packed = values.pack(PACK_BIG_ENDIAN_64_BIT_FLOAT_ARRAY)
1089
- else # endianness == :LITTLE_ENDIAN
1090
- packed = values.pack(PACK_LITTLE_ENDIAN_64_BIT_FLOAT_ARRAY)
1091
- end
1092
-
1093
- else
1094
- raise ArgumentError, "bit_size is #{given_bit_size} but must be 32 or 64 for data_type #{data_type}"
1095
- end
1096
-
1097
- # Adjust packed size to hold number of items written
1098
- buffer[lower_bound..upper_bound] = adjust_packed_size(num_bytes, packed) if num_bytes > 0
1099
-
1100
- else
1101
- raise ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}"
1102
- end
1103
-
1104
- else
1105
- ############################
1106
- # Handle Unknown data types
1107
- ############################
1108
- raise ArgumentError, "data_type #{data_type} is not recognized"
1109
- end # case data_type
1110
-
1111
- values
1112
- end # def write_array
1113
-
1114
- # Adjusts the packed array to be the given number of bytes
1115
- #
1116
- # @param num_bytes [Integer] The desired number of bytes
1117
- # @param packed [Array] The packed data buffer
1118
- def self.adjust_packed_size(num_bytes, packed)
1119
- difference = num_bytes - packed.length
1120
- if difference > 0
1121
- packed << (ZERO_STRING * difference)
1122
- elsif difference < 0
1123
- packed = packed[0..(packed.length - 1 + difference)]
1124
- end
1125
- packed
1126
- end
1127
-
1128
- # Byte swaps every X bytes of data in a buffer overwriting the buffer
1129
- #
1130
- # @param buffer [String] Buffer to modify
1131
- # @param num_bytes_per_word [Integer] Number of bytes per word that will be swapped
1132
- # @return [String] buffer passed in as a parameter
1133
- def self.byte_swap_buffer!(buffer, num_bytes_per_word)
1134
- num_swaps = buffer.length / num_bytes_per_word
1135
- index = 0
1136
- num_swaps.times do
1137
- range = index..(index + num_bytes_per_word - 1)
1138
- buffer[range] = buffer[range].reverse
1139
- index += num_bytes_per_word
1140
- end
1141
- buffer
1142
- end
1143
-
1144
- # Byte swaps every X bytes of data in a buffer into a new buffer
1145
- #
1146
- # @param buffer [String] Buffer that will be copied then modified
1147
- # @param num_bytes_per_word [Integer] Number of bytes per word that will be swapped
1148
- # @return [String] modified buffer
1149
- def self.byte_swap_buffer(buffer, num_bytes_per_word)
1150
- buffer = buffer.clone
1151
- self.byte_swap_buffer!(buffer, num_bytes_per_word)
1152
- end
1153
-
1154
- # Checks for overflow of an integer data type
1155
- #
1156
- # @param value [Integer] Value to write into the buffer
1157
- # @param min_value [Integer] Minimum allowed value
1158
- # @param max_value [Integer] Maximum allowed value
1159
- # @param hex_max_value [Integer] Maximum allowed value if specified in hex
1160
- # @param bit_size [Integer] Size of the item in bits
1161
- # @param data_type [Symbol] {DATA_TYPES}
1162
- # @param overflow [Symbol] {OVERFLOW_TYPES}
1163
- # @return [Integer] Potentially modified value
1164
- def self.check_overflow(value, min_value, max_value, hex_max_value, bit_size, data_type, overflow)
1165
- if overflow == :TRUNCATE
1166
- # Note this will always convert to unsigned equivalent for signed integers
1167
- value = value % (hex_max_value + 1)
1168
- else
1169
- if value > max_value
1170
- if overflow == :SATURATE
1171
- value = max_value
1172
- else
1173
- if overflow == :ERROR or value > hex_max_value
1174
- raise ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}"
1175
- end
1176
- end
1177
- elsif value < min_value
1178
- if overflow == :SATURATE
1179
- value = min_value
1180
- else
1181
- raise ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}"
1182
- end
1183
- end
1184
- end
1185
- value
1186
- end
1187
-
1188
- # Checks for overflow of an array of integer data types
1189
- #
1190
- # @param values [Array[Integer]] Values to write into the buffer
1191
- # @param min_value [Integer] Minimum allowed value
1192
- # @param max_value [Integer] Maximum allowed value
1193
- # @param hex_max_value [Integer] Maximum allowed value if specified in hex
1194
- # @param bit_size [Integer] Size of the item in bits
1195
- # @param data_type [Symbol] {DATA_TYPES}
1196
- # @param overflow [Symbol] {OVERFLOW_TYPES}
1197
- # @return [Array[Integer]] Potentially modified values
1198
- def self.check_overflow_array(values, min_value, max_value, hex_max_value, bit_size, data_type, overflow)
1199
- if overflow != :TRUNCATE
1200
- values.each_with_index do |value, index|
1201
- values[index] = check_overflow(value, min_value, max_value, hex_max_value, bit_size, data_type, overflow)
1202
- end
1203
- end
1204
- values
1205
- end
1206
- end # class BinaryAccessor
1207
- end
1
+ # This file remains here for backwards compatibility
2
+ require 'openc3/accessors/binary_accessor'