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
@@ -20,27 +20,16 @@ require 'hiredis-client'
20
20
  require 'json'
21
21
  require 'connection_pool'
22
22
 
23
- if ENV['OPENC3_REDIS_CLUSTER']
24
- require 'openc3-enterprise/utilities/store'
25
- $openc3_redis_cluster = true
26
- else
27
- $openc3_redis_cluster = false
28
- end
29
-
30
23
  module OpenC3
31
24
  class StoreConnectionPool < ConnectionPool
32
25
  def pipelined
33
- if $openc3_redis_cluster
34
- yield # TODO: Update keys to support pipelining in cluster
35
- else
36
- with do |redis|
37
- redis.pipelined do |pipeline|
38
- Thread.current[:pipeline] = pipeline
39
- begin
40
- yield
41
- ensure
42
- Thread.current[:pipeline] = nil
43
- end
26
+ with do |redis|
27
+ redis.pipelined do |pipeline|
28
+ Thread.current[:pipeline] = pipeline
29
+ begin
30
+ yield
31
+ ensure
32
+ Thread.current[:pipeline] = nil
44
33
  end
45
34
  end
46
35
  end
@@ -57,8 +46,13 @@ module OpenC3
57
46
  end
58
47
 
59
48
  class Store
60
- # Variable that holds the singleton instance
61
- @instance = nil
49
+ # Variable that holds the singleton instances per db_shard
50
+ @instances = []
51
+
52
+ # DB_Shard cache: { "scope__target_name" => [db_shard_number, Time] }
53
+ @@db_shard_cache = {}
54
+ @@db_shard_cache_mutex = Mutex.new
55
+ DB_SHARD_CACHE_TIMEOUT = 60 # seconds
62
56
 
63
57
  # Mutex used to ensure that only one instance is created
64
58
  @@instance_mutex = Mutex.new
@@ -66,14 +60,48 @@ module OpenC3
66
60
  attr_reader :redis_url
67
61
  attr_reader :redis_pool
68
62
 
63
+ # Look up the db_shard number for a target with a 1-minute cache.
64
+ # Reads directly from Redis db_shard 0 to avoid circular deps with TargetModel.
65
+ # Non-target-specific data (nil target_name) always returns db_shard 0.
66
+ def self.db_shard_for_target(target_name, scope: "DEFAULT")
67
+ return 0 unless target_name
68
+
69
+ cache_key = "#{scope}__#{target_name}"
70
+ now = Time.now
71
+
72
+ @@db_shard_cache_mutex.synchronize do
73
+ cached = @@db_shard_cache[cache_key]
74
+ if cached
75
+ db_shard, cached_at = cached
76
+ return db_shard if (now - cached_at) < DB_SHARD_CACHE_TIMEOUT
77
+ end
78
+ end
79
+
80
+ begin
81
+ json = Store.instance(db_shard: 0).hget("#{scope}__openc3_targets", target_name)
82
+ db_shard = json ? JSON.parse(json)['db_shard'].to_i : 0
83
+ rescue
84
+ db_shard = 0
85
+ end
86
+
87
+ @@db_shard_cache_mutex.synchronize do
88
+ @@db_shard_cache[cache_key] = [db_shard, now]
89
+ end
90
+
91
+ db_shard
92
+ end
93
+
69
94
  # Get the singleton instance
70
- def self.instance(pool_size = 100)
95
+ def self.instance(pool_size = 100, db_shard: 0)
71
96
  # Logger.level = Logger::DEBUG
72
- return @instance if @instance
97
+ @instances ||= []
98
+ the_instance = @instances[db_shard]
99
+ return the_instance if the_instance
73
100
 
74
101
  @@instance_mutex.synchronize do
75
- @instance ||= self.new(pool_size)
76
- return @instance
102
+ @instances ||= []
103
+ @instances[db_shard] ||= self.new(pool_size, db_shard: db_shard)
104
+ return @instances[db_shard]
77
105
  end
