openc3 5.3.0 → 5.4.0

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +108 -105
  3. data/data/config/item_modifiers.yaml +4 -2
  4. data/data/config/table_manager.yaml +2 -2
  5. data/data/config/tool.yaml +1 -1
  6. data/ext/openc3/ext/config_parser/config_parser.c +17 -2
  7. data/lib/openc3/api/api.rb +1 -0
  8. data/lib/openc3/api/metrics_api.rb +97 -0
  9. data/lib/openc3/api/tlm_api.rb +2 -1
  10. data/lib/openc3/config/config_parser.rb +29 -4
  11. data/lib/openc3/core_ext/time.rb +6 -1
  12. data/lib/openc3/microservices/cleanup_microservice.rb +17 -1
  13. data/lib/openc3/microservices/decom_microservice.rb +12 -9
  14. data/lib/openc3/microservices/interface_microservice.rb +46 -6
  15. data/lib/openc3/microservices/log_microservice.rb +11 -5
  16. data/lib/openc3/microservices/microservice.rb +4 -9
  17. data/lib/openc3/microservices/periodic_microservice.rb +7 -0
  18. data/lib/openc3/microservices/reaction_microservice.rb +0 -33
  19. data/lib/openc3/microservices/reducer_microservice.rb +14 -10
  20. data/lib/openc3/microservices/text_log_microservice.rb +12 -3
  21. data/lib/openc3/microservices/timeline_microservice.rb +0 -6
  22. data/lib/openc3/microservices/trigger_group_microservice.rb +0 -20
  23. data/lib/openc3/models/metric_model.rb +53 -6
  24. data/lib/openc3/models/microservice_model.rb +8 -1
  25. data/lib/openc3/models/plugin_model.rb +6 -1
  26. data/lib/openc3/models/target_model.rb +2 -2
  27. data/lib/openc3/models/tool_model.rb +17 -8
  28. data/lib/openc3/operators/operator.rb +5 -1
  29. data/lib/openc3/packets/packet.rb +21 -7
  30. data/lib/openc3/packets/packet_item.rb +3 -2
  31. data/lib/openc3/script/script.rb +8 -0
  32. data/lib/openc3/script/script_runner.rb +1 -2
  33. data/lib/openc3/script/storage.rb +2 -1
  34. data/lib/openc3/script/suite.rb +15 -11
  35. data/lib/openc3/system/system.rb +6 -3
  36. data/lib/openc3/topics/interface_topic.rb +1 -1
  37. data/lib/openc3/topics/router_topic.rb +1 -1
  38. data/lib/openc3/utilities/aws_bucket.rb +20 -3
  39. data/lib/openc3/utilities/bucket.rb +1 -1
  40. data/lib/openc3/utilities/bucket_file_cache.rb +1 -1
  41. data/lib/openc3/utilities/bucket_utilities.rb +1 -1
  42. data/lib/openc3/utilities/local_mode.rb +1 -0
  43. data/lib/openc3/utilities/metric.rb +77 -101
  44. data/lib/openc3/utilities/s3_autoload.rb +19 -9
  45. data/lib/openc3/utilities/target_file.rb +3 -1
  46. data/lib/openc3/version.rb +5 -5
  47. data/templates/plugin-template/LICENSE.txt +7 -0
  48. data/templates/plugin-template/plugin.gemspec +4 -4
  49. metadata +4 -3
  50. data/data/config/_interfaces.yaml.err +0 -1017
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 883789b740b96ae3fac34c78b5a643a2fa1fb087d601a0f87c46d0062eec3dec
4
- data.tar.gz: 510b9f4c8f98735f033f6dd6d31ebd86ffaeb8df0664d77720e15a3df3df6dcb
3
+ metadata.gz: 6c51fd2722cde933052c2bf5941c3a111a14ae995de868bb6371faf9f32f708e
4
+ data.tar.gz: 0a1684b608df0335f485e59b29e55e66a1307cb62c7ab76586989a4566096946
5
5
  SHA512:
