cosmos 5.0.4 → 5.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a561fd5492c1b282759d45170da4e687b4e53eea086804bde1c549a75a8bdd5
4
- data.tar.gz: c88407b16125e7ef2fb9764dd14338f6508e2fbde1567ff3b7d23b24f1b7dd2a
3
+ metadata.gz: 520339a0daade42a51611f9e17fe6e5a2c1343d9bab3ad6b36dbb03f11f55f28
4
+ data.tar.gz: 59a4d41cc6ef453ea1f375c5e9671aa0b14b390da42d8413bd080ab7bdb18ff4
5
5
  SHA512:
6
- metadata.gz: f80a81b27f8c00611a730ef329b4c4afc01e572419ee03d1c7621295e2670a1e829d41511ee4a82af5874d43d32f7b51280281c26310ee8f1e79e767e987c542
7
- data.tar.gz: 14f36f225ac641ca74dac4eca580ffad8e6ffea7d69bed1c685d387db0ff6d3977c6ad8b148b8a8117914bd7752914423d2691aa899b0ec902fde5c25f4e931a
6
+ metadata.gz: 700274a130767236a08e76ce223a09fa0229e659153c8a5f50896447796d3ca0e4487423704ece2d655511a642c6dfed0a8fc9482f8ee7ef9681f1726c23a2ce
7
+ data.tar.gz: 20773c2fd54d95674f66252edbd51faee8107cca83c05bace69a4607fd5563dc543d10f670513b4fb2df25d6ca6c18d0e58923f517e66f7f186fe7bc0e1eecbb
data/bin/cosmos CHANGED
@@ -24,6 +24,7 @@ require 'cosmos'
24
24
  require 'cosmos/utilities/s3'
25
25
  require 'cosmos/models/scope_model'
26
26
  require 'cosmos/models/plugin_model'
27
+ require 'cosmos/packets/packet_config'
27
28
  require 'cosmos/bridge/bridge'
28
29
  require 'ostruct'
29
30
  require 'optparse'
@@ -32,6 +33,7 @@ require 'fileutils'
32
33
  require 'find'
33
34
  require 'json'
34
35
  require 'redis'
36
+ require 'psych'
35
37
  require 'erb'
36
38
 
37
39
  $redis_url = "redis://#{ENV['COSMOS_REDIS_HOSTNAME']}:#{ENV['COSMOS_REDIS_PORT']}"
@@ -40,25 +42,27 @@ $redis_url = "redis://#{ENV['COSMOS_REDIS_HOSTNAME']}:#{ENV['COSMOS_REDIS_PORT']
40
42
  MIGRATE_OPTIONS = OpenStruct.new
41
43
  MIGRATE_OPTIONS.all = false
42
44
  MIGRATE_PARSER = OptionParser.new do |op|
43
- op.banner = "cosmos migrate PLUGIN [TGT1...] # Create a COSMOS 5 plugin from existing COSMOS 4 targets"
44
- op.on("-a", "--all", "Move all COSMOS 4 targets into a single COSMOS 5 plugin") do
45
+ op.banner = "cosmos migrate PLUGIN [TGT1...] # Create a COSMOS 5 plugin from existing COSMOS 4 targets"
46
+ op.on("-a", "--all", " Move all COSMOS 4 targets into a single COSMOS 5 plugin") do
45
47
  MIGRATE_OPTIONS.all = true
46
48
  end
47
49
  end
50
+ ERROR_CODE = 67 # ASCII 'C' for COSMOS
48
51
 
49
52
  # Prints the usage text for the cosmos executable
50
53
  def print_usage
51
54
  puts "Usage:"
52
55
  puts " cosmos help # Displays this information"
53
56
  puts " cosmos rake # Runs rake in the local directory"
54
- puts " cosmos load /PATH/FILENAME.gem # Loads a COSMOS plugin gem file"
57
+ puts " cosmos validate /PATH/FILENAME.gem SCOPE variables.txt # Validate a COSMOS plugin gem file"
58
+ puts " cosmos load /PATH/FILENAME.gem SCOPE variables.txt # Loads a COSMOS plugin gem file"
55
59
  puts " cosmos generate plugin PLUGIN_NAME # Generate a COSMOS plugin"
