openc3 7.0.1 → 7.1.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +47 -3
  3. data/data/config/item_modifiers.yaml +1 -1
  4. data/data/config/microservice.yaml +12 -1
  5. data/data/config/parameter_modifiers.yaml +49 -7
  6. data/data/config/target.yaml +11 -0
  7. data/data/config/target_config.yaml +6 -2
  8. data/lib/openc3/api/cmd_api.rb +2 -1
  9. data/lib/openc3/api/metrics_api.rb +11 -1
  10. data/lib/openc3/api/tlm_api.rb +21 -6
  11. data/lib/openc3/core_ext/faraday.rb +1 -1
  12. data/lib/openc3/io/json_api.rb +1 -1
  13. data/lib/openc3/logs/log_writer.rb +3 -1
  14. data/lib/openc3/microservices/decom_common.rb +128 -0
  15. data/lib/openc3/microservices/decom_microservice.rb +26 -95
  16. data/lib/openc3/microservices/interface_decom_common.rb +6 -2
  17. data/lib/openc3/microservices/interface_microservice.rb +10 -8
  18. data/lib/openc3/microservices/log_microservice.rb +1 -1
  19. data/lib/openc3/microservices/microservice.rb +3 -2
  20. data/lib/openc3/microservices/queue_microservice.rb +1 -1
  21. data/lib/openc3/microservices/scope_cleanup_microservice.rb +60 -46
  22. data/lib/openc3/microservices/text_log_microservice.rb +1 -2
  23. data/lib/openc3/models/cvt_model.rb +24 -13
  24. data/lib/openc3/models/db_sharded_model.rb +110 -0
  25. data/lib/openc3/models/interface_model.rb +9 -0
  26. data/lib/openc3/models/interface_status_model.rb +33 -3
  27. data/lib/openc3/models/metric_model.rb +96 -37
  28. data/lib/openc3/models/microservice_model.rb +7 -0
  29. data/lib/openc3/models/microservice_status_model.rb +30 -3
  30. data/lib/openc3/models/reingest_job_model.rb +153 -0
  31. data/lib/openc3/models/scope_model.rb +3 -2
  32. data/lib/openc3/models/script_status_model.rb +4 -20
  33. data/lib/openc3/models/target_model.rb +113 -100
  34. data/lib/openc3/packets/packet_config.rb +4 -1
  35. data/lib/openc3/script/script.rb +2 -2
  36. data/lib/openc3/script/script_runner.rb +4 -4
  37. data/lib/openc3/script/telemetry.rb +3 -3
  38. data/lib/openc3/script/web_socket_api.rb +29 -22
  39. data/lib/openc3/system/system.rb +20 -3
  40. data/lib/openc3/topics/command_decom_topic.rb +4 -2
  41. data/lib/openc3/topics/command_topic.rb +8 -5
  42. data/lib/openc3/topics/decom_interface_topic.rb +15 -10
  43. data/lib/openc3/topics/interface_topic.rb +71 -29
  44. data/lib/openc3/topics/limits_event_topic.rb +62 -41
  45. data/lib/openc3/topics/router_topic.rb +61 -21
  46. data/lib/openc3/topics/system_events_topic.rb +18 -1
  47. data/lib/openc3/topics/telemetry_decom_topic.rb +2 -1
  48. data/lib/openc3/topics/telemetry_topic.rb +4 -2
  49. data/lib/openc3/topics/topic.rb +77 -5
  50. data/lib/openc3/utilities/aws_bucket.rb +2 -0
  51. data/lib/openc3/utilities/cli_generator.rb +3 -2
  52. data/lib/openc3/utilities/metric.rb +15 -1
  53. data/lib/openc3/utilities/questdb_client.rb +173 -37
  54. data/lib/openc3/utilities/reingest_job.rb +377 -0
  55. data/lib/openc3/utilities/ruby_lex_utils.rb +2 -0
  56. data/lib/openc3/utilities/store_autoload.rb +78 -52
  57. data/lib/openc3/utilities/store_queued.rb +20 -12
  58. data/lib/openc3/version.rb +6 -6
  59. data/templates/plugin/plugin.gemspec +13 -1
  60. data/templates/tool_angular/package.json +2 -2
  61. data/templates/tool_react/package.json +1 -1
  62. data/templates/tool_svelte/package.json +1 -1
  63. data/templates/tool_vue/package.json +3 -3
  64. data/templates/tool_vue/src/router.js +2 -2
  65. data/templates/widget/package.json +2 -2
  66. metadata +7 -3