6
- metadata.gz: 999476145415b7f6b160319fee0f67174ad2cdf415ef3a87628177964e37bf690e5c368bf3da9c6a08ca663679999b71b3e7d4c176c2a302724073b7c5c8faca
7
- data.tar.gz: 206852353795f779b2d8481f68bcf881c465ceaf3c6a48b852a61dab34275b7736249def97edd7932e7fa9286d71f07f210ca340c0363dbce30839504d548640
6
+ metadata.gz: c36db608db41b6f0c1edc8ae6b83293d71eb37984e115d86a9ca9ff1e6aa4f124c3e0d645b1ac01b329d3fd490ef6a3b281ddc2754344bbdc1c78a702258522c
7
+ data.tar.gz: 754bc2ce9f0fd0066e47731bc5b50279ac5ce166f20752bb5be42d2ce4ab09698212ee82abcfd1ab353aa1e85029f47e5b766b4bfdba60823682c23148754837
data/bin/openc3cli CHANGED
@@ -322,14 +322,14 @@ ensure
322
322
  exit(result)
323
323
  end
324
324
 
325
- def update_plugin(plugin_file_path, plugin_name, variables: nil, plugin_txt_lines: nil, scope:, existing_plugin_name:)
325
+ def update_plugin(plugin_file_path, plugin_name, variables: nil, plugin_txt_lines: nil, scope:, existing_plugin_name:, force: false)
326
326
  new_gem = File.basename(plugin_file_path)
327
327
  old_gem = existing_plugin_name.split("__")[0]
328
328
  puts "Updating existing plugin: #{existing_plugin_name} with #{File.basename(plugin_file_path)}"
329
329
  plugin_model = OpenC3::PluginModel.get_model(name: existing_plugin_name, scope: scope)
330
330
  begin
331
331
  # Only update if something has changed
332
- if (new_gem != old_gem) or (variables and variables != plugin_model.variables) or (plugin_txt_lines and plugin_txt_lines != plugin_model.plugin_txt_lines)
332
+ if force or (new_gem != old_gem) or (variables and variables != plugin_model.variables) or (plugin_txt_lines and plugin_txt_lines != plugin_model.plugin_txt_lines)
333
333
  puts "Gem version change detected - New: #{new_gem}, Old: #{old_gem}" if new_gem != old_gem
334
334
  if variables and variables != plugin_model.variables
335
335
  pp_variables = ""
@@ -360,7 +360,7 @@ end
360
360
  # This code is used from the command line and is the same code that gets called if you
361
361
  # edit/upgrade or install a new plugin from the Admin interface
362
362
  #
363
- # Usage: cli load gemfile_path [scope] [plugin_hash_file_path]
363
+ # Usage: cli load gemfile_path [scope] [plugin_hash_file_path] [force]
364
364
  #
365
365
  # With just gemfile_path and/or scope: Will do nothing if any plugin
366
366
  # with the same gem file already exists
@@ -370,7 +370,10 @@ end
370
370
  # Otherwise, it will be assumed that the plugin is intentionally being installed for a second
371
371
  # time
372
372
  #
373
- def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil)
373
+ # Pass true as the last argument to force install even if a plugin with
374
+ # the same version number exists
375
+ #
376
+ def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil, force: false)
374
377
  scope ||= 'DEFAULT'
375
378
  # Only create the scope if it doesn't already exist
376
379
  unless OpenC3::ScopeModel.names.include?(scope)
@@ -403,7 +406,7 @@ def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil)
403
406
  found = true
404
407
  # Upgrade if version changed else do nothing
405
408
  if file_full_name != full_name
406
- update_plugin(plugin_file_path, plugin_name, scope: scope, existing_plugin_name: plugin_name)
409
+ update_plugin(plugin_file_path, plugin_name, scope: scope, existing_plugin_name: plugin_name, force: force)
407
410
  else
408
411
  puts "No version change detected for: #{plugin_name}"
409
412
  end
@@ -430,15 +433,16 @@ def load_plugin(plugin_file_path, scope:, plugin_hash_file: nil)
430
433
 
431
434
  if existing_plugin_hash
432
435
  # Upgrade or Edit
433
- update_plugin(plugin_file_path, plugin_hash['name'], variables: plugin_hash['variables'], plugin_txt_lines: plugin_hash['plugin_txt_lines'], scope: scope, existing_plugin_name: existing_plugin_hash['name'])
436
+ update_plugin(plugin_file_path, plugin_hash['name'], variables: plugin_hash['variables'], scope: scope,
437
+ plugin_txt_lines: plugin_hash['plugin_txt_lines'], existing_plugin_name: existing_plugin_hash['name'], force: force)
434
438
  else
435
439
  # New Install