56
60
  puts " #{MIGRATE_PARSER}"
57
61
  puts " cosmos bridge CONFIG_FILENAME # Run COSMOS host bridge"
58
62
  puts " cosmos bridgesetup CONFIG_FILENAME # Create a default config file"
59
63
  puts " cosmos geminstall GEMFILENAME # Install loaded gem to /gems"
60
64
  puts " cosmos rubysloc # Counts Ruby SLOC recursively. Run with --help for more info."
61
- # puts " cosmos xtce_converter # Convert to and from the XTCE format. Run with --help for more info."
65
+ puts " cosmos xtce_converter # Convert to and from the XTCE format. Run with --help for more info."
62
66
  puts " cosmos cstol_converter # Converts CSTOL files (.prc) to COSMOS. Run with --help for more info."
63
67
  puts ""
64
68
  end
@@ -185,6 +189,76 @@ def migrate(args)
185
189
  puts "Plugin complete: #{File.expand_path('.')}" # Remember we're inside the plugin dir
186
190
  end
187
191
 
192
+ def xtce_converter(args)
193
+ options = {}
194
+ option_parser = OptionParser.new do |option_parser|
195
+ option_parser.banner = "Usage: xtce_converter [options] --import input_xtce_filename --output output_dir\n"+
196
+ " xtce_converter [options] --plugin /PATH/FILENAME.gem --output output_dir --variables variables.txt"
197
+ option_parser.separator("")
198
+ option_parser.on("-h", "--help", "Show this message") do
199
+ puts option_parser
200
+ exit
201
+ end
202
+ option_parser.on("-i VALUE", "--import VALUE", "Import the specified .xtce file") do |arg|
203
+ options[:import] = arg
204
+ end
205
+ option_parser.on("-o", "--output DIRECTORY", "Create files in the directory") do |arg|
206
+ options[:output] = arg
207
+ end
208
+ option_parser.on("-p", "--plugin PLUGIN", "Export .xtce file(s) from the plugin") do |arg|
209
+ options[:plugin] = arg
210
+ end
211
+ option_parser.on("-v", "--variables", "Optional variables file to pass to the plugin") do |arg|
212
+ options[:variables] = arg
213
+ end
214
+ end
215
+
216
+ begin
217
+ option_parser.parse!(args)
218
+ rescue => err
219
+ abort(option_parser.to_s)
220
+ end
221
+
222
+ if options[:import] && options[:plugin]
223
+ puts "xtce_converter options --import and --plugin are mutually exclusive"
224
+ abort(option_parser.to_s)
225
+ end
226
+
227
+ ENV['COSMOS_NO_STORE'] = '1' # it can be anything
228
+ Cosmos::Logger.stdout = false
229
+ Cosmos::Logger.level = Cosmos::Logger::DEBUG
230
+
231
+ if options[:import] && options[:output]
232
+ packet_config = Cosmos::PacketConfig.new
233
+ puts "Processing #{options[:import]}..."
234
+ packet_config.process_file(options[:import], nil)
235
+ puts "Writing COSMOS config files to #{options[:output]}/"
236
+ packet_config.to_config(options[:output])
237
+ exit(0)
238
+ elsif options[:plugin] && options[:output]
239
+ begin
240
+ variables = nil
241
+ variables = JSON.parse(File.read(options[:variables])) if options[:variables]
242
+ puts "Installing #{File.basename(options[:plugin])}"
243
+ plugin_hash = Cosmos::PluginModel.install_phase1(options[:plugin], variables, scope: 'DEFAULT', validate_only: true)
244
+ plugin_hash['variables']['xtce_output'] = options[:output]
245
+ Cosmos::PluginModel.install_phase2(plugin_hash['name'], plugin_hash['variables'], scope: 'DEFAULT', validate_only: true,
246
+ gem_file_path: options[:plugin])
247
+ result = 0 # bash and Windows consider 0 success
248
+ rescue => e
249
+ puts "Error: #{e.message}"
250
+ result = ERROR_CODE
251
+ ensure
252
+ name = Psych.safe_load(`gem spec #{options[:plugin]} name`).to_s
253
+ version = Psych.safe_load(`gem spec #{options[:plugin]} version`, permitted_classes: [Gem::Version]).to_s
254
+ Gem::Uninstaller.new(name, {:version => version, :force => true}).uninstall
255
+ exit(result)
256
+ end
257
+ else
258
+ abort(option_parser.to_s)
259
+ end
260
+ end
261
+
188
262
  # A helper method to make the zip writing recursion work