@@ -0,0 +1,153 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2026 OpenC3, Inc.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is distributed in the hope that it will be useful,
7
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
+ # See LICENSE.md for more details.
10
+ #
11
+ # This file may also be used under the terms of a commercial license
12
+ # if purchased from OpenC3, Inc.
13
+
14
+ require 'openc3/models/model'
15
+
16
+ module OpenC3
17
+ # Tracks one run of OpenC3::ReingestJob. The job updates this record from a
18
+ # background thread; the storage_controller status endpoint reads it.
19
+ # `updated_at` doubles as the heartbeat — if a Running record hasn't been
20
+ # touched in STALE_THRESHOLD_SEC, the status endpoint surfaces it as 'Stale'.
21
+ class ReingestJobModel < Model
22
+ PRIMARY_KEY = 'openc3_reingest_job'
23
+ STALE_THRESHOLD_SEC = 60
24
+
25
+ STATES = %w[Queued Running Complete Crashed Stale].freeze
26
+ PHASES = %w[downloading enabling_dedup ingesting dedup_cooldown disabling_dedup].freeze
27
+
28
+ attr_accessor :state
29
+ attr_accessor :files
30
+ attr_accessor :bucket
31
+ attr_accessor :path
32
+ attr_accessor :table_names
33
+ attr_accessor :target_version
34
+ attr_accessor :versions_used
35
+ attr_accessor :warnings
36
+ attr_accessor :progress_phase
37
+ attr_accessor :progress_current
38
+ attr_accessor :progress_total
39
+ attr_accessor :packets_written
40
+ attr_accessor :dedup_enabled_by_us
41
+ attr_accessor :dedup_preexisting
42
+ attr_accessor :dedup_disabled_tables
43
+ attr_accessor :dedup_cooldown_seconds
44
+ attr_accessor :dedup_enabled_at
45
+ attr_accessor :dedup_disabled_at
46
+ attr_accessor :error
47
+ attr_accessor :started_at
48
+ attr_accessor :finished_at
49
+
50
+ def self.get(name:, scope:)
51
+ super("#{scope}__#{PRIMARY_KEY}", name: name)
52
+ end
53
+
54
+ def self.names(scope:)
55
+ super("#{scope}__#{PRIMARY_KEY}")
56
+ end
57
+
58
+ def self.all(scope:)
59
+ all = super("#{scope}__#{PRIMARY_KEY}")
60
+ all.sort_by { |_key, value| value['updated_at'] }.reverse
61
+ end
62
+
63
+ def initialize(
64
+ name:,
65
+ state: 'Queued',
66
+ files: [],
67
+ bucket: nil,
68
+ path: nil,
69
+ table_names: [],
70
+ target_version: 'as_logged',
71
+ versions_used: [],
72
+ warnings: [],
73
+ progress_phase: nil,
74
+ progress_current: 0,
75
+ progress_total: 0,
76
+ packets_written: 0,
77
+ dedup_enabled_by_us: [],
78
+ dedup_preexisting: [],
79
+ dedup_disabled_tables: [],
80
+ dedup_cooldown_seconds: 60,
81
+ dedup_enabled_at: nil,
82
+ dedup_disabled_at: nil,
83
+ error: nil,
84
+ started_at: nil,
85
+ finished_at: nil,
86
+ updated_at: nil,
87
+ plugin: nil,
88
+ scope:
89
+ )
90
+ super("#{scope}__#{PRIMARY_KEY}", name: name, updated_at: updated_at, plugin: plugin, scope: scope)
91
+ @state = state
92
+ @files = files
93
+ @bucket = bucket
94
+ @path = path
95
+ @table_names = table_names
96
+ @target_version = target_version
97
+ @versions_used = versions_used
98
+ @warnings = warnings
99
+ @progress_phase = progress_phase
100
+ @progress_current = progress_current
101
+ @progress_total = progress_total
102
+ @packets_written = packets_written
103
+ @dedup_enabled_by_us = dedup_enabled_by_us
104
+ @dedup_preexisting = dedup_preexisting
105
+ @dedup_disabled_tables = dedup_disabled_tables
106
+ @dedup_cooldown_seconds = dedup_cooldown_seconds
107
+ @dedup_enabled_at = dedup_enabled_at
108
+ @dedup_disabled_at = dedup_disabled_at
109
+ @error = error
110
+ @started_at = started_at
111
+ @finished_at = finished_at
112
+ end
113
+
114
+ # True if state is Running but the heartbeat (updated_at) is older than
115
+ # STALE_THRESHOLD_SEC. Callers should surface state as 'Stale' in that case.
116
+ def stale?
117
+ return false unless @state == 'Running'
118
+ return false unless @updated_at
119
+ age_nsec = Time.now.to_nsec_from_epoch - @updated_at.to_i
120
+ age_nsec > STALE_THRESHOLD_SEC * 1_000_000_000
121
+ end
122
+
123
+ def as_json(*_a)
124
+ {
125
+ 'name' => @name,
126
+ 'state' => stale? ? 'Stale' : @state,
127
+ 'files' => @files,
128
+ 'bucket' => @bucket,
129
+ 'path' => @path,
130
+ 'table_names' => @table_names,
131
+ 'target_version' => @target_version,
132
+ 'versions_used' => @versions_used,
133
+ 'warnings' => @warnings,
134
+ 'progress_phase' => @progress_phase,
135
+ 'progress_current' => @progress_current,
136
+ 'progress_total' => @progress_total,
137
+ 'packets_written' => @packets_written,
138
+ 'dedup_enabled_by_us' => @dedup_enabled_by_us,
139
+ 'dedup_preexisting' => @dedup_preexisting,
140
+ 'dedup_disabled_tables' => @dedup_disabled_tables,
141
+ 'dedup_cooldown_seconds' => @dedup_cooldown_seconds,
142
+ 'dedup_enabled_at' => @dedup_enabled_at,
143
+ 'dedup_disabled_at' => @dedup_disabled_at,
144
+ 'error' => @error,
145
+ 'started_at' => @started_at,
146
+ 'finished_at' => @finished_at,
147
+ 'updated_at' => @updated_at,
148
+ 'plugin' => @plugin,
149
+ 'scope' => @scope,
150
+ }
151
+ end
152
+ end
153
+ end
@@ -389,8 +389,9 @@ module OpenC3
389
389
  end