436
440
  puts "Loading new plugin: #{plugin_file_path}\n#{plugin_hash}"
437
441
  plugin_hash = OpenC3::PluginModel.install_phase2(plugin_hash, scope: scope)
438
442
  OpenC3::LocalMode.update_local_plugin(plugin_file_path, plugin_hash, scope: scope)
439
443
  end
440
- rescue => err
441
- abort("Error installing plugin: #{scope}: #{plugin_file_path}: #{err.formatted}")
444
+ rescue => error
445
+ abort("Error installing plugin: #{scope}: #{plugin_file_path}\n#{error.message}")
442
446
  end
443
447
  end
444
448
 
@@ -517,129 +521,128 @@ def run_migrations(folder)
517
521
  end
518
522
  end
519
523
 
520
- if __FILE__ == $0
521
- if not ARGV[0].nil? # argument(s) given
524
+ if not ARGV[0].nil? # argument(s) given
522
525
 
523
- # Handle each task
524
- case ARGV[0].downcase
526
+ # Handle each task
527
+ case ARGV[0].downcase
525
528
 
526
- when 'rake'
527
- puts `rake #{ARGV[1..-1].join(' ')}`
529
+ when 'rake'
530
+ puts `rake #{ARGV[1..-1].join(' ')}`
528
531
 
529
- when 'validate'
530
- validate_plugin(ARGV[1], scope: ARGV[2], variables_file: ARGV[3])
532
+ when 'validate'
533
+ validate_plugin(ARGV[1], scope: ARGV[2], variables_file: ARGV[3])
531
534
 
532
- when 'load'
533
- load_plugin(ARGV[1], scope: ARGV[2], plugin_hash_file: ARGV[3])
535
+ when 'load'
536
+ # force is a boolean so if they pass 'force' it is true
537
+ # See plugins_controller.rb install for usage
538
+ load_plugin(ARGV[1], scope: ARGV[2], plugin_hash_file: ARGV[3], force: ARGV[4] == 'force')
534
539
 
535
- when 'unload'
536
- unload_plugin(ARGV[1], scope: ARGV[2])
540
+ when 'unload'
541
+ unload_plugin(ARGV[1], scope: ARGV[2])
537
542
 
538
- when 'geminstall'
539
- gem_install(ARGV[1], scope: ARGV[2])
543
+ when 'geminstall'
544
+ gem_install(ARGV[1], scope: ARGV[2])
540
545
 
541
- when 'generate'
542
- generate(ARGV[1..-1])
546
+ when 'generate'
547
+ generate(ARGV[1..-1])
543
548
 
544
- when 'migrate'
545
- migrate(ARGV[1..-1])
549
+ when 'migrate'
550
+ migrate(ARGV[1..-1])
546
551
 
547
- when 'rubysloc'
548
- puts `ruby /openc3/bin/rubysloc #{ARGV[1..-1].join(' ')}`
552
+ when 'rubysloc'
553
+ puts `ruby /openc3/bin/rubysloc #{ARGV[1..-1].join(' ')}`
549
554
 
550
- when 'cstol_converter'
551
- puts `ruby /openc3/bin/cstol_converter #{ARGV[1..-1].join(' ')}`
555
+ when 'cstol_converter'
556
+ puts `ruby /openc3/bin/cstol_converter #{ARGV[1..-1].join(' ')}`
552
557
 
553
- when 'xtce_converter'
554
- xtce_converter(ARGV[1..-1])
558
+ when 'xtce_converter'
559
+ xtce_converter(ARGV[1..-1])
555
560
 
556
- when 'bridge'
557
- ENV['OPENC3_NO_STORE'] = '1'
558
- filename = ARGV[1]
559
- filename = 'bridge.txt' unless filename
560
- bridge = OpenC3::Bridge.new(filename)
561
- begin
562
- while true
563
- sleep(1)
564
- end
565
- rescue Interrupt
566
- exit(0)
561
+ when 'bridge'
562
+ ENV['OPENC3_NO_STORE'] = '1'
563
+ filename = ARGV[1]
564
+ filename = 'bridge.txt' unless filename
565
+ bridge = OpenC3::Bridge.new(filename)
566
+ begin
567
+ while true
568
+ sleep(1)
567
569
  end
570
+ rescue Interrupt
571
+ exit(0)
572
+ end
568
573
 