78
106
  end
79
107
 
@@ -87,17 +115,16 @@ module OpenC3
87
115
  @redis_pool.with { |redis| redis.public_send(message, *args, **kwargs, &block) }
88
116
  end
89
117
 
90
- def initialize(pool_size = 10)
118
+ def initialize(pool_size = 10, db_shard: 0)
91
119
  @redis_username = ENV['OPENC3_REDIS_USERNAME']
92
120
  @redis_key = ENV['OPENC3_REDIS_PASSWORD']
93
- @redis_url = "redis://#{ENV['OPENC3_REDIS_HOSTNAME']}:#{ENV['OPENC3_REDIS_PORT']}"
121
+ hostname = ENV['OPENC3_REDIS_HOSTNAME'].to_s.gsub("SHARDNUM", db_shard.to_s)
122
+ @redis_url = "redis://#{hostname}:#{ENV.fetch('OPENC3_REDIS_PORT', 6379)}"
94
123
  @redis_pool = StoreConnectionPool.new(size: pool_size) { build_redis() }
95
124
  end
96
125
 
97
- unless $openc3_redis_cluster
98
- def build_redis
99
- return Redis.new(url: @redis_url, username: @redis_username, password: @redis_key)
100
- end
126
+ def build_redis
127
+ return Redis.new(url: @redis_url, username: @redis_username, password: @redis_key)
101
128
  end
102
129
 
103
130
  ###########################################################################
@@ -160,30 +187,28 @@ module OpenC3
160
187
  return offsets
161
188
  end
162
189
 
163
- unless $openc3_redis_cluster
164
- def read_topics(topics, offsets = nil, timeout_ms = 1000, count = nil)
165
- return {} if topics.empty?
166
- Thread.current[:topic_offsets] ||= {}
167
- topic_offsets = Thread.current[:topic_offsets]
168
- begin
169
- # Logger.debug "read_topics: #{topics}, #{offsets} pool:#{@redis_pool}"
170
- @redis_pool.with do |redis|
171
- offsets = update_topic_offsets(topics) unless offsets
172
- result = redis.xread(topics, offsets, block: timeout_ms, count: count)
173
- if result and result.length > 0
174
- result.each do |topic, messages|
175
- messages.each do |msg_id, msg_hash|
176
- topic_offsets[topic] = msg_id
177
- yield topic, msg_id, msg_hash, redis if block_given?
178
- end
190
+ def read_topics(topics, offsets = nil, timeout_ms = 1000, count = nil)
191
+ return {} if topics.empty?
192
+ Thread.current[:topic_offsets] ||= {}
193
+ topic_offsets = Thread.current[:topic_offsets]
194
+ begin
195
+ # Logger.debug "read_topics: #{topics}, #{offsets} pool:#{@redis_pool}"
196
+ @redis_pool.with do |redis|
197
+ offsets = update_topic_offsets(topics) unless offsets
198
+ result = redis.xread(topics, offsets, block: timeout_ms, count: count)
199
+ if result and result.length > 0
200
+ result.each do |topic, messages|
201
+ messages.each do |msg_id, msg_hash|
202
+ topic_offsets[topic] = msg_id
203
+ yield topic, msg_id, msg_hash, redis if block_given?
179
204
  end
180
205
  end
181
- # Logger.debug "result:#{result}" if result and result.length > 0
182
- return result
183
206
  end
184
- rescue Redis::TimeoutError
185
- return {} # Should return an empty hash not array - xread returns a hash
207
+ # Logger.debug "result:#{result}" if result and result.length > 0
208
+ return result
186
209
  end
210
+ rescue Redis::TimeoutError
211
+ return {} # Should return an empty hash not array - xread returns a hash
187
212
  end
188
213
  end
189
214
 
@@ -233,9 +258,10 @@ module OpenC3
233
258
  end
234
259
 