189
263
  def write_zip_entries(base_dir, entries, zip_path, io)
190
264
  io.add(zip_path, base_dir) # Add the directory whether it has entries or not
@@ -212,7 +286,66 @@ def put_into_archive(disk_file_path, io, zip_file_path)
212
286
  end
213
287
  end
214
288
 
215
- def load_plugin(plugin_file_path, scope:, variables_file: nil)
289
+ def validate_plugin(plugin_file_path, scope:, variables_file: nil)
290
+ ENV['COSMOS_NO_STORE'] = '1' # it can be anything
291
+ Cosmos::Logger.stdout = false
292
+ Cosmos::Logger.level = Cosmos::Logger::DEBUG
293
+ scope ||= 'DEFAULT'
294
+ variables = nil
295
+ variables = JSON.parse(File.read(variables_file)) if variables_file
296
+ puts "Installing #{File.basename(plugin_file_path)}"
297
+ plugin_hash = Cosmos::PluginModel.install_phase1(plugin_file_path, variables, scope: scope, validate_only: true)
298
+ Cosmos::PluginModel.install_phase2(plugin_hash['name'], plugin_hash['variables'], scope: scope, validate_only: true,
299
+ gem_file_path: plugin_file_path)
300
+ puts "Successfully validated #{File.basename(plugin_file_path)}"
301
+ result = 0 # bash and Windows consider 0 success
302
+ rescue => e
303
+ puts "Error: #{e.message}"
304
+ result = ERROR_CODE
305
+ ensure
306
+ name = Psych.safe_load(`gem spec #{plugin_file_path} name`).to_s
307
+ version = Psych.safe_load(`gem spec #{plugin_file_path} version`, permitted_classes: [Gem::Version]).to_s
308
+ Gem::Uninstaller.new(name, {:version => version, :force => true}).uninstall
309
+ exit(result)
310
+ end
311
+
312
+ def update_plugin(plugin_file_path, plugin_name, variables: nil, plugin_txt_lines: nil, scope:)
313
+ new_gem = File.basename(plugin_file_path)
314
+ old_gem = plugin_name.split("__")[0]
315
+ puts "Updating existing plugin: #{plugin_name} with #{File.basename(plugin_file_path)}"
316
+ plugin_model = Cosmos::PluginModel.get_model(name: plugin_name, scope: scope)
317
+ begin
318
+ # Only update if something has changed
319
+ if (new_gem != old_gem) or (variables != plugin_model.variables) or (plugin_txt_lines != plugin_model.plugin_txt_lines)
320
+ variables = plugin_model.variables unless variables
321
+ plugin_model.destroy
322
+ plugin_hash = Cosmos::PluginModel.install_phase1(plugin_file_path, existing_variables: variables, existing_plugin_txt_lines: plugin_txt_lines, process_existing: true, scope: scope)
323
+ Cosmos::PluginModel.install_phase2(plugin_hash, scope: scope)
324
+ else
325
+ puts "No changes detected - Exiting without change"
326
+ end
327
+ rescue => error
328
+ puts error.formatted
329
+ plugin_model.restore if plugin_model.destroyed?
330
+ raise error
331
+ end
332
+ end
333
+
334
+ # Loads a plugin into the COSMOS system
335
+ # This code is used from the command line and is the same code that gets called if you
336
+ # edit/upgrade or install a new plugin from the Admin interface
337
+ #
338
+ # Usage: cosmos load gemfile_path [scope] [plugin_hash_file_path]
339
+ #
340
+ # With just gemfile_path and/or scope: Will do nothing if any plugin
341
+ # with the same gem file already exists
342
+ #
343
+ # Otherwise will do what the plugin_hash_file says to do
344
+ # Plugin hash file must have the exact name of an existing plugin for upgrades and edits
345
+ # Otherwise, it will be assumed that the plugin is intentionally being installed for a second
346
+ # time
347
+ #
348
+ def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil)
216
349
  scope ||= 'DEFAULT'
