openc3 5.11.3 → 5.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -2
- data/bin/openc3cli +26 -12
- data/data/config/_id_items.yaml +6 -4
- data/data/config/_id_params.yaml +9 -6
- data/data/config/_items.yaml +6 -4
- data/data/config/_params.yaml +3 -2
- data/data/config/interface_modifiers.yaml +1 -1
- data/data/config/microservice.yaml +10 -1
- data/data/config/plugins.yaml +13 -3
- data/data/config/target.yaml +9 -0
- data/data/config/target_config.yaml +12 -0
- data/data/config/tool.yaml +12 -3
- data/lib/openc3/api/api.rb +1 -1
- data/lib/openc3/api/cmd_api.rb +24 -24
- data/lib/openc3/api/config_api.rb +12 -12
- data/lib/openc3/api/limits_api.rb +4 -3
- data/lib/openc3/api/settings_api.rb +5 -2
- data/lib/openc3/api/tlm_api.rb +7 -10
- data/lib/openc3/conversions/unix_time_conversion.rb +8 -6
- data/lib/openc3/interfaces/tcpip_server_interface.rb +0 -7
- data/lib/openc3/io/json_drb.rb +3 -2
- data/lib/openc3/io/json_rpc.rb +6 -6
- data/lib/openc3/logs/buffered_packet_log_writer.rb +4 -2
- data/lib/openc3/logs/packet_log_writer.rb +22 -7
- data/lib/openc3/microservices/cleanup_microservice.rb +8 -1
- data/lib/openc3/microservices/decom_microservice.rb +1 -1
- data/lib/openc3/microservices/interface_microservice.rb +2 -2
- data/lib/openc3/microservices/microservice.rb +5 -2
- data/lib/openc3/microservices/reaction_microservice.rb +1 -0
- data/lib/openc3/microservices/timeline_microservice.rb +7 -5
- data/lib/openc3/migrations/20231022000000_tlm_viewer_config.rb +22 -0
- data/lib/openc3/models/activity_model.rb +21 -3
- data/lib/openc3/models/cvt_model.rb +2 -1
- data/lib/openc3/models/gem_model.rb +4 -1
- data/lib/openc3/models/interface_model.rb +11 -5
- data/lib/openc3/models/metadata_model.rb +11 -0
- data/lib/openc3/models/microservice_model.rb +16 -3
- data/lib/openc3/models/model.rb +18 -0
- data/lib/openc3/models/note_model.rb +11 -0
- data/lib/openc3/models/plugin_model.rb +18 -0
- data/lib/openc3/models/python_package_model.rb +104 -0
- data/lib/openc3/models/scope_model.rb +2 -0
- data/lib/openc3/models/sorted_model.rb +17 -8
- data/lib/openc3/models/target_model.rb +53 -18
- data/lib/openc3/models/tool_config_model.rb +9 -3
- data/lib/openc3/models/tool_model.rb +22 -7
- data/lib/openc3/models/widget_model.rb +19 -3
- data/lib/openc3/operators/microservice_operator.rb +2 -0
- data/lib/openc3/packets/limits.rb +6 -18
- data/lib/openc3/packets/packet.rb +1 -0
- data/lib/openc3/packets/parsers/format_string_parser.rb +4 -4
- data/lib/openc3/packets/parsers/limits_parser.rb +4 -4
- data/lib/openc3/packets/parsers/limits_response_parser.rb +5 -5
- data/lib/openc3/packets/parsers/processor_parser.rb +4 -4
- data/lib/openc3/packets/parsers/state_parser.rb +3 -3
- data/lib/openc3/script/api_shared.rb +50 -32
- data/lib/openc3/script/calendar.rb +109 -0
- data/lib/openc3/script/commands.rb +1 -8
- data/lib/openc3/script/{gems.rb → packages.rb} +20 -16
- data/lib/openc3/script/script.rb +49 -38
- data/lib/openc3/system/system.rb +2 -0
- data/lib/openc3/system/target.rb +10 -1
- data/lib/openc3/top_level.rb +2 -2
- data/lib/openc3/utilities/aws_bucket.rb +3 -2
- data/lib/openc3/utilities/bucket_file_cache.rb +1 -1
- data/lib/openc3/utilities/local_mode.rb +3 -1
- data/lib/openc3/utilities/logger.rb +1 -1
- data/lib/openc3/utilities/ruby_lex_utils.rb +0 -8
- data/lib/openc3/version.rb +6 -6
- data/templates/tool_angular/package.json +14 -14
- data/templates/tool_angular/yarn.lock +282 -129
- data/templates/tool_react/package.json +11 -11
- data/templates/tool_react/yarn.lock +353 -300
- data/templates/tool_svelte/package.json +12 -12
- data/templates/tool_svelte/src/services/openc3-api.js +16 -60
- data/templates/tool_svelte/yarn.lock +338 -212
- data/templates/tool_vue/package.json +9 -9
- data/templates/tool_vue/yarn.lock +55 -41
- data/templates/widget/package.json +10 -10
- data/templates/widget/yarn.lock +59 -45
- metadata +36 -5
data/lib/openc3/api/tlm_api.rb
CHANGED
@@ -67,22 +67,19 @@ module OpenC3
|
|
67
67
|
# @param type [Symbol] Telemetry type, :RAW, :CONVERTED (default), :FORMATTED, or :WITH_UNITS
|
68
68
|
# @return [Object] The telemetry value formatted as requested
|
69
69
|
def tlm(*args, type: :CONVERTED, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
|
70
|
-
target_name, packet_name, item_name =
|
70
|
+
target_name, packet_name, item_name = _tlm_process_args(args, 'tlm', cache_timeout: cache_timeout, scope: scope)
|
71
71
|
authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
|
72
72
|
CvtModel.get_item(target_name, packet_name, item_name, type: type.intern, cache_timeout: cache_timeout, scope: scope)
|
73
73
|
end
|
74
74
|
|
75
|
-
# @deprecated Use tlm with type: :RAW
|
76
75
|
def tlm_raw(*args, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
|
77
76
|
tlm(*args, type: :RAW, cache_timeout: cache_timeout, scope: scope, token: token)
|
78
77
|
end
|
79
78
|
|
80
|
-
# @deprecated Use tlm with type: :FORMATTED
|
81
79
|
def tlm_formatted(*args, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
|
82
80
|
tlm(*args, type: :FORMATTED, cache_timeout: cache_timeout, scope: scope, token: token)
|
83
81
|
end
|
84
82
|
|
85
|
-
# @deprecated Use tlm with type: :WITH_UNITS
|
86
83
|
def tlm_with_units(*args, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
|
87
84
|
tlm(*args, type: :WITH_UNITS, cache_timeout: cache_timeout, scope: scope, token: token)
|
88
85
|
end
|
@@ -108,7 +105,7 @@ module OpenC3
|
|
108
105
|
# @param args [String|Array<String>] See the description for calling style
|
109
106
|
# @param type [Symbol] Telemetry type, :RAW, :CONVERTED (default), :FORMATTED, or :WITH_UNITS
|
110
107
|
def set_tlm(*args, type: :CONVERTED, scope: $openc3_scope, token: $openc3_token)
|
111
|
-
target_name, packet_name, item_name, value =
|
108
|
+
target_name, packet_name, item_name, value = _set_tlm_process_args(args, __method__, scope: scope)
|
112
109
|
authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
|
113
110
|
CvtModel.set_item(target_name, packet_name, item_name, value, type: type.intern, scope: scope)
|
114
111
|
end
|
@@ -169,7 +166,7 @@ module OpenC3
|
|
169
166
|
# description).
|
170
167
|
# @param type [Symbol] Telemetry type, :ALL (default), :RAW, :CONVERTED, :FORMATTED, :WITH_UNITS
|
171
168
|
def override_tlm(*args, type: :ALL, scope: $openc3_scope, token: $openc3_token)
|
172
|
-
target_name, packet_name, item_name, value =
|
169
|
+
target_name, packet_name, item_name, value = _set_tlm_process_args(args, __method__, scope: scope)
|
173
170
|
authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
|
174
171
|
CvtModel.override(target_name, packet_name, item_name, value, type: type.intern, scope: scope)
|
175
172
|
end
|
@@ -194,7 +191,7 @@ module OpenC3
|
|
194
191
|
# @param type [Symbol] Telemetry type, :ALL (default), :RAW, :CONVERTED, :FORMATTED, :WITH_UNITS
|
195
192
|
# Also takes :ALL which means to normalize all telemetry types
|
196
193
|
def normalize_tlm(*args, type: :ALL, scope: $openc3_scope, token: $openc3_token)
|
197
|
-
target_name, packet_name, item_name =
|
194
|
+
target_name, packet_name, item_name = _tlm_process_args(args, __method__, scope: scope)
|
198
195
|
authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
|
199
196
|
CvtModel.normalize(target_name, packet_name, item_name, type: type.intern, scope: scope)
|
200
197
|
end
|
@@ -389,7 +386,7 @@ module OpenC3
|
|
389
386
|
# Get the transmit counts for telemetry packets
|
390
387
|
#
|
391
388
|
# @param target_packets [Array<Array<String, String>>] Array of arrays containing target_name, packet_name
|
392
|
-
# @return [Numeric]
|
389
|
+
# @return [Array<Numeric>] Receive count for the telemetry packets
|
393
390
|
def get_tlm_cnts(target_packets, scope: $openc3_scope, token: $openc3_token)
|
394
391
|
authorize(permission: 'system', scope: scope, token: token)
|
395
392
|
counts = []
|
@@ -430,7 +427,7 @@ module OpenC3
|
|
430
427
|
return nil
|
431
428
|
end
|
432
429
|
|
433
|
-
def
|
430
|
+
def _tlm_process_args(args, method_name, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
|
434
431
|
case args.length
|
435
432
|
when 1
|
436
433
|
target_name, packet_name, item_name = extract_fields_from_tlm_text(args[0])
|
@@ -456,7 +453,7 @@ module OpenC3
|
|
456
453
|
return [target_name, packet_name, item_name]
|
457
454
|
end
|
458
455
|
|
459
|
-
def
|
456
|
+
def _set_tlm_process_args(args, method_name, scope: $openc3_scope, token: $openc3_token)
|
460
457
|
case args.length
|
461
458
|
when 1
|
462
459
|
target_name, packet_name, item_name, value = extract_fields_from_set_tlm_text(args[0])
|
@@ -31,30 +31,32 @@ module OpenC3
|
|
31
31
|
# represents the number of seconds since the UNIX time epoch
|
32
32
|
# @param microseconds_item_name [String] The telemetry item in the packet
|
33
33
|
# which represents microseconds
|
34
|
-
def initialize(seconds_item_name, microseconds_item_name = nil)
|
34
|
+
def initialize(seconds_item_name, microseconds_item_name = nil, seconds_type = 'RAW', microseconds_type = 'RAW')
|
35
35
|
super()
|
36
36
|
@seconds_item_name = seconds_item_name
|
37
37
|
@microseconds_item_name = microseconds_item_name
|
38
38
|
@converted_type = :RUBY_TIME
|
39
39
|
@converted_bit_size = 0
|
40
|
+
@seconds_type = seconds_type.to_sym
|
41
|
+
@microseconds_type = microseconds_type.to_sym
|
40
42
|
end
|
41
43
|
|
42
44
|
# @param (see Conversion#call)
|
43
45
|
# @return [Float] Packet time in seconds since UNIX epoch
|
44
46
|
def call(value, packet, buffer)
|
45
47
|
if @microseconds_item_name
|
46
|
-
return Time.at(packet.read(@seconds_item_name,
|
48
|
+
return Time.at(packet.read(@seconds_item_name, @seconds_type, buffer), packet.read(@microseconds_item_name, @microseconds_type, buffer)).sys
|
47
49
|
else
|
48
|
-
return Time.at(packet.read(@seconds_item_name,
|
50
|
+
return Time.at(packet.read(@seconds_item_name, @seconds_type, buffer), 0).sys
|
49
51
|
end
|
50
52
|
end
|
51
53
|
|
52
54
|
# @return [String] The name of the class followed by the time conversion
|
53
55
|
def to_s
|
54
56
|
if @microseconds_item_name
|
55
|
-
return "Time.at(packet.read('#{@seconds_item_name}',
|
57
|
+
return "Time.at(packet.read('#{@seconds_item_name}', :#{@seconds_type}, buffer), packet.read('#{@microseconds_item_name}', :#{@microseconds_type}, buffer)).sys"
|
56
58
|
else
|
57
|
-
return "Time.at(packet.read('#{@seconds_item_name}',
|
59
|
+
return "Time.at(packet.read('#{@seconds_item_name}', :#{@seconds_type}, buffer), 0).sys"
|
58
60
|
end
|
59
61
|
end
|
60
62
|
|
@@ -66,7 +68,7 @@ module OpenC3
|
|
66
68
|
|
67
69
|
def as_json(*a)
|
68
70
|
result = super(*a)
|
69
|
-
result['params'] = [@seconds_item_name, @microseconds_item_name]
|
71
|
+
result['params'] = [@seconds_item_name, @microseconds_item_name, @seconds_type, @microseconds_type]
|
70
72
|
result
|
71
73
|
end
|
72
74
|
end # class UnixTimeConversion
|
@@ -500,13 +500,6 @@ module OpenC3
|
|
500
500
|
write_to_clients(:write_raw, data) if data
|
501
501
|
end
|
502
502
|
|
503
|
-
def interface_disconnect(interface_info)
|
504
|
-
Logger.info "#{@name}: Tcpip server lost write connection to "\
|
505
|
-
"#{interface_info.hostname}(#{interface_info.host_ip}):#{interface_info.port}"
|
506
|
-
interface_info.interface.disconnect
|
507
|
-
interface_info.interface.stream_log_pair.stop if interface_info.interface.stream_log_pair
|
508
|
-
end
|
509
|
-
|
510
503
|
def write_thread_hook(packet)
|
511
504
|
packet # By default just return the packet
|
512
505
|
end
|
data/lib/openc3/io/json_drb.rb
CHANGED
@@ -28,7 +28,8 @@ require 'drb/drb'
|
|
28
28
|
require 'set'
|
29
29
|
require 'openc3/io/json_rpc'
|
30
30
|
require 'openc3/io/json_drb_rack'
|
31
|
-
require '
|
31
|
+
require 'rackup'
|
32
|
+
require 'puma'
|
32
33
|
|
33
34
|
# Add methods to the Puma::Launcher and Puma::Single class so we can tell
|
34
35
|
# if the server has been started.
|
@@ -150,7 +151,7 @@ module OpenC3
|
|
150
151
|
}
|
151
152
|
|
152
153
|
# The run call will block until the server is stopped.
|
153
|
-
|
154
|
+
Rackup::Handler::Puma.run(JsonDrbRack.new(self), server_config) do |server|
|
154
155
|
@server_mutex.synchronize do
|
155
156
|
@server = server
|
156
157
|
end
|
data/lib/openc3/io/json_rpc.rb
CHANGED
@@ -36,9 +36,9 @@ end
|
|
36
36
|
|
37
37
|
class Struct #:nodoc:
|
38
38
|
def as_json(options = nil)
|
39
|
-
|
40
|
-
self.each_pair { |k, v|
|
41
|
-
|
39
|
+
hash = {}
|
40
|
+
self.each_pair { |k, v| hash[k.to_s] = v.as_json(options) }
|
41
|
+
hash
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -115,9 +115,9 @@ end
|
|
115
115
|
|
116
116
|
class Hash
|
117
117
|
def as_json(options = nil) #:nodoc:
|
118
|
-
|
119
|
-
self.each {
|
120
|
-
|
118
|
+
hash = {}
|
119
|
+
self.each {|k,v| hash[k.to_s] = v.as_json(options) }
|
120
|
+
hash
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
@@ -44,7 +44,8 @@ module OpenC3
|
|
44
44
|
cycle_hour = nil,
|
45
45
|
cycle_minute = nil,
|
46
46
|
enforce_time_order = true,
|
47
|
-
buffer_depth = 60 # Default assumes 1 minute of 1Hz data
|
47
|
+
buffer_depth = 60, # Default assumes 1 minute of 1Hz data
|
48
|
+
scope: $openc3_scope
|
48
49
|
)
|
49
50
|
super(
|
50
51
|
remote_log_directory,
|
@@ -54,7 +55,8 @@ module OpenC3
|
|
54
55
|
cycle_size,
|
55
56
|
cycle_hour,
|
56
57
|
cycle_minute,
|
57
|
-
enforce_time_order
|
58
|
+
enforce_time_order,
|
59
|
+
scope: scope
|
58
60
|
)
|
59
61
|
@buffer_depth = Integer(buffer_depth)
|
60
62
|
@buffer = []
|
@@ -22,6 +22,7 @@
|
|
22
22
|
|
23
23
|
require 'openc3/logs/log_writer'
|
24
24
|
require 'openc3/logs/packet_log_constants'
|
25
|
+
require 'openc3/models/target_model'
|
25
26
|
require 'cbor'
|
26
27
|
|
27
28
|
module OpenC3
|
@@ -52,7 +53,8 @@ module OpenC3
|
|
52
53
|
cycle_size = 1_000_000_000,
|
53
54
|
cycle_hour = nil,
|
54
55
|
cycle_minute = nil,
|
55
|
-
enforce_time_order = true
|
56
|
+
enforce_time_order = true,
|
57
|
+
scope: $openc3_scope
|
56
58
|
)
|
57
59
|
super(
|
58
60
|
remote_log_directory,
|
@@ -73,6 +75,9 @@ module OpenC3
|
|
73
75
|
@target_indexes = {}
|
74
76
|
@next_target_index = 0
|
75
77
|
@data_format = :CBOR # Default to CBOR for improved compression
|
78
|
+
@target_id_cache = {}
|
79
|
+
@packet_id_cache = {}
|
80
|
+
@scope = scope
|
76
81
|
end
|
77
82
|
|
78
83
|
# Write a packet to the log file.
|
@@ -171,8 +176,14 @@ module OpenC3
|
|
171
176
|
@tlm_packet_table[target_name] = target_table
|
172
177
|
end
|
173
178
|
id = nil
|
174
|
-
|
175
|
-
|
179
|
+
unless ENV['OPENC3_NO_STORE']
|
180
|
+
id = @target_id_cache[target_name]
|
181
|
+
unless id
|
182
|
+
target = TargetModel.get(name: target_name, scope: @scope)
|
183
|
+
id = target["id"] if target
|
184
|
+
@target_id_cache[target_name] = id
|
185
|
+
end
|
186
|
+
end
|
176
187
|
write_entry(:TARGET_DECLARATION, cmd_or_tlm, target_name, packet_name, nil, nil, nil, id)
|
177
188
|
end
|
178
189
|
|
@@ -187,10 +198,14 @@ module OpenC3
|
|
187
198
|
|
188
199
|
id = nil
|
189
200
|
begin
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
id
|
201
|
+
unless ENV['OPENC3_NO_STORE']
|
202
|
+
cache_key = "#{cmd_or_tlm}__#{target_name}__#{packet_name}"
|
203
|
+
id = @packet_id_cache[cache_key]
|
204
|
+
unless id
|
205
|
+
target_model_packet = TargetModel.packet(target_name, packet_name, type: cmd_or_tlm, scope: @scope)
|
206
|
+
id = target_model_packet["config_name"] if target_model_packet
|
207
|
+
@packet_id_cache[cache_key] = id
|
208
|
+
end
|
194
209
|
end
|
195
210
|
rescue
|
196
211
|
# No packet def
|
@@ -49,7 +49,14 @@ module OpenC3
|
|
49
49
|
if oldest_list.length > 0
|
50
50
|
@state = 'DELETING_OBJECTS'
|
51
51
|
oldest_list.each_slice(1000) do |slice|
|
52
|
-
|
52
|
+
# The delete_objects function utilizes an MD5 hash when verifying the checksums, which is not
|
53
|
+
# FIPS compliant (https://github.com/aws/aws-sdk-ruby/issues/2645).
|
54
|
+
# delete_object does NOT require an MD5 hash and will work on FIPS compliant systems. It is
|
55
|
+
# probably less performant, but we can instead delete each item one at a time.
|
56
|
+
# bucket.delete_objects(bucket: ENV['OPENC3_LOGS_BUCKET'], keys: slice)
|
57
|
+
slice.each do |item|
|
58
|
+
bucket.delete_object(bucket: ENV['OPENC3_LOGS_BUCKET'], key: item)
|
59
|
+
end
|
53
60
|
@logger.debug("Cleanup deleted #{slice.length} log files")
|
54
61
|
@delete_count += slice.length
|
55
62
|
@metric.set(name: 'cleanup_delete_total', value: @delete_count, type: 'counter')
|
@@ -34,7 +34,7 @@ module OpenC3
|
|
34
34
|
# Should only be one target, but there might be multiple decom microservices for a given target
|
35
35
|
# First Decom microservice has no number in the name
|
36
36
|
if @name =~ /__DECOM__/
|
37
|
-
@topics << "#{scope}__DECOMINTERFACE__{#{@target_names[0]}}"
|
37
|
+
@topics << "#{@scope}__DECOMINTERFACE__{#{@target_names[0]}}"
|
38
38
|
end
|
39
39
|
Topic.update_topic_offsets(@topics)
|
40
40
|
System.telemetry.limits_change_callback = method(:limits_change_callback)
|
@@ -348,13 +348,13 @@ module OpenC3
|
|
348
348
|
def initialize(name)
|
349
349
|
@mutex = Mutex.new
|
350
350
|
super(name)
|
351
|
+
@interface_or_router = self.class.name.to_s.split("Microservice")[0].upcase.split("::")[-1]
|
351
352
|
if @interface_or_router == 'INTERFACE'
|
352
353
|
@metric.set(name: 'interface_tlm_total', value: @count, type: 'counter')
|
353
354
|
else
|
354
355
|
@metric.set(name: 'router_cmd_total', value: @count, type: 'counter')
|
355
356
|
end
|
356
357
|
|
357
|
-
@interface_or_router = self.class.name.to_s.split("Microservice")[0].upcase.split("::")[-1]
|
358
358
|
@scope = name.split("__")[0]
|
359
359
|
interface_name = name.split("__")[2]
|
360
360
|
if @interface_or_router == 'INTERFACE'
|
@@ -553,7 +553,7 @@ module OpenC3
|
|
553
553
|
unknown_packet.extra = packet.extra
|
554
554
|
packet = unknown_packet
|
555
555
|
json_hash = CvtModel.build_json_from_packet(packet)
|
556
|
-
CvtModel.set(json_hash, target_name: packet.target_name, packet_name: packet.packet_name, scope: scope)
|
556
|
+
CvtModel.set(json_hash, target_name: packet.target_name, packet_name: packet.packet_name, scope: @scope)
|
557
557
|
num_bytes_to_print = [UNKNOWN_BYTES_TO_PRINT, packet.length].min
|
558
558
|
data = packet.buffer(false)[0..(num_bytes_to_print - 1)]
|
559
559
|
prefix = data.each_byte.map { | byte | sprintf("%02X", byte) }.join()
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
|
16
16
|
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright
|
17
|
+
# All changes Copyright 2023, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -79,7 +79,7 @@ module OpenC3
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def initialize(name, is_plugin: false)
|
82
|
-
|
82
|
+
@shutdown_complete = false
|
83
83
|
raise "Microservice must be named" unless name
|
84
84
|
|
85
85
|
@name = name
|
@@ -120,6 +120,7 @@ module OpenC3
|
|
120
120
|
# Get configuration for any targets
|
121
121
|
@target_names = @config["target_names"]
|
122
122
|
@target_names ||= []
|
123
|
+
# NOTE: setup_targets doesn't do anything if @target_names is empty
|
123
124
|
System.setup_targets(@target_names, @temp_dir, scope: @scope) unless is_plugin
|
124
125
|
|
125
126
|
# Use at_exit to shutdown cleanly no matter how we die
|
@@ -199,6 +200,7 @@ module OpenC3
|
|
199
200
|
end
|
200
201
|
|
201
202
|
def shutdown
|
203
|
+
return if @shutdown_complete
|
202
204
|
@logger.info("Shutting down microservice: #{@name}")
|
203
205
|
@cancel_thread = true
|
204
206
|
@microservice_status_sleeper.cancel if @microservice_status_sleeper
|
@@ -206,6 +208,7 @@ module OpenC3
|
|
206
208
|
FileUtils.remove_entry(@temp_dir) if File.exist?(@temp_dir)
|
207
209
|
@metric.shutdown
|
208
210
|
@logger.info("Shutting down microservice complete: #{@name}")
|
211
|
+
@shutdown_complete = true
|
209
212
|
end
|
210
213
|
end
|
211
214
|
end
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
|
16
16
|
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright
|
17
|
+
# All changes Copyright 2023, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -43,6 +43,7 @@ module OpenC3
|
|
43
43
|
|
44
44
|
def get_token(username)
|
45
45
|
if ENV['OPENC3_API_CLIENT'].nil?
|
46
|
+
ENV['OPENC3_API_PASSWORD'] ||= ENV['OPENC3_SERVICE_PASSWORD']
|
46
47
|
return OpenC3Authentication.new().token
|
47
48
|
else
|
48
49
|
# Check for offline access token
|
@@ -125,7 +126,8 @@ module OpenC3
|
|
125
126
|
|
126
127
|
def clear_expired(activity)
|
127
128
|
begin
|
128
|
-
ActivityModel.range_destroy(name: @timeline_name, scope: @scope, min: activity.start, max: activity.stop)
|
129
|
+
num = ActivityModel.range_destroy(name: @timeline_name, scope: @scope, min: activity.start, max: activity.stop)
|
130
|
+
@logger.info "#{@timeline_name} clear_expired removed #{num} items from #{activity.start} to #{activity.stop}"
|
129
131
|
activity.add_event(status: 'completed')
|
130
132
|
rescue StandardError => e
|
131
133
|
@logger.error "#{@timeline_name} clear_expired failed > #{activity.as_json(:allow_nan => true)} #{e.message}"
|
@@ -231,15 +233,15 @@ module OpenC3
|
|
231
233
|
@logger.info "#{@timeline_name} timeine manager exiting"
|
232
234
|
end
|
233
235
|
|
234
|
-
# Add task to remove events older than 7
|
236
|
+
# Add task to remove events older than 7 days
|
235
237
|
def add_expire_activity
|
236
238
|
now = Time.now.to_i
|
237
239
|
@expire = now + 3_000
|
238
240
|
activity = ActivityModel.new(
|
239
241
|
name: @timeline_name,
|
240
242
|
scope: @scope,
|
241
|
-
start:
|
242
|
-
stop: (now -
|
243
|
+
start: 0,
|
244
|
+
stop: (now - 86_400 * 7),
|
243
245
|
kind: 'EXPIRE',
|
244
246
|
data: {}
|
245
247
|
)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'openc3/utilities/migration'
|
2
|
+
require 'openc3/models/tool_config_model'
|
3
|
+
|
4
|
+
module OpenC3
|
5
|
+
class TlmViewerConfig < Migration
|
6
|
+
def self.run
|
7
|
+
ScopeModel.names.each do |scope|
|
8
|
+
# Get all existing ToolConfigModels and change keys from tlm_viewer to telemetry_viewer
|
9
|
+
names = ToolConfigModel.list_configs('tlm_viewer')
|
10
|
+
names.each do |name|
|
11
|
+
config = ToolConfigModel.load_config('tlm_viewer', name)
|
12
|
+
ToolConfigModel.save_config('telemetry_viewer', name, config)
|
13
|
+
ToolConfigModel.delete_config('tlm_viewer', name)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
unless ENV['OPENC3_NO_MIGRATE']
|
21
|
+
OpenC3::TlmViewerConfig.run
|
22
|
+
end
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
|
16
16
|
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright
|
17
|
+
# All changes Copyright 2023, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -94,13 +94,31 @@ module OpenC3
|
|
94
94
|
# Remove one member from a sorted set.
|
95
95
|
# @return [Integer] count of the members removed
|
96
96
|
def self.destroy(name:, scope:, score:)
|
97
|
-
Store.zremrangebyscore("#{scope}#{PRIMARY_KEY}__#{name}", score, score)
|
97
|
+
result = Store.zremrangebyscore("#{scope}#{PRIMARY_KEY}__#{name}", score, score)
|
98
|
+
notification = {
|
99
|
+
# start / stop to match SortedModel
|
100
|
+
'data' => JSON.generate({'start' => score}),
|
101
|
+
'kind' => 'deleted',
|
102
|
+
'type' => 'activity',
|
103
|
+
'timeline' => name
|
104
|
+
}
|
105
|
+
TimelineTopic.write_activity(notification, scope: scope)
|
106
|
+
return result
|
98
107
|
end
|
99
108
|
|
100
109
|
# Remove members from min to max of the sorted set.
|
101
110
|
# @return [Integer] count of the members removed
|
102
111
|
def self.range_destroy(name:, scope:, min:, max:)
|
103
|
-
Store.zremrangebyscore("#{scope}#{PRIMARY_KEY}__#{name}", min, max)
|
112
|
+
result = Store.zremrangebyscore("#{scope}#{PRIMARY_KEY}__#{name}", min, max)
|
113
|
+
notification = {
|
114
|
+
# start / stop to match SortedModel
|
115
|
+
'data' => JSON.generate({'start' => min, 'stop' => max}),
|
116
|
+
'kind' => 'deleted',
|
117
|
+
'type' => 'activity',
|
118
|
+
'timeline' => name
|
119
|
+
}
|
120
|
+
TimelineTopic.write_activity(notification, scope: scope)
|
121
|
+
return result
|
104
122
|
end
|
105
123
|
|
106
124
|
# @return [ActivityModel] Model generated from the passed JSON
|
@@ -237,10 +237,11 @@ module OpenC3
|
|
237
237
|
end
|
238
238
|
|
239
239
|
tgt_pkt_key = "#{scope}__tlm__#{target_name}__#{packet_name}"
|
240
|
-
@@override_cache[tgt_pkt_key] = [Time.now, hash]
|
241
240
|
if hash.empty?
|
241
|
+
@@override_cache.delete(tgt_pkt_key)
|
242
242
|
Store.hdel("#{scope}__override__#{target_name}", packet_name)
|
243
243
|
else
|
244
|
+
@@override_cache[tgt_pkt_key] = [Time.now, hash]
|
244
245
|
Store.hset("#{scope}__override__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true)))
|
245
246
|
end
|
246
247
|
end
|
@@ -27,6 +27,7 @@ require 'rubygems'
|
|
27
27
|
require 'rubygems/uninstaller'
|
28
28
|
require 'tempfile'
|
29
29
|
require 'openc3/utilities/process_manager'
|
30
|
+
require 'openc3/api/api'
|
30
31
|
require 'pathname'
|
31
32
|
|
32
33
|
module OpenC3
|
@@ -35,6 +36,8 @@ module OpenC3
|
|
35
36
|
# and destroy to allow interaction with gem files from the PluginModel and
|
36
37
|
# the GemsController.
|
37
38
|
class GemModel
|
39
|
+
include Api
|
40
|
+
|
38
41
|
def self.names
|
39
42
|
result = Pathname.new("#{ENV['GEM_HOME']}/gems").children.select { |c| c.directory? }.collect { |p| File.basename(p) + '.gem' }
|
40
43
|
return result.sort
|
@@ -53,7 +56,7 @@ module OpenC3
|
|
53
56
|
FileUtils.cp(gem_file_path, "#{ENV['GEM_HOME']}/cache/#{File.basename(gem_file_path)}")
|
54
57
|
if gem_install
|
55
58
|
Logger.info "Installing gem: #{gem_filename}"
|
56
|
-
result = OpenC3::ProcessManager.instance.spawn(["ruby", "/openc3/bin/openc3cli", "geminstall", gem_filename, scope], "
|
59
|
+
result = OpenC3::ProcessManager.instance.spawn(["ruby", "/openc3/bin/openc3cli", "geminstall", gem_filename, scope], "package_install", gem_filename, Time.now + 3600.0, scope: scope)
|
57
60
|
return result.name
|
58
61
|
end
|
59
62
|
else
|
@@ -145,7 +145,13 @@ module OpenC3
|
|
145
145
|
unless @cmd
|
146
146
|
type = self.class._get_type
|
147
147
|
microservice_name = "#{@scope}__#{type}__#{@name}"
|
148
|
-
|
148
|
+
if config_params[0] and File.extname(config_params[0]) == '.py'
|
149
|
+
work_dir.sub!('openc3/lib', 'openc3/python')
|
150
|
+
@cmd = ["python", "#{type.downcase}_microservice.py", microservice_name]
|
151
|
+
else
|
152
|
+
# If there are no config_params we assume ruby
|
153
|
+
@cmd = ["ruby", "#{type.downcase}_microservice.rb", microservice_name]
|
154
|
+
end
|
149
155
|
end
|
150
156
|
@work_dir = work_dir
|
151
157
|
@ports = ports
|
@@ -417,7 +423,7 @@ module OpenC3
|
|
417
423
|
# Respawn the microservice
|
418
424
|
type = self.class._get_type
|
419
425
|
microservice_name = "#{@scope}__#{type}__#{@name}"
|
420
|
-
microservice = MicroserviceModel.get_model(name: microservice_name, scope: scope)
|
426
|
+
microservice = MicroserviceModel.get_model(name: microservice_name, scope: @scope)
|
421
427
|
microservice.target_names.delete(target_name) unless @target_names.include?(target_name)
|
422
428
|
microservice.update
|
423
429
|
end
|
@@ -432,11 +438,11 @@ module OpenC3
|
|
432
438
|
|
433
439
|
if unmap_old
|
434
440
|
# Remove from old interface
|
435
|
-
all_interfaces = InterfaceModel.all(scope: scope)
|
441
|
+
all_interfaces = InterfaceModel.all(scope: @scope)
|
436
442
|
old_interface = nil
|
437
443
|
all_interfaces.each do |old_interface_name, old_interface_details|
|
438
444
|
if old_interface_details['target_names'].include?(target_name)
|
439
|
-
old_interface = InterfaceModel.from_json(old_interface_details, scope: scope)
|
445
|
+
old_interface = InterfaceModel.from_json(old_interface_details, scope: @scope)
|
440
446
|
old_interface.unmap_target(target_name, cmd_only: cmd_only, tlm_only: tlm_only) if old_interface
|
441
447
|
end
|
442
448
|
end
|
@@ -451,7 +457,7 @@ module OpenC3
|
|
451
457
|
# Respawn the microservice
|
452
458
|
type = self.class._get_type
|
453
459
|
microservice_name = "#{@scope}__#{type}__#{@name}"
|
454
|
-
microservice = MicroserviceModel.get_model(name: microservice_name, scope: scope)
|
460
|
+
microservice = MicroserviceModel.get_model(name: microservice_name, scope: @scope)
|
455
461
|
microservice.target_names << target_name unless microservice.target_names.include?(target_name)
|
456
462
|
microservice.update
|
457
463
|
end
|
@@ -33,6 +33,17 @@ module OpenC3
|
|
33
33
|
"#{scope}#{PRIMARY_KEY}"
|
34
34
|
end
|
35
35
|
|
36
|
+
def self.notify(scope:, kind:, start:, stop: nil)
|
37
|
+
json = {'type' => METADATA_TYPE, 'start' => start}
|
38
|
+
json['stop'] = stop if stop
|
39
|
+
notification = {
|
40
|
+
'data' => JSON.generate(json),
|
41
|
+
'kind' => kind,
|
42
|
+
'type' => 'calendar',
|
43
|
+
}
|
44
|
+
CalendarTopic.write_entry(notification, scope: scope)
|
45
|
+
end
|
46
|
+
|
36
47
|
attr_reader :color, :metadata, :constraints, :type
|
37
48
|
|
38
49
|
# @param [Integer] start - Time metadata is active in seconds from Epoch
|
@@ -42,6 +42,7 @@ module OpenC3
|
|
42
42
|
attr_accessor :parent
|
43
43
|
attr_accessor :secrets
|
44
44
|
attr_accessor :prefix
|
45
|
+
attr_accessor :disable_erb
|
45
46
|
|
46
47
|
# NOTE: The following three class methods are used by the ModelController
|
47
48
|
# and are reimplemented to enable various Model class methods to work
|
@@ -101,6 +102,7 @@ module OpenC3
|
|
101
102
|
needs_dependencies: false,
|
102
103
|
secrets: [],
|
103
104
|
prefix: nil,
|
105
|
+
disable_erb: nil,
|
104
106
|
scope:
|
105
107
|
)
|
106
108
|
parts = name.split("__")
|
@@ -125,6 +127,7 @@ module OpenC3
|
|
125
127
|
@needs_dependencies = needs_dependencies
|
126
128
|
@secrets = secrets
|
127
129
|
@prefix = prefix
|
130
|
+
@disable_erb = disable_erb
|
128
131
|
@bucket = Bucket.getClient()
|
129
132
|
end
|
130
133
|
|
@@ -145,7 +148,8 @@ module OpenC3
|
|
145
148
|
'plugin' => @plugin,
|
146
149
|
'needs_dependencies' => @needs_dependencies,
|
147
150
|
'secrets' => @secrets.as_json(*a),
|
148
|
-
'prefix' => @prefix
|
151
|
+
'prefix' => @prefix,
|
152
|
+
'disable_erb' => @disable_erb
|
149
153
|
}
|
150
154
|
end
|
151
155
|
|
@@ -201,6 +205,12 @@ module OpenC3
|
|
201
205
|
when 'ROUTE_PREFIX'
|
202
206
|
parser.verify_num_parameters(1, 1, "#{keyword} <Route Prefix>")
|
203
207
|
@prefix = parameters[0]
|
208
|
+
when 'DISABLE_ERB'
|
209
|
+
# 0 to unlimited parameters
|
210
|
+
@disable_erb ||= []
|
211
|
+
if parameters
|
212
|
+
@disable_erb.concat(parameters)
|
213
|
+
end
|
204
214
|
else
|
205
215
|
raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Microservice: #{keyword} #{parameters.join(" ")}")
|
206
216
|
end
|
@@ -220,8 +230,11 @@ module OpenC3
|
|
220
230
|
|
221
231
|
# Load microservice files
|
222
232
|
data = File.read(filename, mode: "rb")
|
223
|
-
|
224
|
-
|
233
|
+
erb_disabled = check_disable_erb(filename)
|
234
|
+
unless erb_disabled
|
235
|
+
OpenC3.set_working_dir(File.dirname(filename)) do
|
236
|
+
data = ERB.new(data.comment_erb(), trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable? and File.basename(filename)[0] != '_'
|
237
|
+
end
|
225
238
|
end
|
226
239
|
unless validate_only
|
227
240
|
@bucket.put_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: key, body: data)
|