235
260
  class EphemeralStore < Store
236
- def initialize(pool_size = 10)
261
+ def initialize(pool_size = 10, db_shard: 0)
237
262
  super(pool_size)
238
- @redis_url = "redis://#{ENV['OPENC3_REDIS_EPHEMERAL_HOSTNAME']}:#{ENV['OPENC3_REDIS_EPHEMERAL_PORT']}"
263
+ hostname = ENV['OPENC3_REDIS_EPHEMERAL_HOSTNAME'].to_s.gsub("SHARDNUM", db_shard.to_s)
264
+ @redis_url = "redis://#{hostname}:#{ENV.fetch('OPENC3_REDIS_EPHEMERAL_PORT', 6380)}"
239
265
  @redis_pool = StoreConnectionPool.new(size: pool_size) { build_redis() }
240
266
  end
241
267
  end
@@ -18,30 +18,34 @@ module OpenC3
18
18
  class StoreQueued
19
19
  attr_reader :update_interval
20
20
 
21
- # Variable that holds the singleton instance
22
- @instance = nil
21
+ # Variable that holds the singleton instances per db_shard
22
+ @instances = []
23
23
 
24
24
  # Mutex used to ensure that only one instance is created
25
25
  @@instance_mutex = Mutex.new
26
26
 
27
- # Get the singleton instance
27
+ # Get the singleton instance for the given db_shard
28
28
  # Sets the update interval to 1 second by default
29
- def self.instance(update_interval = 1) # seconds
30
- return @instance if @instance
29
+ def self.instance(update_interval = 1, db_shard: 0) # seconds
30
+ @instances ||= []
31
+ the_instance = @instances[db_shard]
32
+ return the_instance if the_instance
31
33
 
32
34
  @@instance_mutex.synchronize do
33
- @instance ||= self.new(update_interval)
34
- return @instance
35
+ @instances ||= []
36
+ @instances[db_shard] ||= self.new(update_interval, db_shard: db_shard)
37
+ return @instances[db_shard]
35
38
  end
36
39
  end
37
40
 
38
- # Delegate all unknown class methods to delegate to the instance
41
+ # Delegate all unknown class methods to delegate to the instance (db_shard 0)
39
42
  def self.method_missing(message, *args, **kwargs, &)
40
43
  self.instance.public_send(message, *args, **kwargs, &)
41
44
  end
42
45
 
43
- def initialize(update_interval)
46
+ def initialize(update_interval, db_shard: 0)
44
47
  @update_interval = update_interval
48
+ @db_shard = db_shard
45
49
  @store = store_instance()
46
50
  # Queue to hold the store requests
47
51
  @store_queue = Queue.new
@@ -80,7 +84,11 @@ module OpenC3
80
84
  while true
81
85
  start_time = Time.now
82
86
 
83
- process_queue()
87
+ begin
88
+ process_queue()
89
+ rescue => e
90
+ puts "StoreQueued thread error (db_shard=#{@db_shard}):\n#{e.formatted}"
91
+ end
84
92
 
85
93
  # Only check whether to update at a set interval
86
94
  run_time = Time.now - start_time
@@ -107,7 +115,7 @@ module OpenC3
107
115
 
108
116
  # Returns the store we're working with
109
117
  def store_instance
110
- Store.instance
118
+ Store.instance(db_shard: @db_shard)
111
119
  end
112
120
 
113
121
  def graceful_kill
@@ -117,7 +125,7 @@ module OpenC3
117
125
 
118
126
  class EphemeralStoreQueued < StoreQueued
119
127
  def store_instance
120
- EphemeralStore.instance
128
+ EphemeralStore.instance(db_shard: @db_shard)
121
129
  end
122
130
  end
123
131
  end
@@ -1,14 +1,14 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- OPENC3_VERSION = '7.0.1'
3
+ OPENC3_VERSION = '7.1.0'
4
4
  module OpenC3
5
5
  module Version
