openc3 5.0.9 → 5.0.11

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of openc3 might be problematic. Click here for more details.

Files changed (46) 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/api/interface_api.rb +10 -3
  22. data/lib/openc3/config/config_parser.rb +10 -4
  23. data/lib/openc3/core_ext/tempfile.rb +20 -0
  24. data/lib/openc3/core_ext.rb +1 -0
  25. data/lib/openc3/interfaces/interface.rb +1 -1
  26. data/lib/openc3/models/auth_model.rb +0 -1
  27. data/lib/openc3/models/cvt_model.rb +1 -10
  28. data/lib/openc3/models/microservice_model.rb +26 -0
  29. data/lib/openc3/models/note_model.rb +5 -5
  30. data/lib/openc3/packets/binary_accessor.rb +2 -1207
  31. data/lib/openc3/packets/packet.rb +106 -6
  32. data/lib/openc3/packets/packet_config.rb +30 -7
  33. data/lib/openc3/packets/parsers/limits_response_parser.rb +1 -3
  34. data/lib/openc3/packets/parsers/processor_parser.rb +1 -2
  35. data/lib/openc3/packets/structure.rb +39 -14
  36. data/lib/openc3/packets/structure_item.rb +15 -1
  37. data/lib/openc3/script/storage.rb +2 -0
  38. data/lib/openc3/utilities/authentication.rb +6 -4
  39. data/lib/openc3/utilities/local_mode.rb +10 -1
  40. data/lib/openc3/utilities/s3.rb +3 -1
  41. data/lib/openc3/utilities/s3_autoload.rb +8 -6
  42. data/lib/openc3/utilities/simulated_target.rb +3 -2
  43. data/lib/openc3/utilities/target_file.rb +45 -6
  44. data/lib/openc3/version.rb +5 -5
  45. data/lib/openc3.rb +1 -0
  46. 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'