217
350
  # Only create the scope if it doesn't already exist
218
351
  unless Cosmos::ScopeModel.names.include?(scope)
@@ -226,41 +359,47 @@ def load_plugin(plugin_file_path, scope:, variables_file: nil)
226
359
  end
227
360
 
228
361
  begin
229
- # Try to find an existing plugin with this file to upgrade
230
- gem_filename = File.basename(plugin_file_path, ".gem")
231
- gem_filename = gem_filename.split('-')[0..-2].join('-')
232
- plugin_names = Cosmos::PluginModel.names(scope: scope)
233
- found = false
234
- plugin_names.each do |plugin_name|
235
- gem_name = plugin_name.split("__")[0]
236
- gem_name = File.basename(plugin_name, ".gem")
237
- gem_name = gem_name.split('-')[0..-2].join('-')
238
- if gem_filename == gem_name
239
- puts "Upgrading existing plugin: #{plugin_name} with #{plugin_file_path}"
240
- plugin_model = Cosmos::PluginModel.get_model(name: plugin_name, scope: scope)
241
- variables = plugin_model.variables
242
- plugin_model.destroy
243
- if variables_file
244
- # Assume phase1 is already done
245
- variables = JSON.parse(File.read(variables_file))
246
- plugin_hash = Cosmos::PluginModel.new(name: File.basename(plugin_file_path), variables: variables, scope: scope).as_json
247
- else
248
- plugin_hash = Cosmos::PluginModel.install_phase1(plugin_file_path, variables, scope: scope)
362
+ if plugin_hash_file
363
+ # Admin Create / Edit / or Upgrade Plugin
364
+ Cosmos::PluginModel.install_phase1(plugin_file_path, scope: scope)
365
+ plugin_hash = JSON.parse(File.read(plugin_hash_file))
366
+ else
367
+ # Init or Command Line cosmos load with no plugin_hash_file
368
+ file_full_name = File.basename(plugin_file_path, ".gem")
369
+ file_gem_name = file_full_name.split('-')[0..-2].join('-')
370
+ found = false
371
+ plugin_names = Cosmos::PluginModel.names(scope: scope)
372
+ plugin_names.each do |plugin_name|
373
+ gem_name = plugin_name.split("__")[0]
374
+ full_name = File.basename(gem_name, ".gem")
375
+ gem_name = full_name.split('-')[0..-2].join('-')
376
+ if file_gem_name == gem_name
377
+ found = true
378
+ # Upgrade if version changed else do nothing
379
+ if file_full_name != full_name
380
+ update_plugin(plugin_file_path, plugin_name, scope: scope)
381
+ end
249
382
  end
250
- Cosmos::PluginModel.install_phase2(plugin_hash['name'], plugin_hash['variables'], scope: scope)
251
- found = true
252
383
  end
384
+ return if found
385
+
386
+ plugin_hash = Cosmos::PluginModel.install_phase1(plugin_file_path, scope: scope)
253
387
  end