6
6
  MAJOR = '7'
7
- MINOR = '0'
8
- PATCH = '1'
7
+ MINOR = '1'
8
+ PATCH = '0'
9
9
  OTHER = ''
10
- BUILD = '665de79cef884b8d470b08817fd4a1fb538fb187'
10
+ BUILD = '1074049d7a87d4b4d8cdc31e3512ab495b7492bb'
11
11
  end
12
- VERSION = '7.0.1'
13
- GEM_VERSION = '7.0.1'
12
+ VERSION = '7.1.0'
13
+ GEM_VERSION = '7.1.0'
14
14
  end
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.description = <<-EOF
8
8
  <%= plugin_name %> plugin for deployment to OpenC3
9
9
  EOF
10
- s.license = 'MIT'
10
+ s.licenses = 'MIT'
11
11
  s.authors = ['Anonymous']
12
12
  s.email = ['name@domain.com']
13
13
  s.homepage = 'https://github.com/OpenC3/cosmos'
@@ -21,4 +21,16 @@ Gem::Specification.new do |s|
21
21
  s.version = '0.0.0' + ".#{time}"
22
22
  end
23
23
  s.files = Dir.glob("{targets,lib,public,tools,microservices}/**/*") + %w(Rakefile README.md LICENSE.md plugin.txt)