569
- when 'bridgesetup'
570
- ENV['OPENC3_NO_STORE'] = '1'
571
- filename = ARGV[1]
572
- filename = 'bridge.txt' unless filename
573
- unless File.exist?(filename)
574
- OpenC3::BridgeConfig.generate_default(filename)
575
- end
574
+ when 'bridgesetup'
575
+ ENV['OPENC3_NO_STORE'] = '1'
576
+ filename = ARGV[1]
577
+ filename = 'bridge.txt' unless filename
578
+ unless File.exist?(filename)
579
+ OpenC3::BridgeConfig.generate_default(filename)
580
+ end
576
581
 
577
- when 'help'
578
- print_usage()
579
-
580
- when 'redis'
581
- case (ARGV[1])
582
- when 'keys'
583
- get_redis_keys()
584
- when 'hget'
585
- redis = Redis.new(url: $redis_url, username: ENV['OPENC3_REDIS_USERNAME'], password: ENV['OPENC3_REDIS_PASSWORD'])
586
- puts JSON.parse(redis.hget(ARGV[2], ARGV[3]), :allow_nan => true, :create_additions => true)
587
- else
588
- puts "Unknown redis task: #{ARGV[1]}\n"
589
- puts "Valid redis tasks: keys, hget"
590
- end
582
+ when 'help'
583
+ print_usage()
591
584
 
592
- when 'removebase'
593
- # Used to remove tool base to better support enterprise upgrade
594
- scopes = OpenC3::ScopeModel.all
595
- scopes.each do |scope_name, scope|
596
- plugins = OpenC3::PluginModel.all(scope: scope_name)
597
- plugins.each do |plugin_name, plugin|
598
- if plugin["name"] =~ /tool-base/ and plugin["name"] !~ /enterprise/
599
- unload_plugin(plugin_name, scope: scope_name)
600
- end
601
- if plugin["name"] =~ /tool-admin/ and plugin["name"] !~ /enterprise/
602
- unload_plugin(plugin_name, scope: scope_name)
603
- end
585
+ when 'redis'
586
+ case (ARGV[1])
587
+ when 'keys'
588
+ get_redis_keys()
589
+ when 'hget'
590
+ redis = Redis.new(url: $redis_url, username: ENV['OPENC3_REDIS_USERNAME'], password: ENV['OPENC3_REDIS_PASSWORD'])
591
+ puts JSON.parse(redis.hget(ARGV[2], ARGV[3]), :allow_nan => true, :create_additions => true)
592
+ else
593
+ puts "Unknown redis task: #{ARGV[1]}\n"
594
+ puts "Valid redis tasks: keys, hget"
595
+ end
596
+
597
+ when 'removebase'
598
+ # Used to remove tool base to better support enterprise upgrade
599
+ scopes = OpenC3::ScopeModel.all
600
+ scopes.each do |scope_name, scope|
601
+ plugins = OpenC3::PluginModel.all(scope: scope_name)
602
+ plugins.each do |plugin_name, plugin|
603
+ if plugin["name"] =~ /tool-base/ and plugin["name"] !~ /enterprise/
604
+ unload_plugin(plugin_name, scope: scope_name)
605
+ end
606
+ if plugin["name"] =~ /tool-admin/ and plugin["name"] !~ /enterprise/
607
+ unload_plugin(plugin_name, scope: scope_name)
604
608
  end
605
609
  end
610
+ end
606
611
 
607
- when 'removeenterprise'
608
- # Used to remove enterprise plugins to better support downgrade
609
- scopes = OpenC3::ScopeModel.all
610
- scopes.each do |scope_name, scope|
611
- plugins = OpenC3::PluginModel.all(scope: scope_name)
612
- plugins.each do |plugin_name, plugin|
613
- if plugin["name"] =~ /enterprise/
614
- unload_plugin(plugin_name, scope: scope_name)
615
- end
612
+ when 'removeenterprise'
613
+ # Used to remove enterprise plugins to better support downgrade
614
+ scopes = OpenC3::ScopeModel.all
615
+ scopes.each do |scope_name, scope|
616
+ plugins = OpenC3::PluginModel.all(scope: scope_name)
617
+ plugins.each do |plugin_name, plugin|
618
+ if plugin["name"] =~ /enterprise/
619
+ unload_plugin(plugin_name, scope: scope_name)
616
620
  end
617
621
  end
