openc3 5.2.0 → 5.4.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/bin/openc3cli +108 -105
- data/data/config/interface_modifiers.yaml +22 -4
- data/data/config/item_modifiers.yaml +4 -2
- data/data/config/microservice.yaml +18 -0
- data/data/config/table_manager.yaml +2 -2
- data/data/config/tool.yaml +1 -1
- data/ext/openc3/ext/config_parser/config_parser.c +17 -2
- data/lib/openc3/api/api.rb +1 -0
- data/lib/openc3/api/interface_api.rb +12 -0
- data/lib/openc3/api/metrics_api.rb +97 -0
- data/lib/openc3/api/router_api.rb +14 -2
- data/lib/openc3/api/target_api.rb +24 -3
- data/lib/openc3/api/tlm_api.rb +5 -4
- data/lib/openc3/config/config_parser.rb +29 -4
- data/lib/openc3/core_ext/time.rb +6 -1
- data/lib/openc3/interfaces/interface.rb +27 -26
- data/lib/openc3/interfaces/mqtt_interface.rb +240 -0
- data/lib/openc3/interfaces/protocols/override_protocol.rb +2 -61
- data/lib/openc3/interfaces/protocols/protocol.rb +6 -1
- data/lib/openc3/interfaces/simulated_target_interface.rb +1 -3
- data/lib/openc3/interfaces/tcpip_server_interface.rb +0 -11
- data/lib/openc3/interfaces.rb +2 -3
- data/lib/openc3/logs/buffered_packet_log_reader.rb +2 -2
- data/lib/openc3/microservices/cleanup_microservice.rb +17 -1
- data/lib/openc3/microservices/decom_microservice.rb +12 -9
- data/lib/openc3/microservices/interface_microservice.rb +93 -9
- data/lib/openc3/microservices/log_microservice.rb +11 -5
- data/lib/openc3/microservices/microservice.rb +10 -9
- data/lib/openc3/microservices/periodic_microservice.rb +7 -0
- data/lib/openc3/microservices/reaction_microservice.rb +0 -33
- data/lib/openc3/microservices/reducer_microservice.rb +14 -10
- data/lib/openc3/microservices/text_log_microservice.rb +12 -3
- data/lib/openc3/microservices/timeline_microservice.rb +0 -6
- data/lib/openc3/microservices/trigger_group_microservice.rb +0 -20
- data/lib/openc3/models/cvt_model.rb +103 -47
- data/lib/openc3/models/interface_model.rb +23 -0
- data/lib/openc3/models/metric_model.rb +53 -6
- data/lib/openc3/models/microservice_model.rb +15 -1
- data/lib/openc3/models/model.rb +1 -1
- data/lib/openc3/models/plugin_model.rb +6 -1
- data/lib/openc3/models/secret_model.rb +53 -0
- data/lib/openc3/models/target_model.rb +2 -2
- data/lib/openc3/models/tool_model.rb +17 -8
- data/lib/openc3/operators/microservice_operator.rb +25 -0
- data/lib/openc3/operators/operator.rb +5 -1
- data/lib/openc3/packets/packet.rb +21 -7
- data/lib/openc3/packets/packet_item.rb +3 -2
- data/lib/openc3/script/api_shared.rb +18 -2
- data/lib/openc3/script/script.rb +8 -0
- data/lib/openc3/script/script_runner.rb +1 -2
- data/lib/openc3/script/storage.rb +2 -1
- data/lib/openc3/script/suite.rb +15 -11
- data/lib/openc3/system/system.rb +6 -3
- data/lib/openc3/topics/interface_topic.rb +17 -1
- data/lib/openc3/topics/router_topic.rb +17 -1
- data/lib/openc3/utilities/aws_bucket.rb +20 -3
- data/lib/openc3/utilities/bucket.rb +1 -1
- data/lib/openc3/utilities/bucket_file_cache.rb +1 -1
- data/lib/openc3/utilities/bucket_utilities.rb +1 -1
- data/lib/openc3/utilities/local_mode.rb +1 -0
- data/lib/openc3/utilities/metric.rb +77 -101
- data/lib/openc3/utilities/redis_secrets.rb +46 -0
- data/lib/openc3/utilities/s3_autoload.rb +19 -9
- data/lib/openc3/utilities/secrets.rb +63 -0
- data/lib/openc3/utilities/target_file.rb +3 -1
- data/lib/openc3/version.rb +5 -5
- data/templates/plugin-template/LICENSE.txt +7 -0
- data/templates/plugin-template/README.md +4 -3
- data/templates/plugin-template/plugin.gemspec +4 -4
- metadata +22 -3
- data/data/config/_interfaces.yaml.err +0 -1017
@@ -28,7 +28,8 @@ module OpenC3
|
|
28
28
|
WHITELIST.concat([
|
29
29
|
'get_target_list',
|
30
30
|
'get_target',
|
31
|
-
'
|
31
|
+
'get_target_interfaces',
|
32
|
+
'get_all_target_info', # DEPRECATED
|
32
33
|
])
|
33
34
|
|
34
35
|
# Returns the list of all target names
|
@@ -49,9 +50,29 @@ module OpenC3
|
|
49
50
|
TargetModel.get(name: target_name, scope: scope)
|
50
51
|
end
|
51
52
|
|
52
|
-
# Get
|
53
|
+
# Get all targets and their interfaces
|
53
54
|
#
|
54
|
-
# @return [Array<Array<String,
|
55
|
+
# @return [Array<Array<String, String] Array of Arrays \[name, interfaces]
|
56
|
+
def get_target_interfaces(scope: $openc3_scope, token: $openc3_token)
|
57
|
+
authorize(permission: 'system', scope: scope, token: token)
|
58
|
+
info = []
|
59
|
+
interfaces = InterfaceModel.all(scope: scope)
|
60
|
+
get_target_list(scope: scope, token: token).each do |target_name|
|
61
|
+
interface_names = []
|
62
|
+
interfaces.each do |name, interface|
|
63
|
+
if interface['target_names'].include? target_name
|
64
|
+
interface_names << interface['name']
|
65
|
+
end
|
66
|
+
end
|
67
|
+
info << [target_name, interface_names.join(",")]
|
68
|
+
end
|
69
|
+
info
|
70
|
+
end
|
71
|
+
|
72
|
+
# DEPRECATED: Get information about all targets
|
73
|
+
# Warning this call can take a long time with many defined packets
|
74
|
+
#
|
75
|
+
# @return [Array<Array<String, String, Numeric, Numeric>] Array of Arrays \[name, interface, cmd_cnt, tlm_cnt]
|
55
76
|
def get_all_target_info(scope: $openc3_scope, token: $openc3_token)
|
56
77
|
authorize(permission: 'system', scope: scope, token: token)
|
57
78
|
info = []
|
data/lib/openc3/api/tlm_api.rb
CHANGED
@@ -118,7 +118,8 @@ module OpenC3
|
|
118
118
|
# @param type [Symbol] Telemetry type, :RAW, :CONVERTED (default), :FORMATTED, or :WITH_UNITS
|
119
119
|
def inject_tlm(target_name, packet_name, item_hash = nil, type: :CONVERTED, scope: $openc3_scope, token: $openc3_token)
|
120
120
|
authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
|
121
|
-
|
121
|
+
type = type.to_s.intern
|
122
|
+
unless CvtModel::VALUE_TYPES.include?(type)
|
122
123
|
raise "Unknown type '#{type}' for #{target_name} #{packet_name}"
|
123
124
|
end
|
124
125
|
|
@@ -161,8 +162,8 @@ module OpenC3
|
|
161
162
|
# @param args The args must either be a string followed by a value or
|
162
163
|
# three strings followed by a value (see the calling style in the
|
163
164
|
# description).
|
164
|
-
# @param type [Symbol] Telemetry type, :RAW, :CONVERTED
|
165
|
-
def override_tlm(*args, type: :
|
165
|
+
# @param type [Symbol] Telemetry type, :ALL (default), :RAW, :CONVERTED, :FORMATTED, :WITH_UNITS
|
166
|
+
def override_tlm(*args, type: :ALL, scope: $openc3_scope, token: $openc3_token)
|
166
167
|
target_name, packet_name, item_name, value = set_tlm_process_args(args, __method__, scope: scope)
|
167
168
|
authorize(permission: 'tlm_set', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
|
168
169
|
CvtModel.override(target_name, packet_name, item_name, value, type: type.intern, scope: scope)
|
@@ -179,7 +180,7 @@ module OpenC3
|
|
179
180
|
#
|
180
181
|
# @param args The args must either be a string or three strings
|
181
182
|
# (see the calling style in the description).
|
182
|
-
# @param type [Symbol] Telemetry type, :RAW, :CONVERTED
|
183
|
+
# @param type [Symbol] Telemetry type, :ALL (default), :RAW, :CONVERTED, :FORMATTED, :WITH_UNITS
|
183
184
|
# Also takes :ALL which means to normalize all telemetry types
|
184
185
|
def normalize_tlm(*args, type: :ALL, scope: $openc3_scope, token: $openc3_token)
|
185
186
|
target_name, packet_name, item_name = tlm_process_args(args, __method__, scope: scope)
|
@@ -219,8 +219,6 @@ module OpenC3
|
|
219
219
|
size,
|
220
220
|
PARSING_REGEX,
|
221
221
|
&block)
|
222
|
-
rescue Exception => e # Catch EVERYTHING so we can re-raise with additional info
|
223
|
-
raise e, "#{e}\n\nParsed output in #{file.path}", e.backtrace
|
224
222
|
ensure
|
225
223
|
file.close unless file.closed?
|
226
224
|
end
|
@@ -446,6 +444,22 @@ module OpenC3
|
|
446
444
|
value
|
447
445
|
end
|
448
446
|
|
447
|
+
def parse_errors(errors)
|
448
|
+
return if errors.empty?
|
449
|
+
message = ''
|
450
|
+
errors.each do |error|
|
451
|
+
if error.is_a? OpenC3::ConfigParser::Error
|
452
|
+
message += "\n#{File.basename(error.filename)}:#{error.line_number}: #{error.line}"
|
453
|
+
message += "\nError: #{error.message}"
|
454
|
+
message += "\nUsage: #{error.usage}" unless error.usage.empty?
|
455
|
+
else
|
456
|
+
message += "\n#{error.message}"
|
457
|
+
end
|
458
|
+
message += "\n"
|
459
|
+
end
|
460
|
+
raise message
|
461
|
+
end
|
462
|
+
|
449
463
|
if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
|
450
464
|
# Iterates over each line of the io object and yields the keyword and parameters
|
451
465
|
def parse_loop(io, yield_non_keyword_lines, remove_quotes, size, rx)
|
@@ -454,6 +468,7 @@ module OpenC3
|
|
454
468
|
@keyword = nil
|
455
469
|
@parameters = []
|
456
470
|
@line = ''
|
471
|
+
errors = []
|
457
472
|
|
458
473
|
while true
|
459
474
|
@line_number += 1
|
@@ -516,7 +531,11 @@ module OpenC3
|
|
516
531
|
# Ignore lines without keywords: comments and blank lines
|
517
532
|
if @keyword.nil?
|
518
533
|
if yield_non_keyword_lines
|
519
|
-
|
534
|
+
begin
|
535
|
+
yield(@keyword, @parameters)
|
536
|
+
rescue => error
|
537
|
+
errors << error
|
538
|
+
end
|
520
539
|
end
|
521
540
|
@line = ''
|
522
541
|
next
|
@@ -545,10 +564,16 @@ module OpenC3
|
|
545
564
|
end
|
546
565
|
end
|
547
566
|
|
548
|
-
|
567
|
+
begin
|
568
|
+
yield(@keyword, @parameters)
|
569
|
+
rescue => error
|
570
|
+
errors << error
|
571
|
+
end
|
549
572
|
@line = ''
|
550
573
|
end
|
551
574
|
|
575
|
+
parse_errors(errors)
|
576
|
+
|
552
577
|
@@progress_callback.call(1.0) if @@progress_callback
|
553
578
|
|
554
579
|
return nil
|
data/lib/openc3/core_ext/time.rb
CHANGED
@@ -17,7 +17,7 @@
|
|
17
17
|
# All changes Copyright 2022, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
|
-
# This file may also be used under the terms of a commercial license
|
20
|
+
# This file may also be used under the terms of a commercial license
|
21
21
|
# if purchased from OpenC3, Inc.
|
22
22
|
|
23
23
|
require 'date'
|
@@ -58,6 +58,7 @@ class Time
|
|
58
58
|
MINUTES_PER_HOUR = 60
|
59
59
|
HOURS_PER_DAY = 24
|
60
60
|
NSEC_PER_SECOND = 1_000_000_000
|
61
|
+
NSEC_PER_MSEC = 1_000_000
|
61
62
|
USEC_PER_SECOND = USEC_PER_MSEC * MSEC_PER_SECOND
|
62
63
|
MSEC_PER_MINUTE = 60 * MSEC_PER_SECOND
|
63
64
|
MSEC_PER_HOUR = 60 * MSEC_PER_MINUTE
|
@@ -508,4 +509,8 @@ class Time
|
|
508
509
|
nanoseconds = nsec_from_epoch % NSEC_PER_SECOND
|
509
510
|
Time.at(seconds, nanoseconds, :nsec)
|
510
511
|
end
|
512
|
+
|
513
|
+
def to_msec_from_epoch
|
514
|
+
(self.tv_sec * MSEC_PER_SECOND) + (self.tv_nsec / NSEC_PER_MSEC)
|
515
|
+
end
|
511
516
|
end
|
@@ -22,6 +22,7 @@
|
|
22
22
|
|
23
23
|
require 'openc3/api/api'
|
24
24
|
require 'openc3/io/raw_logger_pair'
|
25
|
+
require 'openc3/utilities/secrets'
|
25
26
|
|
26
27
|
module OpenC3
|
27
28
|
# Defines all the attributes and methods common to all interface classes
|
@@ -112,9 +113,6 @@ module OpenC3
|
|
112
113
|
# @return [Array<[Protocol Class, Protocol Args, Protocol kind (:READ, :WRITE, :READ_WRITE)>] Info to recreate protocols
|
113
114
|
attr_accessor :protocol_info
|
114
115
|
|
115
|
-
# @return [Hash or nil] Hash of overridden telemetry points
|
116
|
-
attr_accessor :override_tlm
|
117
|
-
|
118
116
|
# @return [String] Most recently read raw data
|
119
117
|
attr_accessor :read_raw_data
|
120
118
|
|
@@ -134,6 +132,9 @@ module OpenC3
|
|
134
132
|
# (when used as a BridgeRouter)
|
135
133
|
attr_accessor :interfaces
|
136
134
|
|
135
|
+
# @return [Secrets] Interface secrets manager class
|
136
|
+
attr_accessor :secrets
|
137
|
+
|
137
138
|
# Initialize default attribute values
|
138
139
|
def initialize
|
139
140
|
@name = self.class.to_s.split("::")[-1] # Remove namespacing if present
|
@@ -166,13 +167,13 @@ module OpenC3
|
|
166
167
|
@read_protocols = []
|
167
168
|
@write_protocols = []
|
168
169
|
@protocol_info = []
|
169
|
-
@override_tlm = nil
|
170
170
|
@read_raw_data = ''
|
171
171
|
@written_raw_data = ''
|
172
172
|
@read_raw_data_time = nil
|
173
173
|
@written_raw_data_time = nil
|
174
174
|
@config_params = []
|
175
175
|
@interfaces = []
|
176
|
+
@secrets = Secrets.getClient
|
176
177
|
end
|
177
178
|
|
178
179
|
# Connects the interface to its target(s). Must be implemented by a
|
@@ -406,13 +407,13 @@ module OpenC3
|
|
406
407
|
# num_clients is per interface so don't copy
|
407
408
|
# read_queue_size is the number of packets in the queue so don't copy
|
408
409
|
# write_queue_size is the number of packets in the queue so don't copy
|
409
|
-
|
410
|
+
self.options.each do |option_name, option_values|
|
411
|
+
other_interface.set_option(option_name, option_values)
|
412
|
+
end
|
410
413
|
other_interface.protocol_info = []
|
411
414
|
self.protocol_info.each do |protocol_class, protocol_args, read_write|
|
412
415
|
other_interface.add_protocol(protocol_class, protocol_args, read_write)
|
413
416
|
end
|
414
|
-
other_interface.override_tlm = nil
|
415
|
-
other_interface.override_tlm = self.override_tlm.clone if self.override_tlm
|
416
417
|
end
|
417
418
|
|
418
419
|
# Set an interface or router specific option
|
@@ -484,28 +485,28 @@ module OpenC3
|
|
484
485
|
protocol.interface = self
|
485
486
|
end
|
486
487
|
|
487
|
-
def
|
488
|
-
|
488
|
+
def interface_cmd(cmd_name, *cmd_args)
|
489
|
+
# Default do nothing - Implemented by subclasses
|
490
|
+
return false
|
489
491
|
end
|
490
492
|
|
491
|
-
def
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
493
|
+
def protocol_cmd(cmd_name, *cmd_args, read_write: :READ_WRITE, index: -1)
|
494
|
+
read_write = read_write.to_s.upcase.intern
|
495
|
+
protocols = nil
|
496
|
+
case read_write
|
497
|
+
when :READ, :READ_WRITE
|
498
|
+
protocols = @read_protocols
|
499
|
+
when :WRITE
|
500
|
+
protocols = @write_protocols.reverse # Reverse so ordering matches configuration ordering
|
501
|
+
else
|
502
|
+
raise "Unknown protocol descriptor: #{read_write}. Must be :READ, :WRITE, or :READ_WRITE."
|
501
503
|
end
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
@override_tlm[target_name][packet_name][item_name] = [value, type]
|
504
|
+
handled = false
|
505
|
+
protocols.each_with_index do |protocol, protocol_index|
|
506
|
+
result = protocol.protocol_cmd(cmd_name, @cmd_args) if index == protocol_index or index == -1
|
507
|
+
handled = true if result
|
508
|
+
end
|
509
|
+
return handled
|
509
510
|
end
|
510
511
|
end
|
511
512
|
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2022 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
|
+
# You can quickly setup an unauthenticated MQTT server in Docker with
|
20
|
+
# docker run -it -p 1883:1883 eclipse-mosquitto:2.0.15 mosquitto -c /mosquitto-no-auth.conf
|
21
|
+
|
22
|
+
require 'openc3/interfaces/interface'
|
23
|
+
require 'openc3/config/config_parser'
|
24
|
+
require 'mqtt'
|
25
|
+
|
26
|
+
# Patches to the Ruby MQTT library so that it will work reliably with COSMOS
|
27
|
+
saved_verbose = $VERBOSE
|
28
|
+
$VERBOSE = nil
|
29
|
+
module MQTT
|
30
|
+
class Client
|
31
|
+
def get(topic = nil, options = {})
|
32
|
+
if block_given?
|
33
|
+
get_packet(topic) do |packet|
|
34
|
+
yield(packet.topic, packet.payload) unless packet.retain && options[:omit_retained]
|
35
|
+
end
|
36
|
+
else
|
37
|
+
loop do
|
38
|
+
# Wait for one packet to be available
|
39
|
+
packet = get_packet(topic)
|
40
|
+
return nil unless packet # Patch for COSMOS
|
41
|
+
return packet.topic, packet.payload unless packet.retain && options[:omit_retained]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_packet(topic = nil)
|
47
|
+
# Subscribe to a topic, if an argument is given
|
48
|
+
subscribe(topic) unless topic.nil?
|
49
|
+
|
50
|
+
if block_given?
|
51
|
+
# Loop forever!
|
52
|
+
loop do
|
53
|
+
packet = @read_queue.pop
|
54
|
+
return nil unless packet # Patch for COSMOS
|
55
|
+
yield(packet)
|
56
|
+
puback_packet(packet) if packet.qos > 0
|
57
|
+
end
|
58
|
+
else
|
59
|
+
# Wait for one packet to be available
|
60
|
+
packet = @read_queue.pop
|
61
|
+
return nil unless packet # Patch for COSMOS
|
62
|
+
puback_packet(packet) if packet.qos > 0
|
63
|
+
return packet
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def disconnect(send_msg = true)
|
68
|
+
# Stop reading packets from the socket first
|
69
|
+
@read_thread.kill if @read_thread && @read_thread.alive?
|
70
|
+
@read_thread = nil
|
71
|
+
|
72
|
+
@read_queue << nil # Patch for COSMOS
|
73
|
+
|
74
|
+
# Close the socket if it is open
|
75
|
+
if connected?
|
76
|
+
if send_msg
|
77
|
+
packet = MQTT::Packet::Disconnect.new
|
78
|
+
send_packet(packet)
|
79
|
+
end
|
80
|
+
@socket.close unless @socket.nil?
|
81
|
+
@socket = nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
$VERBOSE = saved_verbose
|
88
|
+
|
89
|
+
module OpenC3
|
90
|
+
# Base class for interfaces that send and receive messages over MQTT
|
91
|
+
class MqttInterface < Interface
|
92
|
+
# @param hostname [String] MQTT server to connect to
|
93
|
+
# @param port [Integer] MQTT port
|
94
|
+
# @param ssl [Boolean] Use SSL true/false
|
95
|
+
def initialize(hostname, port = 1883, ssl = false)
|
96
|
+
super()
|
97
|
+
@hostname = hostname
|
98
|
+
@port = Integer(port)
|
99
|
+
@ssl = ConfigParser.handle_true_false(ssl)
|
100
|
+
@username = nil
|
101
|
+
@password = nil
|
102
|
+
@cert = nil
|
103
|
+
@key = nil
|
104
|
+
@ca_file = nil
|
105
|
+
|
106
|
+
@write_topics = []
|
107
|
+
@read_topics = []
|
108
|
+
|
109
|
+
# Build list of packets by topic
|
110
|
+
@read_packets_by_topic = {}
|
111
|
+
System.telemetry.all.each do |target_name, target_packets|
|
112
|
+
target_packets.each do |packet_name, packet|
|
113
|
+
topics = packet.meta['TOPIC']
|
114
|
+
topics = packet.meta['TOPICS'] unless topics
|
115
|
+
if topics
|
116
|
+
topics.each do |topic|
|
117
|
+
@read_packets_by_topic[topic] = packet
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Connects the interface to its target(s)
|
125
|
+
def connect
|
126
|
+
@write_topics = []
|
127
|
+
@read_topics = []
|
128
|
+
@client = MQTT::Client.new
|
129
|
+
@client.host = @hostname
|
130
|
+
@client.port = @port
|
131
|
+
@client.ssl = @ssl
|
132
|
+
@client.username = @username if @username
|
133
|
+
@client.password = @password if @password
|
134
|
+
@client.cert = @cert if @cert
|
135
|
+
@client.key = @key if @key
|
136
|
+
@client.ca_file = @ca_file.path if @ca_file
|
137
|
+
@client.connect
|
138
|
+
@read_packets_by_topic.each do |topic, _|
|
139
|
+
Logger.info "#{@name}: Subscribing to #{topic}"
|
140
|
+
@client.subscribe(topic)
|
141
|
+
end
|
142
|
+
super()
|
143
|
+
end
|
144
|
+
|
145
|
+
# @return [Boolean] Whether the active ports (read and/or write) have
|
146
|
+
# created sockets. Since UDP is connectionless, creation of the sockets
|
147
|
+
# is used to determine connection.
|
148
|
+
def connected?
|
149
|
+
if @client
|
150
|
+
return @client.connected?
|
151
|
+
else
|
152
|
+
return false
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Disconnects the interface from its target(s)
|
157
|
+
def disconnect
|
158
|
+
@client.disconnect
|
159
|
+
@client = nil
|
160
|
+
super()
|
161
|
+
end
|
162
|
+
|
163
|
+
def read
|
164
|
+
topic = @read_topics.shift
|
165
|
+
packet = super()
|
166
|
+
return nil unless packet
|
167
|
+
identified_packet = @read_packets_by_topic[topic]
|
168
|
+
if identified_packet
|
169
|
+
identified_packet = identified_packet.dup
|
170
|
+
identified_packet.buffer = packet.buffer
|
171
|
+
packet = identified_packet
|
172
|
+
end
|
173
|
+
packet.received_time = nil
|
174
|
+
return packet
|
175
|
+
end
|
176
|
+
|
177
|
+
def write(packet)
|
178
|
+
topics = packet.meta['TOPIC']
|
179
|
+
topics = packet.meta['TOPICS'] unless topics
|
180
|
+
if topics
|
181
|
+
topics.each do |topic|
|
182
|
+
@write_topics << topic
|
183
|
+
super(packet)
|
184
|
+
end
|
185
|
+
else
|
186
|
+
raise "Command packet #{packet.target_name} #{packet.packet_name} requires a META TOPIC or TOPICS"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Reads from the socket if the read_port is defined
|
191
|
+
def read_interface
|
192
|
+
topic, data = @client.get
|
193
|
+
if data.nil? or data.length <= 0
|
194
|
+
Logger.info "#{@name}: read returned nil" if data.nil?
|
195
|
+
Logger.info "#{@name}: read returned 0 bytes" if not data.nil? and data.length <= 0
|
196
|
+
return nil
|
197
|
+
end
|
198
|
+
@read_topics << topic
|
199
|
+
read_interface_base(data)
|
200
|
+
return data
|
201
|
+
rescue IOError # Disconnected
|
202
|
+
return nil
|
203
|
+
end
|
204
|
+
|
205
|
+
# Writes to the socket
|
206
|
+
# @param data [String] Raw packet data
|
207
|
+
def write_interface(data)
|
208
|
+
write_interface_base(data)
|
209
|
+
topic = @write_topics.shift
|
210
|
+
@client.publish(topic, data)
|
211
|
+
data
|
212
|
+
end
|
213
|
+
|
214
|
+
# Supported Options
|
215
|
+
# USERNAME - Username for Mqtt Server
|
216
|
+
# PASSWORD - Password for Mqtt Server
|
217
|
+
# CERT - Public Key for Client Cert Auth
|
218
|
+
# KEY - Private Key for Client Cert Auth
|
219
|
+
# CA_FILE - Certificate Authority for Client Cert Auth
|
220
|
+
# (see Interface#set_option)
|
221
|
+
def set_option(option_name, option_values)
|
222
|
+
super(option_name, option_values)
|
223
|
+
case option_name.upcase
|
224
|
+
when 'USERNAME'
|
225
|
+
@username = option_values[0]
|
226
|
+
when 'PASSWORD'
|
227
|
+
@password = option_values[0]
|
228
|
+
when 'CERT'
|
229
|
+
@cert = option_values[0]
|
230
|
+
when 'KEY'
|
231
|
+
@key = option_values[0]
|
232
|
+
when 'CA_FILE'
|
233
|
+
# CA_FILE must be given as a file
|
234
|
+
@ca_file = Tempfile.new('ca_file')
|
235
|
+
@ca_file.write(option_values[0])
|
236
|
+
@ca_file.close
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
@@ -1,63 +1,4 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
-
#
|
4
|
-
#
|
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
|
-
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright 2022, OpenC3, Inc.
|
18
|
-
# All Rights Reserved
|
19
|
-
#
|
20
|
-
# This file may also be used under the terms of a commercial license
|
21
|
-
# if purchased from OpenC3, Inc.
|
22
|
-
|
23
|
-
require 'openc3/interfaces/protocols/protocol'
|
24
|
-
|
25
|
-
module OpenC3
|
26
|
-
# Protocol which permanently overrides an item value such that reading the
|
27
|
-
# item returns the overriden value. Methods are prefixed with underscores
|
28
|
-
# so the API can include the original name which calls out to these
|
29
|
-
# methods. Clearing the override requires calling normalize_tlm.
|
30
|
-
class OverrideProtocol < Protocol
|
31
|
-
# @param allow_empty_data [true/false/nil] See Protocol#initialize
|
32
|
-
def initialize(allow_empty_data = nil)
|
33
|
-
super(allow_empty_data)
|
34
|
-
end
|
35
|
-
|
36
|
-
# Called to perform modifications on a read packet before it is given to the user
|
37
|
-
#
|
38
|
-
# @param packet [Packet] Original packet
|
39
|
-
# @return [Packet] Potentially modified packet
|
40
|
-
def read_packet(packet)
|
41
|
-
if @interface.override_tlm && !@interface.override_tlm.empty?
|
42
|
-
# Need to make sure packet is identified and defined
|
43
|
-
target_names = nil
|
44
|
-
target_names = @interface.tlm_target_names if @interface
|
45
|
-
identified_packet = System.telemetry.identify_and_define_packet(packet, target_names)
|
46
|
-
if identified_packet
|
47
|
-
packet = identified_packet
|
48
|
-
packets = @interface.override_tlm[packet.target_name]
|
49
|
-
if packets
|
50
|
-
items = packets[packet.packet_name]
|
51
|
-
if items
|
52
|
-
items.each do |item_name, value|
|
53
|
-
# This should be safe because we check at the API level it exists
|
54
|
-
packet.write(item_name, value[0], value[1])
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
return packet
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
3
|
+
# This class is deprecated and this file exists only to satisfy existing code requiring it
|
4
|
+
# TODO: Remove this in a future release
|
@@ -17,7 +17,7 @@
|
|
17
17
|
# All changes Copyright 2022, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
|
-
# This file may also be used under the terms of a commercial license
|
20
|
+
# This file may also be used under the terms of a commercial license
|
21
21
|
# if purchased from OpenC3, Inc.
|
22
22
|
|
23
23
|
require 'openc3/config/config_parser'
|
@@ -81,5 +81,10 @@ module OpenC3
|
|
81
81
|
def post_write_interface(packet, data)
|
82
82
|
return packet, data
|
83
83
|
end
|
84
|
+
|
85
|
+
def protocol_cmd(cmd_name, *cmd_args)
|
86
|
+
# Default do nothing - Implemented by subclasses
|
87
|
+
return false
|
88
|
+
end
|
84
89
|
end
|
85
90
|
end
|
@@ -17,11 +17,10 @@
|
|
17
17
|
# All changes Copyright 2022, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
|
-
# This file may also be used under the terms of a commercial license
|
20
|
+
# This file may also be used under the terms of a commercial license
|
21
21
|
# if purchased from OpenC3, Inc.
|
22
22
|
|
23
23
|
require 'openc3/interfaces/interface'
|
24
|
-
require 'openc3/interfaces/protocols/override_protocol'
|
25
24
|
|
26
25
|
module OpenC3
|
27
26
|
# An interface class that provides simulated telemetry and command responses
|
@@ -38,7 +37,6 @@ module OpenC3
|
|
38
37
|
@sim_target = nil
|
39
38
|
@write_raw_allowed = false
|
40
39
|
@raw_logger_pair = nil
|
41
|
-
add_protocol(OverrideProtocol, [], :READ)
|
42
40
|
end
|
43
41
|
|
44
42
|
# Initialize the simulated target object and "connect" to the target
|
@@ -58,8 +58,6 @@ module OpenC3
|
|
58
58
|
attr_accessor :raw_logger_pair
|
59
59
|
# @return [String] The ip address to bind to. Default to ANY (0.0.0.0)
|
60
60
|
attr_accessor :listen_address
|
61
|
-
# @return [boolean] Automatically send SYSTEM META on connect - Default false - Can be CMD/TLM
|
62
|
-
attr_accessor :auto_system_meta
|
63
61
|
|
64
62
|
# @param write_port [Integer] The server write port. Clients should connect
|
65
63
|
# and expect to receive data from this port.
|
@@ -114,7 +112,6 @@ module OpenC3
|
|
114
112
|
@raw_logging_enabled = false
|
115
113
|
@connection_mutex = Mutex.new
|
116
114
|
@listen_address = "0.0.0.0"
|
117
|
-
@auto_system_meta = false
|
118
115
|
|
119
116
|
@read_allowed = false unless ConfigParser.handle_nil(read_port)
|
120
117
|
@write_allowed = false unless ConfigParser.handle_nil(write_port)
|
@@ -282,15 +279,12 @@ module OpenC3
|
|
282
279
|
|
283
280
|
# Supported Options
|
284
281
|
# LISTEN_ADDRESS - Ip address of the interface to accept connections on - Default: 0.0.0.0
|
285
|
-
# AUTO_SYSTEM_META - Automatically send SYSTEM META on connect - Default false
|
286
282
|
# (see Interface#set_option)
|
287
283
|
def set_option(option_name, option_values)
|
288
284
|
super(option_name, option_values)
|
289
285
|
case option_name.upcase
|
290
286
|
when 'LISTEN_ADDRESS'
|
291
287
|
@listen_address = option_values[0]
|
292
|
-
when 'AUTO_SYSTEM_META'
|
293
|
-
@auto_system_meta = ConfigParser.handle_true_false(option_values[0])
|
294
288
|
end
|
295
289
|
end
|
296
290
|
|
@@ -411,11 +405,6 @@ module OpenC3
|
|
411
405
|
interface.connect
|
412
406
|
|
413
407
|
if listen_write
|
414
|
-
if @auto_system_meta
|
415
|
-
meta_packet = System.telemetry.packet('SYSTEM', 'META').clone
|
416
|
-
interface.write(meta_packet)
|
417
|
-
end
|
418
|
-
|
419
408
|
@write_connection_callback.call(interface) if @write_connection_callback
|
420
409
|
@connection_mutex.synchronize do
|
421
410
|
@write_interface_infos << InterfaceInfo.new(interface, hostname, host_ip, port)
|