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.
- checksums.yaml +4 -4
- data/bin/openc3cli +47 -3
- data/data/config/item_modifiers.yaml +1 -1
- data/data/config/microservice.yaml +12 -1
- data/data/config/parameter_modifiers.yaml +49 -7
- data/data/config/target.yaml +11 -0
- data/data/config/target_config.yaml +6 -2
- data/lib/openc3/api/cmd_api.rb +2 -1
- data/lib/openc3/api/metrics_api.rb +11 -1
- data/lib/openc3/api/tlm_api.rb +21 -6
- data/lib/openc3/core_ext/faraday.rb +1 -1
- data/lib/openc3/io/json_api.rb +1 -1
- data/lib/openc3/logs/log_writer.rb +3 -1
- data/lib/openc3/microservices/decom_common.rb +128 -0
- data/lib/openc3/microservices/decom_microservice.rb +26 -95
- data/lib/openc3/microservices/interface_decom_common.rb +6 -2
- data/lib/openc3/microservices/interface_microservice.rb +10 -8
- data/lib/openc3/microservices/log_microservice.rb +1 -1
- data/lib/openc3/microservices/microservice.rb +3 -2
- data/lib/openc3/microservices/queue_microservice.rb +1 -1
- data/lib/openc3/microservices/scope_cleanup_microservice.rb +60 -46
- data/lib/openc3/microservices/text_log_microservice.rb +1 -2
- data/lib/openc3/models/cvt_model.rb +24 -13
- data/lib/openc3/models/db_sharded_model.rb +110 -0
- data/lib/openc3/models/interface_model.rb +9 -0
- data/lib/openc3/models/interface_status_model.rb +33 -3
- data/lib/openc3/models/metric_model.rb +96 -37
- data/lib/openc3/models/microservice_model.rb +7 -0
- data/lib/openc3/models/microservice_status_model.rb +30 -3
- data/lib/openc3/models/reingest_job_model.rb +153 -0
- data/lib/openc3/models/scope_model.rb +3 -2
- data/lib/openc3/models/script_status_model.rb +4 -20
- data/lib/openc3/models/target_model.rb +113 -100
- data/lib/openc3/packets/packet_config.rb +4 -1
- data/lib/openc3/script/script.rb +2 -2
- data/lib/openc3/script/script_runner.rb +4 -4
- data/lib/openc3/script/telemetry.rb +3 -3
- data/lib/openc3/script/web_socket_api.rb +29 -22
- data/lib/openc3/system/system.rb +20 -3
- data/lib/openc3/topics/command_decom_topic.rb +4 -2
- data/lib/openc3/topics/command_topic.rb +8 -5
- data/lib/openc3/topics/decom_interface_topic.rb +15 -10
- data/lib/openc3/topics/interface_topic.rb +71 -29
- data/lib/openc3/topics/limits_event_topic.rb +62 -41
- data/lib/openc3/topics/router_topic.rb +61 -21
- data/lib/openc3/topics/system_events_topic.rb +18 -1
- data/lib/openc3/topics/telemetry_decom_topic.rb +2 -1
- data/lib/openc3/topics/telemetry_topic.rb +4 -2
- data/lib/openc3/topics/topic.rb +77 -5
- data/lib/openc3/utilities/aws_bucket.rb +2 -0
- data/lib/openc3/utilities/cli_generator.rb +3 -2
- data/lib/openc3/utilities/metric.rb +15 -1
- data/lib/openc3/utilities/questdb_client.rb +173 -37
- data/lib/openc3/utilities/reingest_job.rb +377 -0
- data/lib/openc3/utilities/ruby_lex_utils.rb +2 -0
- data/lib/openc3/utilities/store_autoload.rb +78 -52
- data/lib/openc3/utilities/store_queued.rb +20 -12
- data/lib/openc3/version.rb +6 -6
- data/templates/plugin/plugin.gemspec +13 -1
- data/templates/tool_angular/package.json +2 -2
- data/templates/tool_react/package.json +1 -1
- data/templates/tool_svelte/package.json +1 -1
- data/templates/tool_vue/package.json +3 -3
- data/templates/tool_vue/src/router.js +2 -2
- data/templates/widget/package.json +2 -2
- metadata +7 -3
|
@@ -21,6 +21,12 @@ module OpenC3
|
|
|
21
21
|
class RouterTopic < Topic
|
|
22
22
|
COMMAND_ACK_TIMEOUT_S = 30
|
|
23
23
|
|
|
24
|
+
# Look up db_shard from RouterModel
|
|
25
|
+
def self._db_shard_for_router(router_name, scope:)
|
|
26
|
+
json = Store.hget("#{scope}__openc3_routers", router_name)
|
|
27
|
+
json ? (JSON.parse(json, allow_nan: true, create_additions: true)['db_shard'] || 0).to_i : 0
|
|
28
|
+
end
|
|
29
|
+
|
|
24
30
|
# Generate a list of topics for this router. This includes the router itself
|
|
25
31
|
# and all the targets which are assigned to this router.
|
|
26
32
|
def self.topics(router, scope:)
|
|
@@ -34,15 +40,39 @@ module OpenC3
|
|
|
34
40
|
topics
|
|
35
41
|
end
|
|
36
42
|
|
|
37
|
-
def self.receive_telemetry(router, scope:)
|
|
43
|
+
def self.receive_telemetry(router, scope:, db_shard: 0)
|
|
44
|
+
db_shard = db_shard.to_i
|
|
45
|
+
router_cmd_topic = "{#{scope}__CMD}ROUTER__#{router.name}"
|
|
46
|
+
|
|
47
|
+
target_topics = []
|
|
48
|
+
router.tlm_target_names.each do |target_name|
|
|
49
|
+
System.telemetry.packets(target_name).each do |_packet_name, packet|
|
|
50
|
+
target_topics << "#{scope}__TELEMETRY__{#{packet.target_name}}__#{packet.packet_name}"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Group telemetry topics by db_shard; include router cmd topic on db_shard
|
|
55
|
+
db_shard_groups = Topic.group_topics_by_db_shard(target_topics, target_pattern: '__TELEMETRY__', scope: scope)
|
|
56
|
+
db_shard_groups[db_shard] ||= []
|
|
57
|
+
db_shard_groups[db_shard] << router_cmd_topic
|
|
58
|
+
|
|
59
|
+
all_same_db_shard = Topic.all_same_db_shard?(db_shard_groups)
|
|
60
|
+
|
|
38
61
|
while true
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
62
|
+
if all_same_db_shard
|
|
63
|
+
# Fast path: everything on one db_shard, single read
|
|
64
|
+
db_shard = db_shard_groups.keys.first || 0
|
|
65
|
+
Topic.read_topics(db_shard_groups[db_shard], db_shard: db_shard) do |topic, msg_id, msg_hash, redis|
|
|
66
|
+
result = yield topic, msg_id, msg_hash, redis
|
|
67
|
+
Topic.write_ack(topic, result, msg_id, db_shard: db_shard) if result and /CMD}ROUTER/.match?(topic)
|
|
68
|
+
end
|
|
69
|
+
else
|
|
70
|
+
timeout_per_db_shard = [1000 / [db_shard_groups.length, 1].max, 100].max
|
|
71
|
+
db_shard_groups.each do |db_shard, topics|
|
|
72
|
+
Topic.read_topics(topics, nil, timeout_per_db_shard, db_shard: db_shard) do |topic, msg_id, msg_hash, redis|
|
|
73
|
+
result = yield topic, msg_id, msg_hash, redis
|
|
74
|
+
Topic.write_ack(topic, result, msg_id, db_shard: db_shard) if result and /CMD}ROUTER/.match?(topic)
|
|
75
|
+
end
|
|
46
76
|
end
|
|
47
77
|
end
|
|
48
78
|
end
|
|
@@ -61,74 +91,84 @@ module OpenC3
|
|
|
61
91
|
end
|
|
62
92
|
|
|
63
93
|
def self.connect_router(router_name, *router_params, scope:)
|
|
94
|
+
db_shard = _db_shard_for_router(router_name, scope: scope)
|
|
64
95
|
if router_params && !router_params.empty?
|
|
65
|
-
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'connect' => 'true', 'params' => JSON.generate(router_params, allow_nan: true) }, '*', 100)
|
|
96
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'connect' => 'true', 'params' => JSON.generate(router_params, allow_nan: true) }, '*', 100, db_shard: db_shard)
|
|
66
97
|
else
|
|
67
|
-
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'connect' => 'true' }, '*', 100)
|
|
98
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'connect' => 'true' }, '*', 100, db_shard: db_shard)
|
|
68
99
|
end
|
|
69
100
|
end
|
|
70
101
|
|
|
71
102
|
def self.disconnect_router(router_name, scope:)
|
|
72
|
-
|
|
103
|
+
db_shard = _db_shard_for_router(router_name, scope: scope)
|
|
104
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'disconnect' => 'true' }, '*', 100, db_shard: db_shard)
|
|
73
105
|
end
|
|
74
106
|
|
|
75
107
|
def self.start_raw_logging(router_name, scope:)
|
|
76
|
-
|
|
108
|
+
db_shard = _db_shard_for_router(router_name, scope: scope)
|
|
109
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'log_stream' => 'true' }, '*', 100, db_shard: db_shard)
|
|
77
110
|
end
|
|
78
111
|
|
|
79
112
|
def self.stop_raw_logging(router_name, scope:)
|
|
80
|
-
|
|
113
|
+
db_shard = _db_shard_for_router(router_name, scope: scope)
|
|
114
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'log_stream' => 'false' }, '*', 100, db_shard: db_shard)
|
|
81
115
|
end
|
|
82
116
|
|
|
83
117
|
def self.shutdown(router, scope:)
|
|
84
|
-
|
|
118
|
+
db_shard = _db_shard_for_router(router.name, scope: scope)
|
|
119
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router.name}", { 'shutdown' => 'true' }, '*', 100, db_shard: db_shard)
|
|
85
120
|
end
|
|
86
121
|
|
|
87
122
|
def self.router_cmd(router_name, cmd_name, *cmd_params, scope:)
|
|
123
|
+
db_shard = _db_shard_for_router(router_name, scope: scope)
|
|
88
124
|
data = {}
|
|
89
125
|
data['cmd_name'] = cmd_name
|
|
90
126
|
data['cmd_params'] = cmd_params
|
|
91
|
-
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'router_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
|
|
127
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'router_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100, db_shard: db_shard)
|
|
92
128
|
end
|
|
93
129
|
|
|
94
130
|
def self.protocol_cmd(router_name, cmd_name, *cmd_params, read_write: :READ_WRITE, index: -1, scope:)
|
|
131
|
+
db_shard = _db_shard_for_router(router_name, scope: scope)
|
|
95
132
|
data = {}
|
|
96
133
|
data['cmd_name'] = cmd_name
|
|
97
134
|
data['cmd_params'] = cmd_params
|
|
98
135
|
data['read_write'] = read_write.to_s.upcase
|
|
99
136
|
data['index'] = index
|
|
100
|
-
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'protocol_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100)
|
|
137
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'protocol_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100, db_shard: db_shard)
|
|
101
138
|
end
|
|
102
139
|
|
|
103
140
|
def self.router_target_enable(router_name, target_name, cmd_only: false, tlm_only: false, scope:)
|
|
141
|
+
db_shard = _db_shard_for_router(router_name, scope: scope)
|
|
104
142
|
data = {}
|
|
105
143
|
data['target_name'] = target_name.to_s.upcase
|
|
106
144
|
data['cmd_only'] = cmd_only
|
|
107
145
|
data['tlm_only'] = tlm_only
|
|
108
146
|
data['action'] = 'enable'
|
|
109
|
-
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'target_control' => JSON.generate(data, allow_nan: true) }, '*', 100)
|
|
147
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'target_control' => JSON.generate(data, allow_nan: true) }, '*', 100, db_shard: db_shard)
|
|
110
148
|
end
|
|
111
149
|
|
|
112
150
|
def self.router_target_disable(router_name, target_name, cmd_only: false, tlm_only: false, scope:)
|
|
151
|
+
db_shard = _db_shard_for_router(router_name, scope: scope)
|
|
113
152
|
data = {}
|
|
114
153
|
data['target_name'] = target_name.to_s.upcase
|
|
115
154
|
data['cmd_only'] = cmd_only
|
|
116
155
|
data['tlm_only'] = tlm_only
|
|
117
156
|
data['action'] = 'disable'
|
|
118
|
-
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'target_control' => JSON.generate(data, allow_nan: true) }, '*', 100)
|
|
157
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'target_control' => JSON.generate(data, allow_nan: true) }, '*', 100, db_shard: db_shard)
|
|
119
158
|
end
|
|
120
159
|
|
|
121
160
|
def self.router_details(router_name, timeout: nil, scope:)
|
|
122
161
|
router_name = router_name.upcase
|
|
162
|
+
db_shard = _db_shard_for_router(router_name, scope: scope)
|
|
123
163
|
|
|
124
164
|
timeout = COMMAND_ACK_TIMEOUT_S unless timeout
|
|
125
165
|
ack_topic = "{#{scope}__ACKCMD}ROUTER__#{router_name}"
|
|
126
|
-
Topic.update_topic_offsets([ack_topic])
|
|
166
|
+
Topic.update_topic_offsets([ack_topic], db_shard: db_shard)
|
|
127
167
|
|
|
128
|
-
cmd_id = Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'router_details' => 'true' }, '*', 100)
|
|
168
|
+
cmd_id = Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'router_details' => 'true' }, '*', 100, db_shard: db_shard)
|
|
129
169
|
time = Time.now
|
|
130
170
|
while (Time.now - time) < timeout
|
|
131
|
-
Topic.read_topics([ack_topic]) do |_topic, _msg_id, msg_hash, _redis|
|
|
171
|
+
Topic.read_topics([ack_topic], db_shard: db_shard) do |_topic, _msg_id, msg_hash, _redis|
|
|
132
172
|
if msg_hash["id"] == cmd_id
|
|
133
173
|
return JSON.parse(msg_hash["result"], :allow_nan => true, :create_additions => true)
|
|
134
174
|
end
|
|
@@ -17,13 +17,30 @@ module OpenC3
|
|
|
17
17
|
class SystemEventsTopic < Topic
|
|
18
18
|
PRIMARY_KEY = "OPENC3__SYSTEM__EVENTS".freeze
|
|
19
19
|
|
|
20
|
+
# Collect all unique target db_shards from TargetModel
|
|
21
|
+
def self._active_db_shards
|
|
22
|
+
db_shards = Set.new([0])
|
|
23
|
+
# Iterate all scopes to find all target db_shards
|
|
24
|
+
Store.scan_each(match: '*__openc3_targets', type: 'hash') do |key|
|
|
25
|
+
Store.hgetall(key).each do |_name, json|
|
|
26
|
+
parsed = JSON.parse(json, allow_nan: true, create_additions: true)
|
|
27
|
+
db_shards << (parsed['db_shard'] || 0).to_i
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
db_shards
|
|
31
|
+
end
|
|
32
|
+
|
|
20
33
|
def self.update_topic_offsets()
|
|
21
34
|
Topic.update_topic_offsets([PRIMARY_KEY])
|
|
22
35
|
end
|
|
23
36
|
|
|
24
37
|
def self.write(type, event)
|
|
25
38
|
event['type'] = type
|
|
26
|
-
|
|
39
|
+
msg = {event: JSON.generate(event, allow_nan: true)}
|
|
40
|
+
# Write to all active db_shards so every interface microservice can read system events inline
|
|
41
|
+
_active_db_shards.each do |db_shard|
|
|
42
|
+
Topic.write_topic(PRIMARY_KEY, msg, '*', 1000, db_shard: db_shard)
|
|
43
|
+
end
|
|
27
44
|
end
|
|
28
45
|
|
|
29
46
|
def self.read()
|
|
@@ -43,7 +43,8 @@ module OpenC3
|
|
|
43
43
|
:received_count => packet.received_count,
|
|
44
44
|
:json_data => json_data,
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
db_shard = Store.db_shard_for_target(packet.target_name, scope: scope)
|
|
47
|
+
Topic.write_topic("#{scope}__DECOM__{#{packet.target_name}}__#{packet.packet_name}", msg_hash, id, db_shard: db_shard)
|
|
47
48
|
|
|
48
49
|
unless packet.stored
|
|
49
50
|
# Also update the current value table with the latest decommutated data
|
|
@@ -31,10 +31,12 @@ module OpenC3
|
|
|
31
31
|
:buffer => packet.buffer(false)
|
|
32
32
|
}
|
|
33
33
|
msg_hash[:extra] = JSON.generate(packet.extra.as_json, allow_nan: true) if packet.extra
|
|
34
|
+
topic = "#{scope}__TELEMETRY__{#{packet.target_name}}__#{packet.packet_name}"
|
|
35
|
+
db_shard = Store.db_shard_for_target(packet.target_name, scope: scope)
|
|
34
36
|
if queued
|
|
35
|
-
EphemeralStoreQueued.write_topic(
|
|
37
|
+
EphemeralStoreQueued.instance(db_shard: db_shard).write_topic(topic, msg_hash)
|
|
36
38
|
else
|
|
37
|
-
Topic.write_topic(
|
|
39
|
+
Topic.write_topic(topic, msg_hash, db_shard: db_shard)
|
|
38
40
|
end
|
|
39
41
|
end
|
|
40
42
|
end
|
data/lib/openc3/topics/topic.rb
CHANGED
|
@@ -19,18 +19,90 @@ require 'openc3/utilities/store'
|
|
|
19
19
|
|
|
20
20
|
module OpenC3
|
|
21
21
|
class Topic
|
|
22
|
-
# Delegate all unknown class methods to
|
|
22
|
+
# Delegate all unknown class methods to EphemeralStore db_shard 0 (system-level topics)
|
|
23
23
|
def self.method_missing(message, *args, **kwargs, &block)
|
|
24
24
|
EphemeralStore.public_send(message, *args, **kwargs, &block)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
def self.clear_topics(topics, maxlen = 0)
|
|
28
|
-
|
|
27
|
+
def self.clear_topics(topics, maxlen = 0, db_shard: 0)
|
|
28
|
+
store = EphemeralStore.instance(db_shard: db_shard)
|
|
29
|
+
topics.each { |topic| store.xtrim(topic, maxlen) }
|
|
29
30
|
end
|
|
30
31
|
|
|
31
|
-
def self.get_cnt(topic)
|
|
32
|
-
_, packet = EphemeralStore.get_newest_message(topic)
|
|
32
|
+
def self.get_cnt(topic, db_shard: 0)
|
|
33
|
+
_, packet = EphemeralStore.instance(db_shard: db_shard).get_newest_message(topic)
|
|
33
34
|
packet ? packet["received_count"].to_i : 0
|
|
34
35
|
end
|
|
36
|
+
|
|
37
|
+
# DB_Shard-aware topic methods for target-specific streams.
|
|
38
|
+
# These explicitly route to the correct EphemeralStore db_shard.
|
|
39
|
+
|
|
40
|
+
def self.write_topic(topic, msg_hash, id = '*', maxlen = nil, approximate = 'true', db_shard: 0)
|
|
41
|
+
EphemeralStore.instance(db_shard: db_shard).write_topic(topic, msg_hash, id, maxlen, approximate)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.read_topics(topics, offsets = nil, timeout_ms = 1000, count = nil, db_shard: 0, &block)
|
|
45
|
+
EphemeralStore.instance(db_shard: db_shard).read_topics(topics, offsets, timeout_ms, count, &block)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.get_newest_message(topic, db_shard: 0)
|
|
49
|
+
EphemeralStore.instance(db_shard: db_shard).get_newest_message(topic)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.get_oldest_message(topic, db_shard: 0)
|
|
53
|
+
EphemeralStore.instance(db_shard: db_shard).get_oldest_message(topic)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.get_last_offset(topic, db_shard: 0)
|
|
57
|
+
EphemeralStore.instance(db_shard: db_shard).get_last_offset(topic)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.update_topic_offsets(topics, db_shard: 0)
|
|
61
|
+
EphemeralStore.instance(db_shard: db_shard).update_topic_offsets(topics)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.trim_topic(topic, minid, approximate = true, limit: 0, db_shard: 0)
|
|
65
|
+
EphemeralStore.instance(db_shard: db_shard).trim_topic(topic, minid, approximate, limit: limit)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.del(topic, db_shard: 0)
|
|
69
|
+
EphemeralStore.instance(db_shard: db_shard).del(topic)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Group topics by db_shard. Each topic's target name is extracted and looked up.
|
|
73
|
+
# Topics matching target_pattern are db_sharded; others go to db_shard 0.
|
|
74
|
+
# @param topics [Array<String>] List of topic strings
|
|
75
|
+
# @param target_pattern [String] Substring to identify target-specific topics (e.g. 'CMD}TARGET__', '__TELEMETRY__')
|
|
76
|
+
# @param scope [String] Scope name for db_shard lookup
|
|
77
|
+
# @return [Hash] { db_shard => [topic, ...] }
|
|
78
|
+
def self.group_topics_by_db_shard(topics, target_pattern:, scope:)
|
|
79
|
+
groups = {}
|
|
80
|
+
topics.each do |topic|
|
|
81
|
+
if topic.include?(target_pattern)
|
|
82
|
+
target_name = topic.match(/__\{?([^}_]+)\}?__/)[1] rescue nil
|
|
83
|
+
# Handle CMD}TARGET__ pattern where target is after TARGET__
|
|
84
|
+
target_name = topic.split('TARGET__')[1] if target_pattern.include?('TARGET__') && target_name.nil?
|
|
85
|
+
db_shard = (Store.db_shard_for_target(target_name, scope: scope) || 0).to_i
|
|
86
|
+
else
|
|
87
|
+
db_shard = 0
|
|
88
|
+
end
|
|
89
|
+
groups[db_shard] ||= []
|
|
90
|
+
groups[db_shard] << topic
|
|
91
|
+
end
|
|
92
|
+
groups
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Check if all db_shard groups resolve to a single db_shard (fast path).
|
|
96
|
+
def self.all_same_db_shard?(db_shard_groups)
|
|
97
|
+
db_shard_groups.length <= 1
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Build the ACK topic from a command/router topic and write the ack.
|
|
101
|
+
def self.write_ack(topic, result, msg_id, db_shard: 0)
|
|
102
|
+
ack_topic = topic.split("__")
|
|
103
|
+
ack_topic[1] = 'ACK' + ack_topic[1]
|
|
104
|
+
ack_topic = ack_topic.join("__")
|
|
105
|
+
Topic.write_topic(ack_topic, { 'result' => result, 'id' => msg_id }, '*', 100, db_shard: db_shard)
|
|
106
|
+
end
|
|
35
107
|
end
|
|
36
108
|
end
|
|
@@ -177,6 +177,7 @@ module OpenC3
|
|
|
177
177
|
@client.put_bucket_policy(options)
|
|
178
178
|
rescue Aws::S3::Errors::NotImplemented, Aws::S3::Errors::ServiceError, Aws::S3::Errors::InternalError => e
|
|
179
179
|
Logger.warn("put_bucket_policy for #{config_bucket} not supported by S3 backend: #{e.message}")
|
|
180
|
+
Logger.warn("Policy applied:\n#{config_policy}")
|
|
180
181
|
end
|
|
181
182
|
|
|
182
183
|
begin
|
|
@@ -186,6 +187,7 @@ module OpenC3
|
|
|
186
187
|
@client.put_bucket_policy(options)
|
|
187
188
|
rescue Aws::S3::Errors::NotImplemented, Aws::S3::Errors::ServiceError, Aws::S3::Errors::InternalError => e
|
|
188
189
|
Logger.warn("put_bucket_policy for #{logs_bucket} not supported by S3 backend: #{e.message}")
|
|
190
|
+
Logger.warn("Policy applied:\n#{logs_policy}")
|
|
189
191
|
end
|
|
190
192
|
end
|
|
191
193
|
|
|
@@ -141,8 +141,9 @@ module OpenC3
|
|
|
141
141
|
abort("Usage: cli generate #{args[0]} <NAME> (--ruby or --python)")
|
|
142
142
|
end
|
|
143
143
|
|
|
144
|
-
# Create the local variables
|
|
145
|
-
|
|
144
|
+
# Create the local variables that are used in process_template below (see openc3/templates/plugin/plugin.gemspec as an example)
|
|
145
|
+
plugin_orig = args[1]
|
|
146
|
+
plugin = plugin_orig.downcase.gsub(/_+|-+/, '-')
|
|
146
147
|
plugin_name = "openc3-cosmos-#{plugin}"
|
|
147
148
|
if File.exist?(plugin_name)
|
|
148
149
|
abort("Plugin #{plugin_name} already exists!")
|
|
@@ -37,15 +37,28 @@ module OpenC3
|
|
|
37
37
|
|
|
38
38
|
attr_reader :microservice
|
|
39
39
|
attr_reader :scope
|
|
40
|
+
attr_reader :db_shard
|
|
40
41
|
attr_reader :data
|
|
41
42
|
attr_reader :mutex
|
|
42
43
|
|
|
43
|
-
def initialize(microservice:, scope:)
|
|
44
|
+
def initialize(microservice:, scope:, db_shard: nil)
|
|
44
45
|
@scope = scope
|
|
45
46
|
@microservice = microservice
|
|
46
47
|
@data = {}
|
|
47
48
|
@mutex = Mutex.new
|
|
48
49
|
|
|
50
|
+
if db_shard
|
|
51
|
+
@db_shard = db_shard
|
|
52
|
+
else
|
|
53
|
+
# Look up db_shard from MicroserviceModel
|
|
54
|
+
begin
|
|
55
|
+
json = Store.hget('openc3_microservices', microservice)
|
|
56
|
+
@db_shard = json ? JSON.parse(json)['db_shard'].to_i : 0
|
|
57
|
+
rescue
|
|
58
|
+
@db_shard = 0
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
49
62
|
# Always make sure there is a update thread
|
|
50
63
|
@@mutex.synchronize do
|
|
51
64
|
@@instances << self
|
|
@@ -90,6 +103,7 @@ module OpenC3
|
|
|
90
103
|
instance.mutex.synchronize do
|
|
91
104
|
json = {}
|
|
92
105
|
json['name'] = instance.microservice
|
|
106
|
+
json['db_shard'] = instance.db_shard
|
|
93
107
|
values = instance.data
|
|
94
108
|
json['values'] = values
|
|
95
109
|
MetricModel.set(json, scope: instance.scope) if values.length > 0
|