254
- unless found
255
- puts "Loading new plugin: #{plugin_file_path}"
256
- if variables_file
257
- # Assume phase1 is already done
258
- variables = JSON.parse(File.read(variables_file))
259
- plugin_hash = Cosmos::PluginModel.new(name: File.basename(plugin_file_path), variables: variables, scope: scope).as_json
260
- else
261
- plugin_hash = Cosmos::PluginModel.install_phase1(plugin_file_path, scope: scope)
262
- end
263
- Cosmos::PluginModel.install_phase2(plugin_hash['name'], plugin_hash['variables'], scope: scope)
388
+
389
+ # Determine if plugin named in plugin_hash exists
390
+ existing_plugin_hash = Cosmos::PluginModel.get(name: plugin_hash['name'], scope: scope)
391
+
392
+ # Existing plugin hash will be present if plugin is being edited or upgraded
393
+ # If editing, gem name will match existing hash name
394
+ # If upgrading, gem name will not match the existing hash name
395
+
396
+ if existing_plugin_hash
397
+ # Upgrade or Edit
398
+ update_plugin(plugin_file_path, plugin_hash['name'], variables: plugin_hash['variables'], plugin_txt_lines: plugin_hash['plugin_txt_lines'], scope: scope)
399
+ else
400
+ # New Install
401
+ puts "Loading new plugin: #{plugin_file_path}\n#{plugin_hash}"
402
+ Cosmos::PluginModel.install_phase2(plugin_hash, scope: scope)
264
403
  end
265
404
  rescue => err
266
405
  abort("Error installing plugin: #{scope}: #{plugin_file_path}: #{err.formatted}")
@@ -305,8 +444,11 @@ if not ARGV[0].nil? # argument(s) given
305
444
  when 'rake'
306
445
  puts `rake #{ARGV[1..-1].join(' ')}`
307
446
 
447
+ when 'validate'
448
+ validate_plugin(ARGV[1], scope: ARGV[2], variables_file: ARGV[3])
449
+
308
450
  when 'load'
309
- load_plugin(ARGV[1], scope: ARGV[2], variables_file: ARGV[3])
451
+ load_plugin(ARGV[1], scope: ARGV[2], plugin_hash_file: ARGV[3])
310
452
 
311
453
  when 'geminstall'
312
454
  gem_install(ARGV[1])
@@ -333,9 +475,8 @@ if not ARGV[0].nil? # argument(s) given
333
475
  when 'cstol_converter'
334
476
  puts `ruby /cosmos/bin/cstol_converter #{ARGV[1..-1].join(' ')}`
335
477
 
336
- # TODO: This still needs work in COSMOS 5
337
- # when 'xtce_converter'
338
- # puts `ruby /cosmos/bin/xtce_converter #{ARGV[1..-1].join(' ')}`
478
+ when 'xtce_converter'
479
+ xtce_converter(ARGV[1..-1])
339
480
 
340
481
  when 'bridge'
341
482
  ENV['COSMOS_NO_STORE'] = '1'
@@ -68,3 +68,13 @@ ROUTER:
68
68
  <%= MetaConfigParser.load('target.yaml').to_meta_config_yaml() %>
69
69
  <%= MetaConfigParser.load('microservice.yaml').to_meta_config_yaml() %>
70
70
  <%= MetaConfigParser.load('tool.yaml').to_meta_config_yaml() %>
71
+ WIDGET:
72
+ summary: Define a custom widget
73
+ example: WIDGET HELLOWORLD
74
+ description: Defines a custom widget that can be used in Telemetry Viewer screens.
75
+ parameters:
76
+ - name: Widget Name
77
+ description: The name of the widget wil be used to build a path to the widget implementation. For example, `WIDGET HELLOWORLD` will find the as-built file tools/widgets/HelloworldWidget/HelloworldWidget.umd.min.js. See the [Custom Widgets](/docs/v5/custom-widgets)
78
+ guide for more details.
79
+ required: true
80
+ values: .+
@@ -30,19 +30,19 @@ VALUE mCosmosIO = Qnil;
30
30
  static ID id_method_read = 0;
31
31
 