390
390
 
391
391
  # Delete the topics we created for the scope
392
- Topic.del("#{@scope}__COMMAND__{UNKNOWN}__UNKNOWN")
393
- Topic.del("#{@scope}__TELEMETRY__{UNKNOWN}__UNKNOWN")
392
+ db_shard = Store.db_shard_for_target('UNKNOWN', scope: @scope)
393
+ Topic.del("#{@scope}__COMMAND__{UNKNOWN}__UNKNOWN", db_shard: db_shard)
394
+ Topic.del("#{@scope}__TELEMETRY__{UNKNOWN}__UNKNOWN", db_shard: db_shard)
394
395
  Topic.del("#{@scope}__openc3_targets")
395
396
  Topic.del("#{@scope}__CONFIG")
396
397
  end
@@ -66,17 +66,9 @@ module OpenC3
66
66
  keys = self.store.zrevrange("#{RUNNING_PRIMARY_KEY}__#{scope}__LIST", offset.to_i, offset.to_i + limit.to_i - 1)
67
67
  return [] if keys.empty?
68
68
  result = []
69
- if $openc3_redis_cluster
70
- # No pipelining for cluster mode
71
- # because it requires using the same shard for all keys
69
+ result = self.store.redis_pool.pipelined do
72
70
  keys.each do |key|
73
- result << self.store.hget("#{RUNNING_PRIMARY_KEY}__#{scope}", key)
74
- end
75
- else
76
- result = self.store.redis_pool.pipelined do
77
- keys.each do |key|
78
- self.store.hget("#{RUNNING_PRIMARY_KEY}__#{scope}", key)
79
- end
71
+ self.store.hget("#{RUNNING_PRIMARY_KEY}__#{scope}", key)
80
72
  end
81
73
  end
82
74
  result = result.map do |r|
@@ -91,17 +83,9 @@ module OpenC3
91
83
  keys = self.store.zrevrange("#{COMPLETED_PRIMARY_KEY}__#{scope}__LIST", offset.to_i, offset.to_i + limit.to_i - 1)
92
84
  return [] if keys.empty?
93
85
  result = []
94
- if $openc3_redis_cluster
95
- # No pipelining for cluster mode
96
- # because it requires using the same shard for all keys
86
+ result = self.store.redis_pool.pipelined do
97
87
  keys.each do |key|
98
- result << self.store.hget("#{COMPLETED_PRIMARY_KEY}__#{scope}", key)
99
- end
100
- else
101
- result = self.store.redis_pool.pipelined do
102
- keys.each do |key|
103
- self.store.hget("#{COMPLETED_PRIMARY_KEY}__#{scope}", key)
104
- end
88
+ self.store.hget("#{COMPLETED_PRIMARY_KEY}__#{scope}", key)
105
89
  end
106
90
  end
107
91
  result = result.map do |r|