622
+ end
618
623
 
619
- when 'destroyscope'
620
- scope = OpenC3::ScopeModel.get_model(name: ARGV[1])
621
- scope.destroy
624
+ when 'destroyscope'
625
+ scope = OpenC3::ScopeModel.get_model(name: ARGV[1])
626
+ scope.destroy
622
627
 
623
- when 'localinit'
624
- OpenC3::LocalMode.local_init()
628
+ when 'localinit'
629
+ OpenC3::LocalMode.local_init()
625
630
 
626
- when 'initbuckets'
627
- client = OpenC3::Bucket.getClient()
628
- client.create(ENV['OPENC3_CONFIG_BUCKET'])
629
- client.create(ENV['OPENC3_LOGS_BUCKET'])
630
- client.create(ENV['OPENC3_TOOLS_BUCKET'])
631
- client.ensure_public(ENV['OPENC3_TOOLS_BUCKET'])
631
+ when 'initbuckets'
632
+ client = OpenC3::Bucket.getClient()
633
+ client.create(ENV['OPENC3_CONFIG_BUCKET'])
634
+ client.create(ENV['OPENC3_LOGS_BUCKET'])
635
+ client.create(ENV['OPENC3_TOOLS_BUCKET'])
636
+ client.ensure_public(ENV['OPENC3_TOOLS_BUCKET'])
632
637
 
633
- when 'runmigrations'
634
- run_migrations(ARGV[1])
638
+ when 'runmigrations'
639
+ run_migrations(ARGV[1])
635
640
 
636
- else # Unknown task
637
- print_usage()
638
- abort("Unknown task: #{ARGV[0]}")
639
- end
640
-
641
- else # No arguments given
641
+ else # Unknown task
642
642
  print_usage()
643
+ abort("Unknown task: #{ARGV[0]}")
643
644
  end
644
645
 
645
- end
646
+ else # No arguments given
647
+ print_usage()
648
+ end
@@ -5,11 +5,13 @@ STATE:
5
5
  description: Key value pairs allow for user friendly strings. For example,
6
6
  you might define states for ON = 1 and OFF = 0. This allows the word ON to be
7
7
  used rather than the number 1 when sending the telemetry item and allows
8
- for much greater clarity and less chance for user error.
8
+ for much greater clarity and less chance for user error. A catch all value
9
+ of ANY applies to all other values not already defined as state values.
9
10
  example: |
10
11
  APPEND_ITEM ENABLE 32 UINT "Enable setting"
11
12
  STATE FALSE 0
12
13
  STATE TRUE 1
14
+ STATE ERROR ANY # Match all other values to ERROR
13
15
  APPEND_ITEM STRING 1024 STRING "String"
14
16
  STATE "NOOP" "NOOP" GREEN
15
17
  STATE "ARM LASER" "ARM LASER" YELLOW
@@ -21,7 +23,7 @@ STATE:
21
23
  values: .*
22
24
  - name: Value
23
25
  required: true
24
- description: The numerical state value
26
+ description: The numerical state value or ANY to apply the state to all other values
25
27
  values: .*
26
28
  - name: Color
27
29
  required: false
@@ -80,8 +80,8 @@ SELECT_TABLE:
80
80
  description: The name of the existin table
81
81
  values: .*
82
82
  DEFAULT:
83
- summary: Specify default values for a SINGLE row in a multi-column table.
84
- If you have multiple rows you need a DEFAULT line for each row.
83
+ summary: Specify default values for a SINGLE row in a multi-column table
84
+ description: If you have multiple rows you need a DEFAULT line for each row.
85
85
  If all your rows are identical consider using ERB as shown in the OpenC3 demo.
86
86
  parameters:
87
87
  - name: Default values
@@ -47,7 +47,7 @@ TOOL:
47
47
  values: .+
48
48
  CATEGORY:
49
49
  summary: Category for the tool
50
- description: Associates the tool with a category. In a future release this will be able to organize tools into submenus in the Navigation menu.
50
+ description: Associates the tool with a category which becomes a submenu in the Navigation menu.
51
51
  parameters:
52
52
  - name: Category Name
53
53
  required: true
@@ -41,6 +41,7 @@ static ID id_method_scan = 0;
41
41
  static ID id_method_strip = 0;
42
42
  static ID id_method_to_s = 0;
43
43
  static ID id_method_upcase = 0;