32
32
  /* Reads a length field and then return the String resulting from reading the
33
- * number of bytes the length field indicates
34
- *
35
- * For example:
36
- * io = StringIO.new
37
- * # where io is "\x02\x01\x02\x03\x04...."
38
- * result = io.read_length_bytes(1)
39
- * # result will be "\x01x02" because the length field was given
40
- * # to be 1 byte. We read 1 byte which is a 2. So we then read two
41
- * # bytes and return.
42
- *
43
- * @param length_num_bytes [Integer] Number of bytes in the length field
44
- * @return [String] A String of "length field" number of bytes
45
- */
33
+ * number of bytes the length field indicates
34
+ *
35
+ * For example:
36
+ * io = StringIO.new
37
+ * # where io is "\x02\x01\x02\x03\x04...."
38
+ * result = io.read_length_bytes(1)
39
+ * # result will be "\x01x02" because the length field was given
40
+ * # to be 1 byte. We read 1 byte which is a 2. So we then read two
41
+ * # bytes and return.
42
+ *
43
+ * @param length_num_bytes [Integer] Number of bytes in the length field
44
+ * @return [String] A String of "length field" number of bytes
45
+ */
46
46
  static VALUE read_length_bytes(int argc, VALUE *argv, VALUE self)
47
47
  {
48
48
  int length_num_bytes = 0;
@@ -134,7 +134,7 @@ static VALUE read_length_bytes(int argc, VALUE *argv, VALUE self)
134
134
  }
135
135
 
136
136
  /* Read String */
137
- temp_string_length = UINT2NUM(string_length);
137
+ temp_string_length = UINT2NUM((unsigned int)string_length);
138
138
  return_value = rb_funcall(self, id_method_read, 1, temp_string_length);
139
139
  if (NIL_P(return_value) || (RSTRING_LEN(return_value) != string_length))
