openc3 5.12.0 → 5.14.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of openc3 might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +3 -3
- data/bin/openc3cli +21 -18
- data/data/config/command_modifiers.yaml +53 -1
- data/data/config/graph_settings.yaml +1 -1
- data/data/config/item_modifiers.yaml +1 -2
- data/data/config/parameter_modifiers.yaml +13 -14
- data/data/config/screen.yaml +1 -2
- data/data/config/target_config.yaml +2 -6
- data/lib/openc3/accessors/accessor.rb +42 -29
- data/lib/openc3/accessors/binary_accessor.rb +11 -1
- data/lib/openc3/accessors/form_accessor.rb +11 -1
- data/lib/openc3/accessors/http_accessor.rb +38 -0
- data/lib/openc3/accessors/json_accessor.rb +15 -3
- data/lib/openc3/accessors/template_accessor.rb +150 -0
- data/lib/openc3/accessors/xml_accessor.rb +11 -1
- data/lib/openc3/accessors.rb +1 -0
- data/lib/openc3/api/cmd_api.rb +99 -35
- data/lib/openc3/api/limits_api.rb +3 -3
- data/lib/openc3/api/tlm_api.rb +70 -31
- data/lib/openc3/interfaces/interface.rb +9 -7
- data/lib/openc3/interfaces/mqtt_interface.rb +11 -9
- data/lib/openc3/interfaces/mqtt_stream_interface.rb +78 -0
- data/lib/openc3/interfaces/protocols/cmd_response_protocol.rb +116 -0
- data/lib/openc3/interfaces/tcpip_client_interface.rb +4 -0
- data/lib/openc3/interfaces/tcpip_server_interface.rb +5 -0
- data/lib/openc3/interfaces.rb +1 -1
- data/lib/openc3/logs/packet_log_reader.rb +2 -2
- data/lib/openc3/logs/text_log_writer.rb +3 -2
- data/lib/openc3/microservices/decom_microservice.rb +1 -0
- data/lib/openc3/microservices/interface_microservice.rb +10 -1
- data/lib/openc3/microservices/trigger_group_microservice.rb +2 -1
- data/lib/openc3/models/cvt_model.rb +16 -12
- data/lib/openc3/models/gem_model.rb +20 -3
- data/lib/openc3/models/microservice_model.rb +1 -1
- data/lib/openc3/models/plugin_model.rb +43 -5
- data/lib/openc3/models/target_model.rb +69 -8
- data/lib/openc3/packets/json_packet.rb +46 -15
- data/lib/openc3/packets/packet.rb +92 -4
- data/lib/openc3/packets/packet_config.rb +27 -2
- data/lib/openc3/packets/parsers/xtce_parser.rb +5 -1
- data/lib/openc3/script/api_shared.rb +42 -31
- data/lib/openc3/script/commands.rb +18 -12
- data/lib/openc3/script/limits.rb +1 -1
- data/lib/openc3/script/script.rb +6 -12
- data/lib/openc3/script/storage.rb +4 -4
- data/lib/openc3/script/web_socket_api.rb +2 -2
- data/lib/openc3/streams/mqtt_stream.rb +109 -0
- data/lib/openc3/streams/tcpip_socket_stream.rb +19 -0
- data/lib/openc3/system/system.rb +13 -1
- data/lib/openc3/utilities/cli_generator.rb +48 -21
- data/lib/openc3/utilities/local_mode.rb +3 -3
- data/lib/openc3/utilities/logger.rb +17 -16
- data/lib/openc3/utilities/process_manager.rb +1 -1
- data/lib/openc3/utilities/store_queued.rb +126 -0
- data/lib/openc3/version.rb +5 -5
- data/templates/conversion/conversion.py +28 -0
- data/templates/conversion/conversion.rb +1 -18
- data/templates/limits_response/response.py +37 -0
- data/templates/limits_response/response.rb +0 -17
- data/templates/microservice/microservices/TEMPLATE/microservice.py +54 -0
- data/templates/microservice/microservices/TEMPLATE/microservice.rb +0 -7
- data/templates/plugin/.gitignore +1 -0
- data/templates/plugin/plugin.gemspec +2 -2
- data/templates/target/targets/TARGET/lib/target.py +9 -0
- data/templates/target/targets/TARGET/procedures/procedure.py +3 -0
- data/templates/target/targets/TARGET/public/README.txt +1 -0
- data/templates/tool_angular/package.json +21 -20
- data/templates/tool_angular/yarn.lock +2287 -3171
- data/templates/tool_react/package.json +15 -15
- data/templates/tool_react/yarn.lock +716 -789
- data/templates/tool_svelte/package.json +16 -15
- data/templates/tool_svelte/src/services/openc3-api.js +17 -22
- data/templates/tool_svelte/yarn.lock +715 -620
- data/templates/tool_vue/package.json +16 -15
- data/templates/tool_vue/yarn.lock +149 -69
- data/templates/widget/package.json +15 -14
- data/templates/widget/yarn.lock +132 -63
- metadata +160 -148
- data/lib/openc3/io/openc3_snmp.rb +0 -61
@@ -198,14 +198,16 @@ module OpenC3
|
|
198
198
|
log_dont_log.upcase!
|
199
199
|
period = "#{period.to_f}s"
|
200
200
|
@scheduler.every period do
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
201
|
+
if connected?()
|
202
|
+
begin
|
203
|
+
if log_dont_log == 'DONT_LOG'
|
204
|
+
cmd(cmd_string, log_message: false)
|
205
|
+
else
|
206
|
+
cmd(cmd_string)
|
207
|
+
end
|
208
|
+
rescue Exception => err
|
209
|
+
Logger.error("Error sending periodic cmd(#{cmd_string}):\n#{err.formatted}")
|
206
210
|
end
|
207
|
-
rescue Exception => err
|
208
|
-
Logger.error("Error sending periodic cmd(#{cmd_string}):\n#{err.formatted}")
|
209
211
|
end
|
210
212
|
end
|
211
213
|
end
|
@@ -161,8 +161,8 @@ module OpenC3
|
|
161
161
|
end
|
162
162
|
|
163
163
|
def read
|
164
|
-
topic = @read_topics.shift
|
165
164
|
packet = super()
|
165
|
+
topic = @read_topics.shift
|
166
166
|
return nil unless packet
|
167
167
|
identified_packet = @read_packets_by_topic[topic]
|
168
168
|
if identified_packet
|
@@ -175,15 +175,17 @@ module OpenC3
|
|
175
175
|
end
|
176
176
|
|
177
177
|
def write(packet)
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
topics
|
182
|
-
|
183
|
-
|
178
|
+
@write_mutex.synchronize do
|
179
|
+
topics = packet.meta['TOPIC']
|
180
|
+
topics = packet.meta['TOPICS'] unless topics
|
181
|
+
if topics
|
182
|
+
topics.each do |topic|
|
183
|
+
@write_topics << topic
|
184
|
+
super(packet)
|
185
|
+
end
|
186
|
+
else
|
187
|
+
raise "Command packet #{packet.target_name} #{packet.packet_name} requires a META TOPIC or TOPICS"
|
184
188
|
end
|
185
|
-
else
|
186
|
-
raise "Command packet #{packet.target_name} #{packet.packet_name} requires a META TOPIC or TOPICS"
|
187
189
|
end
|
188
190
|
end
|
189
191
|
|
@@ -0,0 +1,78 @@
|
|
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/interfaces/stream_interface'
|
20
|
+
require 'openc3/streams/mqtt_stream'
|
21
|
+
|
22
|
+
module OpenC3
|
23
|
+
class MqttStreamInterface < StreamInterface
|
24
|
+
# @param hostname [String] MQTT server to connect to
|
25
|
+
# @param port [Integer] MQTT port
|
26
|
+
# @param ssl [Boolean] Use SSL true/false
|
27
|
+
def initialize(hostname, port = 1883, ssl = false, write_topic = nil, read_topic = nil, protocol_type = nil, *protocol_args)
|
28
|
+
super(protocol_type, protocol_args)
|
29
|
+
@hostname = hostname
|
30
|
+
@port = Integer(port)
|
31
|
+
@ssl = ConfigParser.handle_true_false(ssl)
|
32
|
+
@write_topic = ConfigParser.handle_nil(write_topic)
|
33
|
+
@read_topic = ConfigParser.handle_nil(read_topic)
|
34
|
+
@username = nil
|
35
|
+
@password = nil
|
36
|
+
@cert = nil
|
37
|
+
@key = nil
|
38
|
+
@ca_file = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# Creates a new {SerialStream} using the parameters passed in the constructor
|
42
|
+
def connect
|
43
|
+
@stream = MqttStream.new(@hostname, @port, @ssl, @write_topic, @read_topic)
|
44
|
+
@stream.username = @username if @username
|
45
|
+
@stream.password = @password if @password
|
46
|
+
@stream.cert = @cert if @cert
|
47
|
+
@stream.key = @key if @key
|
48
|
+
@stream.ca_file = @ca_file if @ca_file
|
49
|
+
super()
|
50
|
+
end
|
51
|
+
|
52
|
+
# Supported Options
|
53
|
+
# USERNAME - Username for Mqtt Server
|
54
|
+
# PASSWORD - Password for Mqtt Server
|
55
|
+
# CERT - Public Key for Client Cert Auth
|
56
|
+
# KEY - Private Key for Client Cert Auth
|
57
|
+
# CA_FILE - Certificate Authority for Client Cert Auth
|
58
|
+
# (see Interface#set_option)
|
59
|
+
def set_option(option_name, option_values)
|
60
|
+
super(option_name, option_values)
|
61
|
+
case option_name.upcase
|
62
|
+
when 'USERNAME'
|
63
|
+
@username = option_values[0]
|
64
|
+
when 'PASSWORD'
|
65
|
+
@password = option_values[0]
|
66
|
+
when 'CERT'
|
67
|
+
@cert = option_values[0]
|
68
|
+
when 'KEY'
|
69
|
+
@key = option_values[0]
|
70
|
+
when 'CA_FILE'
|
71
|
+
# CA_FILE must be given as a file
|
72
|
+
@ca_file = Tempfile.new('ca_file')
|
73
|
+
@ca_file.write(option_values[0])
|
74
|
+
@ca_file.close
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2024 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/config/config_parser'
|
20
|
+
require 'openc3/interfaces/protocols/protocol'
|
21
|
+
require 'thread' # For Queue
|
22
|
+
require 'timeout' # For Timeout::Error
|
23
|
+
|
24
|
+
module OpenC3
|
25
|
+
# Protocol that waits for a response for any commands with a defined response packet.
|
26
|
+
# The response packet is identified but not defined by the protocol.
|
27
|
+
class CmdResponseProtocol < Protocol
|
28
|
+
# @param response_timeout [Float] Number of seconds to wait before timing out
|
29
|
+
# when waiting for a response
|
30
|
+
# @param response_polling_period [Float] Number of seconds to wait between polling
|
31
|
+
# for a response
|
32
|
+
# @param raise_exceptions [String] Whether to raise exceptions when errors
|
33
|
+
# occur in the protocol like unexpected responses or response timeouts.
|
34
|
+
# @param allow_empty_data [true/false/nil] See Protocol#initialize
|
35
|
+
def initialize(
|
36
|
+
response_timeout = 5.0,
|
37
|
+
response_polling_period = 0.02,
|
38
|
+
raise_exceptions = false,
|
39
|
+
allow_empty_data = nil
|
40
|
+
)
|
41
|
+
super(allow_empty_data)
|
42
|
+
@response_timeout = ConfigParser.handle_nil(response_timeout)
|
43
|
+
@response_timeout = @response_timeout.to_f if @response_timeout
|
44
|
+
@response_polling_period = response_polling_period.to_f
|
45
|
+
@raise_exceptions = ConfigParser.handle_true_false(raise_exceptions)
|
46
|
+
@write_block_queue = Queue.new
|
47
|
+
@response_packet = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def connect_reset
|
51
|
+
super()
|
52
|
+
@write_block_queue.clear
|
53
|
+
end
|
54
|
+
|
55
|
+
def disconnect_reset
|
56
|
+
super()
|
57
|
+
@write_block_queue << nil # Unblock the write block queue
|
58
|
+
end
|
59
|
+
|
60
|
+
def read_packet(packet)
|
61
|
+
if @response_packet
|
62
|
+
# Grab the response packet specified in the command
|
63
|
+
result_packet = System.telemetry.packet(@response_packet[0], @response_packet[1]).clone
|
64
|
+
result_packet.buffer = packet.buffer
|
65
|
+
result_packet.received_time = nil
|
66
|
+
result_packet.stored = packet.stored
|
67
|
+
result_packet.extra = packet.extra
|
68
|
+
|
69
|
+
# Release the write
|
70
|
+
@write_block_queue << nil
|
71
|
+
|
72
|
+
# This returns the fully identified and defined packet
|
73
|
+
# Received time is handled by the interface microservice
|
74
|
+
return result_packet
|
75
|
+
else
|
76
|
+
return packet
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def write_packet(packet)
|
81
|
+
# Setup the response packet (if there is one)
|
82
|
+
# This primes waiting for the response in post_write_interface
|
83
|
+
@response_packet = packet.response
|
84
|
+
|
85
|
+
return packet
|
86
|
+
end
|
87
|
+
|
88
|
+
def post_write_interface(packet, data, extra = nil)
|
89
|
+
if @response_packet
|
90
|
+
if @response_timeout
|
91
|
+
response_timeout_time = Time.now + @response_timeout
|
92
|
+
else
|
93
|
+
response_timeout_time = nil
|
94
|
+
end
|
95
|
+
|
96
|
+
# Block the write until the response is received
|
97
|
+
begin
|
98
|
+
@write_block_queue.pop(true)
|
99
|
+
rescue
|
100
|
+
sleep(@response_polling_period)
|
101
|
+
retry if !response_timeout_time
|
102
|
+
retry if response_timeout_time and Time.now < response_timeout_time
|
103
|
+
handle_error("#{@interface ? @interface.name : ""}: Timeout waiting for response")
|
104
|
+
end
|
105
|
+
|
106
|
+
@response_packet = nil
|
107
|
+
end
|
108
|
+
return super(packet, data, extra)
|
109
|
+
end
|
110
|
+
|
111
|
+
def handle_error(msg)
|
112
|
+
Logger.error(msg)
|
113
|
+
raise msg if @raise_exceptions
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -391,6 +391,11 @@ module OpenC3
|
|
391
391
|
read_socket = socket if listen_read
|
392
392
|
stream = TcpipSocketStream.new(write_socket, read_socket, @write_timeout, @read_timeout)
|
393
393
|
|
394
|
+
# Pass down options to the stream
|
395
|
+
@options.each do |option_name, option_values|
|
396
|
+
stream.set_option(option_name, option_values)
|
397
|
+
end
|
398
|
+
|
394
399
|
interface = StreamInterface.new
|
395
400
|
interface.target_names = @target_names
|
396
401
|
interface.cmd_target_names = @cmd_target_names
|
data/lib/openc3/interfaces.rb
CHANGED
@@ -42,7 +42,7 @@ module OpenC3
|
|
42
42
|
autoload(:PreidentifiedProtocol, 'openc3/interfaces/protocols/preidentified_protocol.rb')
|
43
43
|
autoload(:TemplateProtocol, 'openc3/interfaces/protocols/template_protocol.rb')
|
44
44
|
autoload(:TerminatedProtocol, 'openc3/interfaces/protocols/terminated_protocol.rb')
|
45
|
-
|
45
|
+
autoload(:CmdResponseProtocol, 'openc3/interfaces/protocols/cmd_response_protocol.rb')
|
46
46
|
autoload(:CrcProtocol, 'openc3/interfaces/protocols/crc_protocol.rb')
|
47
47
|
autoload(:IgnorePacketProtocol, 'openc3/interfaces/protocols/ignore_packet_protocol.rb')
|
48
48
|
end
|
@@ -298,12 +298,12 @@ module OpenC3
|
|
298
298
|
next_offset = 12
|
299
299
|
received_time_nsec_since_epoch = time_nsec_since_epoch
|
300
300
|
if includes_received_time
|
301
|
-
received_time_nsec_since_epoch = entry[next_offset..(next_offset + OPENC3_RECEIVED_TIME_FIXED_SIZE - 1)].unpack(OPENC3_RECEIVED_TIME_PACK_DIRECTIVE)
|
301
|
+
received_time_nsec_since_epoch = entry[next_offset..(next_offset + OPENC3_RECEIVED_TIME_FIXED_SIZE - 1)].unpack(OPENC3_RECEIVED_TIME_PACK_DIRECTIVE)[0]
|
302
302
|
next_offset += OPENC3_RECEIVED_TIME_FIXED_SIZE
|
303
303
|
end
|
304
304
|
extra = nil
|
305
305
|
if includes_extra
|
306
|
-
extra_length = entry[next_offset..(next_offset + OPENC3_EXTRA_LENGTH_FIXED_SIZE - 1)].unpack(OPENC3_EXTRA_LENGTH_PACK_DIRECTIVE)
|
306
|
+
extra_length = entry[next_offset..(next_offset + OPENC3_EXTRA_LENGTH_FIXED_SIZE - 1)].unpack(OPENC3_EXTRA_LENGTH_PACK_DIRECTIVE)[0]
|
307
307
|
next_offset += OPENC3_EXTRA_LENGTH_FIXED_SIZE
|
308
308
|
extra_encoded = entry[next_offset..(next_offset + extra_length - 1)]
|
309
309
|
next_offset += extra_length
|
@@ -78,8 +78,9 @@ module OpenC3
|
|
78
78
|
begin
|
79
79
|
# Need to write the OFFSET_MARKER for each packet
|
80
80
|
@last_offsets.each do |redis_topic, last_offset|
|
81
|
-
time = Time.now
|
82
|
-
|
81
|
+
time = Time.now.utc
|
82
|
+
# timestamp iso8601 with 6 decimal places to match the python output format
|
83
|
+
data = { time: time.to_nsec_from_epoch, '@timestamp' => time.iso8601(6), level: 'INFO', "microservice_name" => Logger.microservice_name, "container_name" => @container_name, "last_offset" => last_offset, "redis_topic" => redis_topic, "type" => "offset" }
|
83
84
|
write_entry(time.to_nsec_from_epoch, data.as_json(allow_nan: true).to_json(allow_nan: true)) if @file
|
84
85
|
end
|
85
86
|
|
@@ -100,6 +100,7 @@ module OpenC3
|
|
100
100
|
packet.extra = extra
|
101
101
|
end
|
102
102
|
packet.buffer = msg_hash["buffer"]
|
103
|
+
packet.process # Run processors
|
103
104
|
packet.check_limits(System.limits_set) # Process all the limits and call the limits_change_callback (as necessary)
|
104
105
|
|
105
106
|
TelemetryDecomTopic.write_packet(packet, scope: @scope)
|
@@ -613,7 +613,16 @@ module OpenC3
|
|
613
613
|
|
614
614
|
def connect
|
615
615
|
@logger.info "#{@interface.name}: Connecting ..."
|
616
|
-
|
616
|
+
begin
|
617
|
+
@interface.connect
|
618
|
+
rescue Exception => error
|
619
|
+
begin
|
620
|
+
@interface.disconnect # Ensure disconnect is called at least once on a partial connect
|
621
|
+
rescue Exception
|
622
|
+
# We want to report any connect errors, not disconnect in this case
|
623
|
+
end
|
624
|
+
raise error
|
625
|
+
end
|
617
626
|
@interface.state = 'CONNECTED'
|
618
627
|
if @interface_or_router == 'INTERFACE'
|
619
628
|
InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
|
@@ -333,7 +333,8 @@ module OpenC3
|
|
333
333
|
)
|
334
334
|
return nil if packet.nil?
|
335
335
|
_, limit = packet.read_with_limits_state(operand[ITEM_TYPE], operand[ITEM_VALUE_TYPE].intern)
|
336
|
-
|
336
|
+
# Convert limit symbol to string since we'll be comparing with strings
|
337
|
+
return limit.to_s
|
337
338
|
end
|
338
339
|
|
339
340
|
# extract the value outlined in the operand to get the packet item value
|
@@ -52,12 +52,14 @@ module OpenC3
|
|
52
52
|
|
53
53
|
# Get the hash for packet in the CVT
|
54
54
|
# Note: Does not apply overrides
|
55
|
-
def self.get(target_name:, packet_name:, cache_timeout:
|
55
|
+
def self.get(target_name:, packet_name:, cache_timeout: nil, scope: $openc3_scope)
|
56
56
|
key = "#{scope}__tlm__#{target_name}"
|
57
57
|
tgt_pkt_key = key + "__#{packet_name}"
|
58
|
-
cache_time, hash = @@packet_cache[tgt_pkt_key]
|
59
58
|
now = Time.now
|
60
|
-
|
59
|
+
if cache_timeout
|
60
|
+
cache_time, hash = @@packet_cache[tgt_pkt_key]
|
61
|
+
return hash if hash and (now - cache_time) < cache_timeout
|
62
|
+
end
|
61
63
|
packet = Store.hget(key, packet_name)
|
62
64
|
raise "Packet '#{target_name} #{packet_name}' does not exist" unless packet
|
63
65
|
hash = JSON.parse(packet, :allow_nan => true, :create_additions => true)
|
@@ -67,7 +69,7 @@ module OpenC3
|
|
67
69
|
|
68
70
|
# Set an item in the current value table
|
69
71
|
def self.set_item(target_name, packet_name, item_name, value, type:, scope: $openc3_scope)
|
70
|
-
hash = get(target_name: target_name, packet_name: packet_name, cache_timeout:
|
72
|
+
hash = get(target_name: target_name, packet_name: packet_name, cache_timeout: nil, scope: scope)
|
71
73
|
case type
|
72
74
|
when :WITH_UNITS
|
73
75
|
hash["#{item_name}__U"] = value.to_s # WITH_UNITS should always be a string
|
@@ -89,10 +91,10 @@ module OpenC3
|
|
89
91
|
end
|
90
92
|
|
91
93
|
# Get an item from the current value table
|
92
|
-
def self.get_item(target_name, packet_name, item_name, type:, cache_timeout:
|
94
|
+
def self.get_item(target_name, packet_name, item_name, type:, cache_timeout: nil, scope: $openc3_scope)
|
93
95
|
result, types = self._handle_item_override(target_name, packet_name, item_name, type: type, cache_timeout: cache_timeout, scope: scope)
|
94
96
|
return result if result
|
95
|
-
hash = get(target_name: target_name, packet_name: packet_name, scope: scope)
|
97
|
+
hash = get(target_name: target_name, packet_name: packet_name, cache_timeout: cache_timeout, scope: scope)
|
96
98
|
hash.values_at(*types).each do |result|
|
97
99
|
if result
|
98
100
|
if type == :FORMATTED or type == :WITH_UNITS
|
@@ -109,7 +111,7 @@ module OpenC3
|
|
109
111
|
# @param items [Array<String>] Items to return. Must be formatted as TGT__PKT__ITEM__TYPE
|
110
112
|
# @param stale_time [Integer] Time in seconds from Time.now that value will be marked stale
|
111
113
|
# @return [Array] Array of values
|
112
|
-
def self.get_tlm_values(items, stale_time: 30, cache_timeout:
|
114
|
+
def self.get_tlm_values(items, stale_time: 30, cache_timeout: nil, scope: $openc3_scope)
|
113
115
|
now = Time.now
|
114
116
|
results = []
|
115
117
|
lookups = []
|
@@ -246,7 +248,7 @@ module OpenC3
|
|
246
248
|
end
|
247
249
|
end
|
248
250
|
|
249
|
-
def self.determine_latest_packet_for_item(target_name, item_name, cache_timeout:
|
251
|
+
def self.determine_latest_packet_for_item(target_name, item_name, cache_timeout: nil, scope: $openc3_scope)
|
250
252
|
item_map = TargetModel.get_item_to_packet_map(target_name, scope: scope)
|
251
253
|
packet_names = item_map[item_name]
|
252
254
|
raise "Item '#{target_name} LATEST #{item_name}' does not exist for scope: #{scope}" unless packet_names
|
@@ -293,10 +295,12 @@ module OpenC3
|
|
293
295
|
end
|
294
296
|
|
295
297
|
def self._get_overrides(now, tgt_pkt_key, overrides, target_name, packet_name, cache_timeout:, scope:)
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
298
|
+
if cache_timeout
|
299
|
+
cache_time, hash = @@override_cache[tgt_pkt_key]
|
300
|
+
if hash and (now - cache_time) < cache_timeout
|
301
|
+
overrides[tgt_pkt_key] = hash
|
302
|
+
return hash
|
303
|
+
end
|
300
304
|
end
|
301
305
|
override_data = Store.hget("#{scope}__override__#{target_name}", packet_name)
|
302
306
|
if override_data
|
@@ -39,11 +39,17 @@ module OpenC3
|
|
39
39
|
include Api
|
40
40
|
|
41
41
|
def self.names
|
42
|
-
|
42
|
+
if Dir.exist?("#{ENV['GEM_HOME']}/gems")
|
43
|
+
result = Pathname.new("#{ENV['GEM_HOME']}/gems").children.select { |c| c.directory? }.collect { |p| File.basename(p) + '.gem' }
|
44
|
+
else
|
45
|
+
result = []
|
46
|
+
end
|
43
47
|
return result.sort
|
44
48
|
end
|
45
49
|
|
46
50
|
def self.get(name)
|
51
|
+
path = "#{ENV['GEM_HOME']}/cosmoscache/#{name}"
|
52
|
+
return path if File.exist?(path)
|
47
53
|
path = "#{ENV['GEM_HOME']}/cache/#{name}"
|
48
54
|
return path if File.exist?(path)
|
49
55
|
raise "Gem #{name} not found"
|
@@ -52,8 +58,9 @@ module OpenC3
|
|
52
58
|
def self.put(gem_file_path, gem_install: true, scope:)
|
53
59
|
if File.file?(gem_file_path)
|
54
60
|
gem_filename = File.basename(gem_file_path)
|
55
|
-
|
56
|
-
FileUtils.
|
61
|
+
# Put into cosmoscache folder that we control
|
62
|
+
FileUtils.mkdir_p("#{ENV['GEM_HOME']}/cosmoscache") unless Dir.exist?("#{ENV['GEM_HOME']}/cosmoscache")
|
63
|
+
FileUtils.cp(gem_file_path, "#{ENV['GEM_HOME']}/cosmoscache/#{File.basename(gem_file_path)}")
|
57
64
|
if gem_install
|
58
65
|
Logger.info "Installing gem: #{gem_filename}"
|
59
66
|
result = OpenC3::ProcessManager.instance.spawn(["ruby", "/openc3/bin/openc3cli", "geminstall", gem_filename, scope], "package_install", gem_filename, Time.now + 3600.0, scope: scope)
|
@@ -118,5 +125,15 @@ module OpenC3
|
|
118
125
|
version = File.basename(split_name[-1], '.gem')
|
119
126
|
return gem_name, version
|
120
127
|
end
|
128
|
+
|
129
|
+
def self.destroy_all_other_versions(name)
|
130
|
+
keep_gem_name, keep_gem_version = GemModel.extract_name_and_version(name)
|
131
|
+
GemModel.names.each do |gem_full_name|
|
132
|
+
gem_name, gem_version = GemModel.extract_name_and_version(gem_full_name)
|
133
|
+
if gem_name == keep_gem_name and gem_version != keep_gem_version
|
134
|
+
GemModel.destroy(gem_full_name)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
121
138
|
end
|
122
139
|
end
|
@@ -185,7 +185,7 @@ module OpenC3
|
|
185
185
|
@topics << parameters[0]
|
186
186
|
when 'TARGET_NAME'
|
187
187
|
parser.verify_num_parameters(1, 1, "#{keyword} <Target Name>")
|
188
|
-
@target_names << parameters[0]
|
188
|
+
@target_names << parameters[0].upcase
|
189
189
|
when 'CMD'
|
190
190
|
parser.verify_num_parameters(1, nil, "#{keyword} <Args>")
|
191
191
|
@cmd = parameters.dup
|
@@ -158,6 +158,11 @@ module OpenC3
|
|
158
158
|
gem_file_path = OpenC3::GemModel.get(gem_name)
|
159
159
|
end
|
160
160
|
|
161
|
+
# Attempt to remove all older versions of this same plugin before install to prevent version conflicts
|
162
|
+
# Especially on downgrades
|
163
|
+
# Leave the same version if it already exists
|
164
|
+
OpenC3::GemModel.destroy_all_other_versions(File.basename(gem_file_path))
|
165
|
+
|
161
166
|
# Actually install the gem now (slow)
|
162
167
|
OpenC3::GemModel.install(gem_file_path, scope: scope) unless validate_only
|
163
168
|
|
@@ -296,10 +301,15 @@ module OpenC3
|
|
296
301
|
|
297
302
|
# Undeploy all models associated with this plugin
|
298
303
|
def undeploy
|
304
|
+
errors = []
|
299
305
|
microservice_count = 0
|
300
306
|
microservices = MicroserviceModel.find_all_by_plugin(plugin: @name, scope: @scope)
|
301
307
|
microservices.each do |name, model_instance|
|
302
|
-
|
308
|
+
begin
|
309
|
+
model_instance.destroy
|
310
|
+
rescue Exception => error
|
311
|
+
errors << error
|
312
|
+
end
|
303
313
|
microservice_count += 1
|
304
314
|
end
|
305
315
|
# Wait for the operator to wake up and remove the microservice processes
|
@@ -308,21 +318,49 @@ module OpenC3
|
|
308
318
|
# Save TargetModel for last as it has the most to cleanup
|
309
319
|
[InterfaceModel, RouterModel, ToolModel, WidgetModel, TargetModel].each do |model|
|
310
320
|
model.find_all_by_plugin(plugin: @name, scope: @scope).each do |name, model_instance|
|
311
|
-
|
321
|
+
begin
|
322
|
+
model_instance.destroy
|
323
|
+
rescue Exception => error
|
324
|
+
errors << error
|
325
|
+
end
|
312
326
|
end
|
313
327
|
end
|
314
328
|
# Cleanup Redis stuff that might have been left by microservices
|
315
329
|
microservices.each do |name, model_instance|
|
316
|
-
|
330
|
+
begin
|
331
|
+
model_instance.cleanup
|
332
|
+
rescue Exception => error
|
333
|
+
errors << error
|
334
|
+
end
|
335
|
+
end
|
336
|
+
# Raise all the errors at once
|
337
|
+
if errors.length > 0
|
338
|
+
message = ''
|
339
|
+
errors.each do |error|
|
340
|
+
message += "\n#{error.formatted}\n"
|
341
|
+
end
|
342
|
+
raise message
|
317
343
|
end
|
318
344
|
rescue Exception => error
|
319
|
-
Logger.error("Error undeploying plugin model #{@name} in scope #{@scope} due to #{error}")
|
345
|
+
Logger.error("Error undeploying plugin model #{@name} in scope #{@scope} due to: #{error}")
|
346
|
+
ensure
|
347
|
+
# Double check everything is gone
|
348
|
+
found = []
|
349
|
+
[MicroserviceModel, InterfaceModel, RouterModel, ToolModel, WidgetModel, TargetModel].each do |model|
|
350
|
+
model.find_all_by_plugin(plugin: @name, scope: @scope).each do |name, model_instance|
|
351
|
+
found << model_instance
|
352
|
+
end
|
353
|
+
end
|
354
|
+
if found.length > 0
|
355
|
+
# If undeploy failed we need to not move forward with anything else
|
356
|
+
Logger.error("Error undeploying plugin model #{@name} in scope #{@scope} due to: Plugin submodels still exist after undeploy = #{found.length}")
|
357
|
+
raise "Plugin #{@name} submodels still exist after undeploy = #{found.length}"
|
358
|
+
end
|
320
359
|
end
|
321
360
|
|
322
361
|
# Reinstall
|
323
362
|
def restore
|
324
363
|
plugin_hash = self.as_json(:allow_nan => true)
|
325
|
-
plugin_hash['name'] = plugin_hash['name'].split("__")[0]
|
326
364
|
OpenC3::PluginModel.install_phase2(plugin_hash, scope: @scope)
|
327
365
|
@destroyed = false
|
328
366
|
end
|