44
+ static ID id_method_parse_errors = 0;
44
45
 
45
46
  /*
46
47
  * Removes quotes from the given string if present.
@@ -103,6 +104,7 @@ static VALUE parse_loop(VALUE self, VALUE io, VALUE yield_non_keyword_lines, VAL
103
104
  volatile VALUE ivar_keyword = Qnil;
104
105
  volatile VALUE ivar_parameters = rb_ary_new();
105
106
  volatile VALUE ivar_line = rb_str_new2("");
107
+ volatile VALUE errors = rb_ary_new();
106
108
 
107
109
  rb_ivar_set(self, id_ivar_line_number, INT2FIX(0));
108
110
  rb_ivar_set(self, id_ivar_keyword, ivar_keyword);
@@ -205,7 +207,12 @@ static VALUE parse_loop(VALUE self, VALUE io, VALUE yield_non_keyword_lines, VAL
205
207
  rb_ary_clear(array);
206
208
  rb_ary_push(array, ivar_keyword);
207
209
  rb_ary_push(array, ivar_parameters);
208
- rb_yield(array);
210
+ line = rb_protect(rb_yield, array, &result);
211
+ if (result)
212
+ {
213
+ rb_ary_push(errors, rb_errinfo());
214
+ rb_set_errinfo(Qnil);
215
+ }
209
216
  }
210
217
  ivar_line = rb_str_new2("");
211
218
  rb_ivar_set(self, id_ivar_line, ivar_line);
@@ -247,11 +254,18 @@ static VALUE parse_loop(VALUE self, VALUE io, VALUE yield_non_keyword_lines, VAL
247
254
  rb_ary_clear(array);
248
255
  rb_ary_push(array, ivar_keyword);
249
256
  rb_ary_push(array, ivar_parameters);
250
- rb_yield(array);
257
+ line = rb_protect(rb_yield, array, &result);
258
+ if (result)
259
+ {
260
+ rb_ary_push(errors, rb_errinfo());
261
+ rb_set_errinfo(Qnil);
262
+ }
251
263
  ivar_line = rb_str_new2("");
252
264
  rb_ivar_set(self, id_ivar_line, ivar_line);
253
265
  }
254
266
 
267
+ rb_funcall(self, id_method_parse_errors, 1, errors);
268
+
255
269
  if (RTEST(progress_callback))
256
270
  {
257
271
  rb_funcall(progress_callback, id_method_call, 1, rb_float_new(1.0));
@@ -278,6 +292,7 @@ void Init_config_parser(void)
278
292
  id_method_strip = rb_intern("strip");
279
293
  id_method_to_s = rb_intern("to_s");
280
294
  id_method_upcase = rb_intern("upcase");
295
+ id_method_parse_errors = rb_intern("parse_errors");
281
296
 
282
297
  mOpenC3 = rb_define_module("OpenC3");
283
298
 
@@ -26,6 +26,7 @@ require 'openc3/api/cmd_api'
26
26
  require 'openc3/api/config_api'
27
27
  require 'openc3/api/interface_api'
28
28
  require 'openc3/api/limits_api'
29
+ require 'openc3/api/metrics_api'
29
30
  require 'openc3/api/offline_access_api'
30
31
  require 'openc3/api/router_api'
31
32
  require 'openc3/api/settings_api'
@@ -0,0 +1,97 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2023 OpenC3, Inc
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
+ # This file may also be used under the terms of a commercial license
17
+ # if purchased from OpenC3, Inc.
18
+
19
+ require 'openc3/models/metric_model'
20
+ require 'openc3/utilities/authentication'
21
+ require 'openc3/utilities/store'
22
+
23
+ module OpenC3
24
+ module Api
25
+ WHITELIST ||= []
26
+ WHITELIST.concat([
27
+ 'get_metrics',
28
+ ])
29
+
30
+ DELAY_METRICS = {}
31
+ DELAY_METRICS['decom_topic_delta_seconds'] = 0.0
32
+ DELAY_METRICS['interface_topic_delta_seconds'] = 0.0
33
+ DELAY_METRICS['log_topic_delta_seconds'] = 0.0
34
+ DELAY_METRICS['router_topic_delta_seconds'] = 0.0
35
+ DELAY_METRICS['text_log_topic_delta_seconds'] = 0.0
36
+
37
+ DURATION_METRICS = {}
38
+ DURATION_METRICS['decom_duration_seconds'] = 0.0
39
+ DURATION_METRICS['reducer_minute_processing_seconds'] = 0
40
+ DURATION_METRICS['reducer_hour_processing_seconds'] = 0
41
+ DURATION_METRICS['reducer_day_processing_seconds'] = 0
42
+
43
+ SUM_METRICS = {}
44
+ SUM_METRICS['cleanup_total'] = 0
45
+ SUM_METRICS['cleanup_delete_total'] = 0
46
+ SUM_METRICS['decom_total'] = 0
47
+ SUM_METRICS['decom_error_total'] = 0
48
+ SUM_METRICS['interface_cmd_total'] = 0
49
+ SUM_METRICS['interface_tlm_total'] = 0
50
+ SUM_METRICS['interface_directive_total'] = 0
51
+ SUM_METRICS['log_total'] = 0
52
+ SUM_METRICS['log_error_total'] = 0
53
+ SUM_METRICS['periodic_total'] = 0
54
+ SUM_METRICS['reducer_total'] = 0
55
+ SUM_METRICS['reducer_error_total'] = 0
56
+ SUM_METRICS['router_cmd_total'] = 0
57
+ SUM_METRICS['router_tlm_total'] = 0
58
+ SUM_METRICS['router_directive_total'] = 0
59
+ SUM_METRICS['text_log_total'] = 0
60
+ SUM_METRICS['text_log_error_total'] = 0
61
+
62
+ def get_metrics(scope: $openc3_scope, token: $openc3_token)
63
+ authorize(permission: 'system', scope: scope, token: token)
64
+
65
+ sum_metrics = SUM_METRICS.dup
66
+ duration_metrics = DURATION_METRICS.dup
67
+ delay_metrics = DELAY_METRICS.dup
68
+
69
+ metrics = MetricModel.all(scope: scope)
70
+ metrics.each do |microservice_name, metrics|
71
+ next unless metrics and metrics['values']
72
+ metrics['values'].each do |metric_name, data|
73
+ value = data['value']
74
+ if sum_metrics[metric_name]
75
+ sum_metrics[metric_name] += value
76
+ elsif duration_metrics[metric_name]
77
+ previous = duration_metrics[metric_name]
78
+ duration_metrics[metric_name] = value if value > previous
79
+ elsif delay_metrics.include?(metric_name)
80
+ previous = delay_metrics[metric_name]
81
+ delay_metrics[metric_name] = value if value > previous
82
+ else
83
+ # Ignore other metrics for now
84
+ end
85
+ end
86
+ end
87
+
88
+ result = delay_metrics
89
+ result.merge!(duration_metrics)
90
+ result.merge!(sum_metrics)
91
+
92
+ result.merge!(MetricModel.redis_metrics)
93
+
94
+ return result
95
+ end
96
+ end
97
+ end
@@ -118,7 +118,8 @@ module OpenC3
118
118
  # @param type [Symbol] Telemetry type, :RAW, :CONVERTED (default), :FORMATTED, or :WITH_UNITS
119
119
  def inject_tlm(target_name, packet_name, item_hash = nil, type: :CONVERTED, scope: $openc3_scope, token: $openc3_token)
120
120
  authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
121
- unless CvtModel::VALUE_TYPES.include?(type.intern)
121
+ type = type.to_s.intern
122
+ unless CvtModel::VALUE_TYPES.include?(type)
122
123
  raise "Unknown type '#{type}' for #{target_name} #{packet_name}"
123
124
  end
124
125
 
@@ -219,8 +219,6 @@ module OpenC3
219
219
  size,
220
220
  PARSING_REGEX,
221
221
  &block)
222
- rescue Exception => e # Catch EVERYTHING so we can re-raise with additional info
223
- raise e, "#{e}\n\nParsed output in #{file.path}", e.backtrace
224
222
  ensure
225
223
  file.close unless file.closed?
226
224
  end
@@ -446,6 +444,22 @@ module OpenC3
446
444
  value
447
445
  end
448
446
 
447
+ def parse_errors(errors)
448
+ return if errors.empty?
449
+ message = ''
450
+ errors.each do |error|
451
+ if error.is_a? OpenC3::ConfigParser::Error
452
+ message += "\n#{File.basename(error.filename)}:#{error.line_number}: #{error.line}"
453
+ message += "\nError: #{error.message}"
454
+ message += "\nUsage: #{error.usage}" unless error.usage.empty?
455
+ else
456
+ message += "\n#{error.message}"
457
+ end
458
+ message += "\n"
459
+ end
460
+ raise message
461
+ end
462
+
449
463
  if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
450
464
  # Iterates over each line of the io object and yields the keyword and parameters
451
465
  def parse_loop(io, yield_non_keyword_lines, remove_quotes, size, rx)
@@ -454,6 +468,7 @@ module OpenC3
454
468
  @keyword = nil
455
469
  @parameters = []
456
470
  @line = ''
471
+ errors = []
457
472
 
458
473
  while true
459
474
  @line_number += 1
@@ -516,7 +531,11 @@ module OpenC3
516
531
  # Ignore lines without keywords: comments and blank lines
517
532
  if @keyword.nil?
518
533
  if yield_non_keyword_lines
519
- yield(@keyword, @parameters)
534
+ begin
535
+ yield(@keyword, @parameters)
536
+ rescue => error
537
+ errors << error
538
+ end
520
539
  end
521
540
  @line = ''
522
541
  next
@@ -545,10 +564,16 @@ module OpenC3
545
564
  end
546
565
  end
547
566
 
548
- yield(@keyword, @parameters)
567
+ begin
568
+ yield(@keyword, @parameters)
569
+ rescue => error
570
+ errors << error
571
+ end
549
572
  @line = ''
550
573
  end
551
574
 
575
+ parse_errors(errors)
576
+
552
577
  @@progress_callback.call(1.0) if @@progress_callback
553
578
 
554
579
  return nil
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  require 'date'
@@ -58,6 +58,7 @@ class Time
58
58
  MINUTES_PER_HOUR = 60
59
59
  HOURS_PER_DAY = 24
60
60
  NSEC_PER_SECOND = 1_000_000_000
61
+ NSEC_PER_MSEC = 1_000_000
61
62
  USEC_PER_SECOND = USEC_PER_MSEC * MSEC_PER_SECOND
62
63
  MSEC_PER_MINUTE = 60 * MSEC_PER_SECOND
63
64
  MSEC_PER_HOUR = 60 * MSEC_PER_MINUTE
@@ -508,4 +509,8 @@ class Time
508
509
  nanoseconds = nsec_from_epoch % NSEC_PER_SECOND
509
510
  Time.at(seconds, nanoseconds, :nsec)
510
511
  end
512
+
513
+ def to_msec_from_epoch
514
+ (self.tv_sec * MSEC_PER_SECOND) + (self.tv_nsec / NSEC_PER_MSEC)
515
+ end
511
516
  end
@@ -27,6 +27,14 @@ require 'openc3/utilities/bucket_utilities'
27
27
 
28
28
  module OpenC3
29
29
  class CleanupMicroservice < Microservice
30
+ def initialize(*args)
31
+ super(*args)
32
+ @metric.set(name: 'cleanup_total', value: @count, type: 'counter')
33
+ @delete_count = 0
34
+ @metric.set(name: 'cleanup_delete_total', value: @delete_count, type: 'counter')
35
+ @sleeper = Sleeper.new
36
+ end
37
+
30
38
  def run
31
39
  split_name = @name.split("__")
32
40
  target_name = split_name[-1]
@@ -55,15 +63,23 @@ module OpenC3
55
63
  oldest_list.each_slice(1000) do |slice|
56
64
  bucket.delete_objects(bucket: ENV['OPENC3_LOGS_BUCKET'], keys: slice)
57
65
  @logger.info("Deleted #{slice.length} #{target_name} log files")
66
+ @delete_count += slice.length
67
+ @metric.set(name: 'cleanup_delete_total', value: @delete_count, type: 'counter')
58
68
  end
59
69
  end
60
70
  end
61
71
 
62
72
  @count += 1
73
+ @metric.set(name: 'cleanup_total', value: @count, type: 'counter')
63
74
  @state = 'SLEEPING'
64
- break if @microservice_sleeper.sleep(target.cleanup_poll_time)
75
+ break if @sleeper.sleep(target.cleanup_poll_time)
65
76
  end
66
77
  end
78
+
79
+ def shutdown
80
+ @sleeper.cancel
81
+ super()
82
+ end
67
83
  end
68
84
  end
69
85