140
140
  {
@@ -196,7 +196,7 @@ static VALUE packet_initialize(int argc, VALUE *argv, VALUE self)
196
196
  packet_name = argv[1];
197
197
  default_endianness = symbol_BIG_ENDIAN;
198
198
  description = Qnil;
199
- buffer = rb_str_new2("");
199
+ buffer = Qnil;
200
200
  item_class = cPacketItem;
201
201
  break;
202
202
  case 3:
@@ -204,7 +204,7 @@ static VALUE packet_initialize(int argc, VALUE *argv, VALUE self)
204
204
  packet_name = argv[1];
205
205
  default_endianness = argv[2];
206
206
  description = Qnil;
207
- buffer = rb_str_new2("");
207
+ buffer = Qnil;
208
208
  item_class = cPacketItem;
209
209
  break;
210
210
  case 4:
@@ -212,7 +212,7 @@ static VALUE packet_initialize(int argc, VALUE *argv, VALUE self)
212
212
  packet_name = argv[1];
213
213
  default_endianness = argv[2];
214
214
  description = argv[3];
215
- buffer = rb_str_new2("");
215
+ buffer = Qnil;
216
216
  item_class = cPacketItem;
217
217
  break;
218
218
  case 5:
@@ -56,6 +56,7 @@ static ID id_method_reverse = 0;
56
56
  static ID id_method_Integer = 0;
57
57
  static ID id_method_Float = 0;
58
58
  static ID id_method_kind_of = 0;
59
+ static ID id_method_allocate_buffer_if_needed = 0;
59
60
 
60
61
  static ID id_ivar_buffer = 0;
61
62
  static ID id_ivar_bit_offset = 0;
@@ -520,7 +521,8 @@ static VALUE binary_accessor_read(VALUE self, VALUE param_bit_offset, VALUE para
520
521
  {
521
522
  string_length = upper_bound - lower_bound + 1;
522
523
  string = malloc(string_length + 1);
523
- if (string == NULL) {
524
+ if (string == NULL)
525
+ {
524
526
  rb_raise(rb_eNoMemError, "malloc of %d returned NULL", string_length + 1);
525
527
  }
526
528
  memcpy(string, buffer + lower_bound, string_length);
@@ -577,7 +579,8 @@ static VALUE binary_accessor_read(VALUE self, VALUE param_bit_offset, VALUE para
577
579
  string_length = ((bit_size - 1) / 8) + 1;
578
580
  array_length = string_length + 4; /* Required number of bytes plus slack */
579
581
  unsigned_char_array = (unsigned char *)malloc(array_length);
580
- if (unsigned_char_array == NULL) {
582
+ if (unsigned_char_array == NULL)
583
+ {
581
584
  rb_raise(rb_eNoMemError, "malloc of %d returned NULL", array_length);
582
585
  }
583
586
  read_bitfield(lower_bound, upper_bound, bit_offset, bit_size, given_bit_offset, given_bit_size, param_endianness, buffer, (int)buffer_length, unsigned_char_array);
@@ -692,7 +695,8 @@ static VALUE binary_accessor_read(VALUE self, VALUE param_bit_offset, VALUE para
692
695
  string_length = ((bit_size - 1) / 8) + 1;
693
696
  array_length = string_length + 4; /* Required number of bytes plus slack */
694
697
  unsigned_char_array = (unsigned char *)malloc(array_length);
695
- if (unsigned_char_array == NULL) {
698
+ if (unsigned_char_array == NULL)
699
+ {
696
700
  rb_raise(rb_eNoMemError, "malloc of %d returned NULL", array_length);
697
701
  }
698
702
  read_bitfield(lower_bound, upper_bound, bit_offset, bit_size, given_bit_offset, given_bit_size, param_endianness, buffer, (int)buffer_length, unsigned_char_array);
@@ -1147,7 +1151,8 @@ static VALUE binary_accessor_write(VALUE self, VALUE value, VALUE param_bit_offs
1147
1151
  string_length = ((bit_size - 1) / 8) + 1;
1148
1152
  array_length = string_length + 4; /* Required number of bytes plus slack */
1149
1153
  unsigned_char_array = (unsigned char *)malloc(array_length);
1150
- if (unsigned_char_array == NULL) {
1154
+ if (unsigned_char_array == NULL)
1155
+ {
1151
1156
  rb_raise(rb_eNoMemError, "malloc of %d returned NULL", array_length);
1152
1157
  }
1153
1158
 
@@ -1275,15 +1280,8 @@ static VALUE binary_accessor_write(VALUE self, VALUE value, VALUE param_bit_offs
1275
1280
  */
1276
1281
  static int get_int_length(VALUE self)
1277
1282
  {
1278
- volatile VALUE buffer = rb_ivar_get(self, id_ivar_buffer);
1279
- if (RTEST(buffer))
1280
- {
1281
- return (int)RSTRING_LEN(buffer);
1282
- }
1283
- else
1284
- {
1285
- return 0;
1286
- }
1283
+ rb_funcall(self, id_method_allocate_buffer_if_needed, 0);
1284
+ return (int)RSTRING_LEN(rb_ivar_get(self, id_ivar_buffer));
1287
1285
  }
1288
1286
 
1289
1287
  /*
@@ -1310,24 +1308,21 @@ static VALUE read_item_internal(VALUE self, VALUE item, VALUE buffer)
1310
1308
  return Qnil;
1311
1309
  }
1312
1310
 
1313
- if (RTEST(buffer))
1311
+ if (!(RTEST(buffer)))
1314
1312
  {
1315
- bit_offset = rb_ivar_get(item, id_ivar_bit_offset);
1316
- bit_size = rb_ivar_get(item, id_ivar_bit_size);
1317
- array_size = rb_ivar_get(item, id_ivar_array_size);
1318
- endianness = rb_ivar_get(item, id_ivar_endianness);
1319
- if (RTEST(array_size))
1320
- {
1321
- return rb_funcall(cBinaryAccessor, id_method_read_array, 6, bit_offset, bit_size, data_type, array_size, buffer, endianness);
1322
- }
1323
- else
1324
- {
1325
- return binary_accessor_read(cBinaryAccessor, bit_offset, bit_size, data_type, buffer, endianness);
1326
- }
1313
+ buffer = rb_funcall(self, id_method_allocate_buffer_if_needed, 0);
1314
+ }
1315
+ bit_offset = rb_ivar_get(item, id_ivar_bit_offset);
1316
+ bit_size = rb_ivar_get(item, id_ivar_bit_size);
1317
+ array_size = rb_ivar_get(item, id_ivar_array_size);
1318
+ endianness = rb_ivar_get(item, id_ivar_endianness);
1319
+ if (RTEST(array_size))
1320
+ {
1321
+ return rb_funcall(cBinaryAccessor, id_method_read_array, 6, bit_offset, bit_size, data_type, array_size, buffer, endianness);
1327
1322
  }
1328
1323
  else
1329
1324
  {
1330
- rb_raise(rb_eRuntimeError, "No buffer given to read_item");
1325
+ return binary_accessor_read(cBinaryAccessor, bit_offset, bit_size, data_type, buffer, endianness);
1331
1326
  }
1332
1327
  }
1333
1328
 
@@ -1404,8 +1399,8 @@ static VALUE structure_item_spaceship(VALUE self, VALUE other_item)
1404
1399
  if ((bit_offset == 0) && (other_bit_offset == 0))
1405
1400
  {
1406
1401
  /* Both bit_offsets are 0 so sort by bit_size
1407
- * This allows derived items with bit_size of 0 to be listed first
1408
- * Compare based on bit size */
1402
+ * This allows derived items with bit_size of 0 to be listed first
1403
+ * Compare based on bit size */
1409
1404
  bit_size = FIX2INT(rb_ivar_get(self, id_ivar_bit_size));
1410
1405
  other_bit_size = FIX2INT(rb_ivar_get(other_item, id_ivar_bit_size));
1411
1406
  if (bit_size == other_bit_size)
@@ -1518,12 +1513,12 @@ static VALUE structure_initialize(int argc, VALUE *argv, VALUE self)
1518
1513
  {
1519
1514
  case 0:
1520
1515
  default_endianness = HOST_ENDIANNESS;
1521
- buffer = rb_str_new2("");
1516
+ buffer = Qnil;
1522
1517
  item_class = cStructureItem;
1523
1518
  break;
1524
1519
  case 1:
1525
1520
  default_endianness = argv[0];
1526
- buffer = rb_str_new2("");
1521
+ buffer = Qnil;
1527
1522
  item_class = cStructureItem;
1528
1523
  break;
1529
1524
  case 2:
@@ -1592,6 +1587,10 @@ static VALUE resize_buffer(VALUE self)
1592
1587
  rb_str_concat(buffer, rb_str_times(ZERO_STRING, INT2FIX(defined_length - current_length)));
1593
1588
  }
1594
1589
  }
1590
+ else
1591
+ {
1592
+ rb_funcall(self, id_method_allocate_buffer_if_needed, 0);
1593
+ }
1595
1594
 
1596
1595
  return self;
1597
1596
  }
@@ -1618,6 +1617,7 @@ void Init_structure(void)
1618
1617
  id_method_Integer = rb_intern("Integer");
1619
1618
  id_method_Float = rb_intern("Float");
1620
1619
  id_method_kind_of = rb_intern("kind_of?");
1620
+ id_method_allocate_buffer_if_needed = rb_intern("allocate_buffer_if_needed");
1621
1621
 
1622
1622
  MIN_INT8 = INT2NUM(-128);
1623
1623
  MAX_INT8 = INT2NUM(127);
@@ -111,11 +111,11 @@ module Cosmos
111
111
 
112
112
  # Injects a packet into the system as if it was received from an interface
113
113
  #
114
- # @param target_name[String] Target name of the packet
115
- # @param packet_name[String] Packet name of the packet
116
- # @param item_hash[Hash] Hash of item_name and value for each item you want to change from the current value table
114
+ # @param target_name [String] Target name of the packet
115
+ # @param packet_name [String] Packet name of the packet
116
+ # @param item_hash [Hash] Hash of item_name and value for each item you want to change from the current value table
117
117
  # @param type [Symbol] Telemetry type, :RAW, :CONVERTED (default), :FORMATTED, or :WITH_UNITS
118
- def inject_tlm(target_name, packet_name, item_hash = nil, log: true, type: :CONVERTED, scope: $cosmos_scope, token: $cosmos_token)
118
+ def inject_tlm(target_name, packet_name, item_hash = nil, type: :CONVERTED, scope: $cosmos_scope, token: $cosmos_token)
119
119
  authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
120
120
  unless CvtModel::VALUE_TYPES.include?(type.intern)
121
121
  raise "Unknown type '#{type}' for #{target_name} #{packet_name}"