24
+
25
+ s.metadata = {
26
+ # These fields are used when you submit your plugin to our App Store at store.openc3.com
27
+ # See this help page for more detail: https://store.openc3.com/help/guidelines
28
+ "source_code_uri" => "https://github.com/your-github/plugin-repo",
29
+ "openc3_store_title" => "<%= plugin_orig %>",
30
+ "openc3_store_description" => "Describe what your plugin does here.",
31
+ "openc3_store_keywords" => "some, comma-delimited, search terms",
32
+ "openc3_store_image" => "public/store_img.png",
33
+ "openc3_cosmos_minimum_version" => "6.0.0", # OPTIONAL
34
+ "openc3_store_access_type" => "public" # OPTIONAL
35
+ }
24
36
  end
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "<%= tool_name %>",
3
- "version": "7.0.1",
3
+ "version": "7.1.0",
4
4
  "scripts": {
5
5
  "ng": "ng",
6
6
  "start": "ng serve",
@@ -23,7 +23,7 @@
23
23
  "@angular/platform-browser-dynamic": "^18.2.6",
24
24
  "@angular/router": "^18.2.6",
25
25
  "@astrouxds/astro-web-components": "^7.24.0",
26
- "@openc3/js-common": "7.0.1",
26
+ "@openc3/js-common": "7.1.0",
27
27
  "rxjs": "~7.8.0",
28
28
  "single-spa": "^5.9.5",
29
29
  "single-spa-angular": "^9.2.0",
@@ -16,7 +16,7 @@
16
16
  "@emotion/react": "^11.13.3",
17
17
  "@emotion/styled": "^11.11.0",
18
18
  "@mui/material": "^6.1.1",
19
- "@openc3/js-common": "7.0.1",
19
+ "@openc3/js-common": "7.1.0",
20
20
  "react": "^18.2.0",
21
21
  "react-dom": "^18.2.0",
22
22
  "single-spa-react": "^5.1.4"
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@astrouxds/astro-web-components": "^7.24.0",
15
- "@openc3/js-common": "7.0.1",
15
+ "@openc3/js-common": "7.1.0",
16
16
  "@smui/button": "^7.0.0",
17
17
  "@smui/common": "^7.0.0",
18
18
  "@smui/card": "^7.0.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "<%= tool_name %>",
3
- "version": "7.0.1",
3
+ "version": "7.1.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -11,8 +11,8 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "@astrouxds/astro-web-components": "^7.24.0",
14
- "@openc3/js-common": "7.0.1",
15
- "@openc3/vue-common": "7.0.1",
14
+ "@openc3/js-common": "7.1.0",
15
+ "@openc3/vue-common": "7.1.0",
16
16
  "axios": "^1.7.7",
17
17
  "date-fns": "^4.1.0",
18
18
  "lodash": "^4.17.21",
@@ -1,5 +1,5 @@
1
1
  /*
2
- # Copyright 2023 OpenC3, Inc.
2
+ # Copyright 2026 OpenC3, Inc.
3
3
  # All Rights Reserved.
4
4
  #
5
5
  # This file may also be used under the terms of a commercial license
@@ -22,7 +22,7 @@ const routes = [
22
22
  component: NotFound,
23
23
  },
24
24
  ]
25
- routes.forEach(prependBasePath)
25
+ routes.forEach((route) => prependBasePath(route))
26
26
 
27
27
  export default createRouter({
28
28
  history: createWebHistory(),
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "<%= widget_name %>",
3
- "version": "7.0.1",
3
+ "version": "7.1.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "@astrouxds/astro-web-components": "^7.24.0",
11
- "@openc3/vue-common": "7.0.1",
11
+ "@openc3/vue-common": "7.1.0",
12
12
  "vuetify": "^3.7.1"
13
13
  },
14
14
  "devDependencies": {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openc3
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.1
4
+ version: 7.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Melton
@@ -184,14 +184,14 @@ dependencies:
184
184
  requirements:
185
185
  - - "~>"
186
186
  - !ruby/object:Gem::Version
187
- version: '7.0'
187
+ version: '8.0'
188
188
  type: :runtime
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - "~>"
193
193
  - !ruby/object:Gem::Version
194
- version: '7.0'
194
+ version: '8.0'
195
195
  - !ruby/object:Gem::Dependency
196
196
  name: rack
197
197
  requirement: !ruby/object:Gem::Requirement
@@ -1081,6 +1081,7 @@ files:
1081
1081
  - lib/openc3/logs/stream_log_pair.rb
1082
1082
  - lib/openc3/logs/text_log_writer.rb
1083
1083
  - lib/openc3/microservices/cleanup_microservice.rb
1084
+ - lib/openc3/microservices/decom_common.rb
1084
1085
  - lib/openc3/microservices/decom_microservice.rb
1085
1086
  - lib/openc3/microservices/interface_decom_common.rb
1086
1087
  - lib/openc3/microservices/interface_microservice.rb
@@ -1107,6 +1108,7 @@ files:
1107
1108
  - lib/openc3/models/activity_model.rb
1108
1109
  - lib/openc3/models/auth_model.rb
1109
1110
  - lib/openc3/models/cvt_model.rb
1111
+ - lib/openc3/models/db_sharded_model.rb
1110
1112
  - lib/openc3/models/environment_model.rb
1111
1113
  - lib/openc3/models/gem_model.rb
1112
1114
  - lib/openc3/models/info_model.rb
@@ -1128,6 +1130,7 @@ files:
1128
1130
  - lib/openc3/models/python_package_model.rb
1129
1131
  - lib/openc3/models/queue_model.rb
1130
1132
  - lib/openc3/models/reaction_model.rb
1133
+ - lib/openc3/models/reingest_job_model.rb
1131
1134
  - lib/openc3/models/router_model.rb
1132
1135
  - lib/openc3/models/router_status_model.rb
1133
1136
  - lib/openc3/models/scope_model.rb
@@ -1259,6 +1262,7 @@ files:
1259
1262
  - lib/openc3/utilities/quaternion.rb
1260
1263
  - lib/openc3/utilities/questdb_client.rb
1261
1264
  - lib/openc3/utilities/redis_secrets.rb
1265
+ - lib/openc3/utilities/reingest_job.rb
1262
1266
  - lib/openc3/utilities/ruby_lex_utils.rb
1263
1267
  - lib/openc3/utilities/running_script.rb
1264
1268
  - lib/openc3/utilities/s3_autoload.rb