openc3 5.4.3.pre.beta0 → 5.5.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 +58 -74
- data/data/config/item_modifiers.yaml +1 -1
- data/data/config/plugins.yaml +5 -0
- data/data/config/widgets.yaml +13 -11
- data/lib/openc3/api/cmd_api.rb +43 -17
- data/lib/openc3/api/tlm_api.rb +37 -4
- data/lib/openc3/bridge/bridge.rb +3 -3
- data/lib/openc3/bridge/bridge_config.rb +68 -20
- data/lib/openc3/interfaces/interface.rb +8 -0
- data/lib/openc3/microservices/decom_microservice.rb +0 -3
- data/lib/openc3/microservices/interface_microservice.rb +2 -0
- data/lib/openc3/microservices/reaction_microservice.rb +0 -1
- data/lib/openc3/models/cvt_model.rb +1 -2
- data/lib/openc3/models/metadata_model.rb +1 -1
- data/lib/openc3/models/microservice_model.rb +1 -1
- data/lib/openc3/models/note_model.rb +1 -1
- data/lib/openc3/models/plugin_model.rb +14 -7
- data/lib/openc3/models/timeline_model.rb +1 -1
- data/lib/openc3/models/trigger_group_model.rb +1 -1
- data/lib/openc3/packets/packet_item.rb +1 -1
- data/lib/openc3/script/script.rb +10 -0
- data/lib/openc3/script/storage.rb +5 -5
- data/lib/openc3/script/web_socket_api.rb +423 -0
- data/lib/openc3/streams/tcpip_client_stream.rb +1 -1
- data/lib/openc3/streams/web_socket_client_stream.rb +98 -0
- data/lib/openc3/tools/table_manager/table_manager_core.rb +1 -2
- data/lib/openc3/utilities/aws_bucket.rb +22 -4
- data/lib/openc3/utilities/bucket_file_cache.rb +3 -2
- data/lib/openc3/utilities/bucket_utilities.rb +1 -1
- data/lib/openc3/utilities/cli_generator.rb +210 -0
- data/lib/openc3/utilities/local_mode.rb +2 -2
- data/lib/openc3/utilities/target_file.rb +43 -32
- data/lib/openc3/version.rb +7 -7
- data/templates/conversion/conversion.rb +43 -0
- data/templates/limits_response/response.rb +51 -0
- data/templates/microservice/microservices/TEMPLATE/microservice.rb +62 -0
- data/templates/plugin/plugin.txt +1 -0
- metadata +49 -15
- data/templates/plugin-template/plugin.txt +0 -9
- /data/templates/{plugin-template → plugin}/LICENSE.txt +0 -0
- /data/templates/{plugin-template → plugin}/README.md +0 -0
- /data/templates/{plugin-template → plugin}/Rakefile +0 -0
- /data/templates/{plugin-template → plugin}/plugin.gemspec +0 -0
- /data/templates/{plugin-template → target}/targets/TARGET/cmd_tlm/cmd.txt +0 -0
- /data/templates/{plugin-template → target}/targets/TARGET/cmd_tlm/tlm.txt +0 -0
- /data/templates/{plugin-template → target}/targets/TARGET/lib/target.rb +0 -0
- /data/templates/{plugin-template → target}/targets/TARGET/procedures/procedure.rb +0 -0
- /data/templates/{plugin-template → target}/targets/TARGET/screens/status.txt +0 -0
- /data/templates/{plugin-template → target}/targets/TARGET/target.txt +0 -0
@@ -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/utilities/logger'
|
@@ -30,28 +30,55 @@ module OpenC3
|
|
30
30
|
# @return [Hash<String, Interface>] Routers hash
|
31
31
|
attr_accessor :routers
|
32
32
|
|
33
|
-
def initialize(filename)
|
33
|
+
def initialize(filename, existing_variables = {})
|
34
34
|
@interfaces = {}
|
35
35
|
@routers = {}
|
36
|
-
process_file(filename)
|
36
|
+
process_file(filename, existing_variables)
|
37
37
|
end
|
38
38
|
|
39
39
|
def self.generate_default(filename)
|
40
40
|
default_config = <<~EOF
|
41
|
-
|
42
|
-
|
43
|
-
# INTERFACE <Interface Name> <Interface File> <Interface Params...>
|
44
|
-
# INTERFACE <Interface Name> serial_interface.rb <Write Port> <Read Port> <Baud Rate> <Parity ODD/EVEN/NONE> <Stop Bits> <Write Timeout> <Read Timeout> <Protocol Name> <Protocol Params>
|
45
|
-
# INTERFACE <Interface Name> serial_interface.rb <Write Port> <Read Port> <Baud Rate> <Parity ODD/EVEN/NONE> <Stop Bits> <Write Timeout> <Read Timeout> BURST <Discard Leading Bytes> <Sync Pattern> <Add Sync On Write>
|
46
|
-
# INTERFACE SERIAL_INT serial_interface.rb /dev/ttyS1 /dev/ttyS1 38400 ODD 1 10.0 nil BURST 4 0xDEADBEEF
|
47
|
-
INTERFACE SERIAL_INT serial_interface.rb COM1 COM1 9600 NONE 1 10.0 nil BURST
|
41
|
+
# Write serial port name
|
42
|
+
VARIABLE write_port_name COM1
|
48
43
|
#{' '}
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
44
|
+
# Read serial port name
|
45
|
+
VARIABLE read_port_name COM1
|
46
|
+
#{' '}
|
47
|
+
# Baud Rate
|
48
|
+
VARIABLE baud_rate 115200
|
49
|
+
#{' '}
|
50
|
+
# Parity - NONE, ODD, or EVEN
|
51
|
+
VARIABLE parity NONE
|
52
|
+
#{' '}
|
53
|
+
# Stop bits - 0, 1, or 2
|
54
|
+
VARIABLE stop_bits 1
|
55
|
+
#{' '}
|
56
|
+
# Write Timeout
|
57
|
+
VARIABLE write_timeout 10.0
|
58
|
+
#{' '}
|
59
|
+
# Read Timeout
|
60
|
+
VARIABLE read_timeout nil
|
61
|
+
#{' '}
|
62
|
+
# Flow Control - NONE, or RTSCTS
|
63
|
+
VARIABLE flow_control NONE
|
64
|
+
#{' '}
|
65
|
+
# Data bits per word - Typically 8
|
66
|
+
VARIABLE data_bits 8
|
67
|
+
#{' '}
|
68
|
+
# Port to listen for connections from COSMOS - Plugin must match
|
69
|
+
VARIABLE router_port 2950
|
70
|
+
#{' '}
|
71
|
+
# Port to listen on for connections from COSMOS. Defaults to localhost for security. Will need to be opened
|
72
|
+
# if COSMOS is on another machine.
|
73
|
+
VARIABLE router_listen_address 127.0.0.1
|
74
|
+
#{' '}
|
75
|
+
INTERFACE SERIAL_INT serial_interface.rb <%= write_port_name %> <%= read_port_name %> <%= baud_rate %> <%= parity %> <%= stop_bits %> <%= write_timeout %> <%= read_timeout %>
|
76
|
+
OPTION FLOW_CONTROL <%= flow_control %>
|
77
|
+
OPTION DATA_BITS <%= data_bits %>
|
78
|
+
#{' '}
|
79
|
+
ROUTER SERIAL_ROUTER tcpip_server_interface.rb <%= router_port %> <%= router_port %> 10.0 nil BURST
|
80
|
+
ROUTE SERIAL_INT
|
81
|
+
OPTION LISTEN_ADDRESS <%= router_listen_address %>
|
55
82
|
#{' '}
|
56
83
|
EOF
|
57
84
|
|
@@ -66,18 +93,39 @@ module OpenC3
|
|
66
93
|
# Processes a file and adds in the configuration defined in the file
|
67
94
|
#
|
68
95
|
# @param filename [String] The name of the configuration file to parse
|
69
|
-
|
70
|
-
# recursively
|
71
|
-
def process_file(filename, recursive = false)
|
96
|
+
def process_file(filename, existing_variables = {})
|
72
97
|
current_interface_or_router = nil
|
73
98
|
current_type = nil
|
74
99
|
current_interface_log_added = false
|
75
100
|
|
76
101
|
Logger.info "Processing Bridge configuration in file: #{File.expand_path(filename)}"
|
77
102
|
|
103
|
+
variables = {}
|
78
104
|
parser = ConfigParser.new
|
79
|
-
parser.parse_file(filename
|
105
|
+
parser.parse_file(filename,
|
106
|
+
false,
|
107
|
+
true,
|
108
|
+
false) do |keyword, params|
|
80
109
|
case keyword
|
110
|
+
when 'VARIABLE'
|
111
|
+
usage = "#{keyword} <Variable Name> <Default Value>"
|
112
|
+
parser.verify_num_parameters(2, nil, usage)
|
113
|
+
variable_name = params[0]
|
114
|
+
value = params[1..-1].join(" ")
|
115
|
+
variables[variable_name] = value
|
116
|
+
if existing_variables && existing_variables.key?(variable_name)
|
117
|
+
variables[variable_name] = existing_variables[variable_name]
|
118
|
+
end
|
119
|
+
# Ignore everything else during phase 1
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
parser = ConfigParser.new
|
124
|
+
parser.parse_file(filename, false, true, true, variables) do |keyword, params|
|
125
|
+
case keyword
|
126
|
+
|
127
|
+
when 'VARIABLE'
|
128
|
+
# Ignore during this pass
|
81
129
|
|
82
130
|
when 'INTERFACE'
|
83
131
|
usage = "INTERFACE <Name> <Filename> <Specific Parameters>"
|
@@ -25,6 +25,11 @@ require 'openc3/io/raw_logger_pair'
|
|
25
25
|
require 'openc3/utilities/secrets'
|
26
26
|
|
27
27
|
module OpenC3
|
28
|
+
# Define a class to allow interfaces and protocols to reject commands without
|
29
|
+
# disconnecting the interface
|
30
|
+
class WriteRejectError < StandardError
|
31
|
+
end
|
32
|
+
|
28
33
|
# Defines all the attributes and methods common to all interface classes
|
29
34
|
# used by OpenC3.
|
30
35
|
class Interface
|
@@ -330,6 +335,9 @@ module OpenC3
|
|
330
335
|
else
|
331
336
|
@write_mutex.synchronize { yield }
|
332
337
|
end
|
338
|
+
rescue WriteRejectError => err
|
339
|
+
Logger.error("#{@name}: Write rejected by interface: #{err.message}")
|
340
|
+
raise err
|
333
341
|
rescue Exception => err
|
334
342
|
Logger.error("#{@name}: Error writing to interface")
|
335
343
|
disconnect()
|
@@ -95,7 +95,6 @@ module OpenC3
|
|
95
95
|
# @param log_change [Boolean] Whether to log this limits change event
|
96
96
|
def limits_change_callback(packet, item, old_limits_state, value, log_change)
|
97
97
|
return if @cancel_thread
|
98
|
-
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
99
98
|
packet_time = packet.packet_time
|
100
99
|
message = "#{packet.target_name} #{packet.packet_name} #{item.name} = #{value} is #{item.limits.state}"
|
101
100
|
message << " (#{packet.packet_time.sys.formatted})" if packet_time
|
@@ -136,8 +135,6 @@ module OpenC3
|
|
136
135
|
@logger.error e.formatted
|
137
136
|
end
|
138
137
|
end
|
139
|
-
|
140
|
-
diff = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start # seconds as a float
|
141
138
|
end
|
142
139
|
end
|
143
140
|
end
|
@@ -264,7 +264,6 @@ module OpenC3
|
|
264
264
|
when 'trigger'
|
265
265
|
process_enabled_trigger(data: data)
|
266
266
|
end
|
267
|
-
current_time = Time.now.to_i
|
268
267
|
rescue StandardError => e
|
269
268
|
@logger.error "ReactionWorker-#{@ident} failed to evaluate kind: #{kind} data: #{data}\n#{e.formatted}"
|
270
269
|
end
|
@@ -240,11 +240,10 @@ module OpenC3
|
|
240
240
|
# return an ordered array of hash with keys
|
241
241
|
def self._parse_item(lookups, overrides, item, scope:)
|
242
242
|
target_name, packet_name, item_name, value_type = item.split('__')
|
243
|
-
raise ArgumentError, "items must be formatted as TGT__PKT__ITEM__TYPE" if target_name.nil? || packet_name.nil? || item_name.nil? || value_type.nil?
|
244
243
|
|
245
244
|
# We build lookup keys by including all the less formatted types to gracefully degrade lookups
|
246
245
|
# This allows the user to specify WITH_UNITS and if there is no conversions it will simply return the RAW value
|
247
|
-
case value_type
|
246
|
+
case value_type
|
248
247
|
when 'RAW'
|
249
248
|
keys = [item_name]
|
250
249
|
when 'CONVERTED'
|
@@ -69,7 +69,7 @@ module OpenC3
|
|
69
69
|
if @color.nil?
|
70
70
|
@color = '#%06x' % (rand * 0xffffff)
|
71
71
|
end
|
72
|
-
unless @color =~ /(#*)([0-
|
72
|
+
unless @color =~ /(#*)([0-9a-fA-F]{6})/
|
73
73
|
raise SortedInputError.new "invalid color, must be in hex format, e.g. #FF0000"
|
74
74
|
end
|
75
75
|
@color = "##{@color}" unless @color.start_with?('#')
|
@@ -163,7 +163,7 @@ module OpenC3
|
|
163
163
|
parser.verify_num_parameters(1, 2, usage)
|
164
164
|
begin
|
165
165
|
@ports << [Integer(parameters[0])]
|
166
|
-
rescue
|
166
|
+
rescue # In case Integer fails
|
167
167
|
raise ConfigParser::Error.new(parser, "Port must be an integer: #{parameters[0]}", usage)
|
168
168
|
end
|
169
169
|
protocol = ConfigParser.handle_nil(parameters[1])
|
@@ -78,7 +78,7 @@ module OpenC3
|
|
78
78
|
if @color.nil?
|
79
79
|
@color = '#%06x' % (rand * 0xffffff)
|
80
80
|
end
|
81
|
-
unless @color =~ /(#*)([0-
|
81
|
+
unless @color =~ /(#*)([0-9a-fA-F]{6})/
|
82
82
|
raise SortedInputError.new "invalid color, must be in hex format, e.g. #FF0000"
|
83
83
|
end
|
84
84
|
@color = "##{@color}" unless @color.start_with?('#')
|
@@ -72,7 +72,7 @@ module OpenC3
|
|
72
72
|
temp_dir = Dir.mktmpdir
|
73
73
|
tf = nil
|
74
74
|
begin
|
75
|
-
if File.
|
75
|
+
if File.exist?(gem_file_path)
|
76
76
|
# Load gem to internal gem server
|
77
77
|
OpenC3::GemModel.put(gem_file_path, gem_install: false, scope: scope) unless validate_only
|
78
78
|
else
|
@@ -122,7 +122,6 @@ module OpenC3
|
|
122
122
|
if existing_variables && existing_variables.key?(variable_name)
|
123
123
|
variables[variable_name] = existing_variables[variable_name]
|
124
124
|
end
|
125
|
-
# Ignore everything else during phase 1
|
126
125
|
end
|
127
126
|
end
|
128
127
|
|
@@ -162,14 +161,21 @@ module OpenC3
|
|
162
161
|
gem_path = File.join(temp_dir, "gem")
|
163
162
|
FileUtils.mkdir_p(gem_path)
|
164
163
|
pkg = Gem::Package.new(gem_file_path)
|
165
|
-
needs_dependencies = pkg.spec.runtime_dependencies.length > 0
|
166
164
|
pkg.extract_files(gem_path)
|
167
165
|
Dir[File.join(gem_path, '**/screens/*.txt')].each do |filename|
|
168
166
|
if File.basename(filename) != File.basename(filename).downcase
|
169
|
-
raise "Invalid screen
|
167
|
+
raise "Invalid screen filename: #{filename}. Screen filenames must be lowercase."
|
170
168
|
end
|
171
169
|
end
|
170
|
+
needs_dependencies = pkg.spec.runtime_dependencies.length > 0
|
172
171
|
needs_dependencies = true if Dir.exist?(File.join(gem_path, 'lib'))
|
172
|
+
# If needs_dependencies hasn't already been set we need to scan the plugin.txt
|
173
|
+
# to see if they've explicitly set the NEEDS_DEPENDENCIES keyword
|
174
|
+
unless needs_dependencies
|
175
|
+
if plugin_hash['plugin_txt_lines'].join("\n").include?('NEEDS_DEPENDENCIES')
|
176
|
+
needs_dependencies = true
|
177
|
+
end
|
178
|
+
end
|
173
179
|
if needs_dependencies
|
174
180
|
plugin_model.needs_dependencies = true
|
175
181
|
plugin_model.update unless validate_only
|
@@ -198,7 +204,7 @@ module OpenC3
|
|
198
204
|
current_model = nil
|
199
205
|
parser.parse_file(plugin_txt_path, false, true, true, variables) do |keyword, params|
|
200
206
|
case keyword
|
201
|
-
when 'VARIABLE'
|
207
|
+
when 'VARIABLE', 'NEEDS_DEPENDENCIES'
|
202
208
|
# Ignore during phase 2
|
203
209
|
when 'TARGET', 'INTERFACE', 'ROUTER', 'MICROSERVICE', 'TOOL', 'WIDGET'
|
204
210
|
if current_model
|
@@ -206,12 +212,13 @@ module OpenC3
|
|
206
212
|
current_model.deploy(gem_path, variables, validate_only: validate_only)
|
207
213
|
current_model = nil
|
208
214
|
end
|
209
|
-
current_model = OpenC3.const_get((keyword.capitalize + 'Model').intern).handle_config(parser,
|
215
|
+
current_model = OpenC3.const_get((keyword.capitalize + 'Model').intern).handle_config(parser,
|
216
|
+
keyword, params, plugin: plugin_model.name, needs_dependencies: needs_dependencies, scope: scope)
|
210
217
|
else
|
211
218
|
if current_model
|
212
219
|
current_model.handle_config(parser, keyword, params)
|
213
220
|
else
|
214
|
-
raise "Invalid keyword #{keyword} in plugin.txt"
|
221
|
+
raise "Invalid keyword '#{keyword}' in plugin.txt"
|
215
222
|
end
|
216
223
|
end
|
217
224
|
end
|
@@ -91,7 +91,7 @@ module OpenC3
|
|
91
91
|
if color.nil?
|
92
92
|
color = '#%06x' % (rand * 0xffffff)
|
93
93
|
end
|
94
|
-
valid_color = color =~ /[0-
|
94
|
+
valid_color = color =~ /[0-9a-fA-F]{6}/
|
95
95
|
if valid_color.nil?
|
96
96
|
raise RuntimeError.new "invalid color but in hex format. #FF0000"
|
97
97
|
end
|
@@ -92,7 +92,7 @@ module OpenC3
|
|
92
92
|
if color.nil?
|
93
93
|
color = '#%06x' % (rand * 0xffffff)
|
94
94
|
end
|
95
|
-
valid_color = color =~ /[0-
|
95
|
+
valid_color = color =~ /[0-9a-fA-F]{6}/
|
96
96
|
if valid_color.nil?
|
97
97
|
raise TriggerGroupInputError.new "invalid color must be in hex format. #FF0000"
|
98
98
|
end
|
@@ -80,7 +80,7 @@ module OpenC3
|
|
80
80
|
|
81
81
|
# @return [Hash] Whether or not messages should be printed for this state.
|
82
82
|
# Given as STATE_NAME => true / false.
|
83
|
-
|
83
|
+
attr_reader :messages_disabled
|
84
84
|
|
85
85
|
# Colors associated with states
|
86
86
|
# @return [Hash] State colors given as STATE_NAME => COLOR
|
data/lib/openc3/script/script.rb
CHANGED
@@ -31,6 +31,7 @@ require 'openc3/script/limits'
|
|
31
31
|
require 'openc3/script/exceptions'
|
32
32
|
require 'openc3/script/script_runner'
|
33
33
|
require 'openc3/script/storage'
|
34
|
+
require 'openc3/script/web_socket_api'
|
34
35
|
require 'openc3/utilities/authentication'
|
35
36
|
|
36
37
|
$api_server = nil
|
@@ -99,6 +100,15 @@ module OpenC3
|
|
99
100
|
# NOOP
|
100
101
|
end
|
101
102
|
|
103
|
+
def openc3_script_sleep(sleep_time = nil)
|
104
|
+
if sleep_time
|
105
|
+
sleep(sleep_time)
|
106
|
+
else
|
107
|
+
prompt("Press any key to continue...")
|
108
|
+
end
|
109
|
+
return false
|
110
|
+
end
|
111
|
+
|
102
112
|
def ask_string(question, blank_or_default = false, password = false)
|
103
113
|
answer = ''
|
104
114
|
default = ''
|
@@ -77,7 +77,7 @@ module OpenC3
|
|
77
77
|
else
|
78
78
|
request.body_stream = io_or_string
|
79
79
|
end
|
80
|
-
|
80
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
81
81
|
http.request(request) do |response|
|
82
82
|
response.value() # Raises an HTTP error if the response is not 2xx (success)
|
83
83
|
return response
|
@@ -157,17 +157,17 @@ module OpenC3
|
|
157
157
|
if $openc3_in_cluster
|
158
158
|
case ENV['OPENC3_CLOUD']
|
159
159
|
when 'local'
|
160
|
-
|
160
|
+
URI.parse("http://openc3-minio:9000" + url)
|
161
161
|
when 'aws'
|
162
|
-
|
162
|
+
URI.parse("https://s3.#{ENV['AWS_REGION']}.amazonaws.com" + url)
|
163
163
|
when 'gcp'
|
164
|
-
|
164
|
+
URI.parse("https://storage.googleapis.com" + url)
|
165
165
|
# when 'azure'
|
166
166
|
else
|
167
167
|
raise "Unknown cloud #{ENV['OPENC3_CLOUD']}"
|
168
168
|
end
|
169
169
|
else
|
170
|
-
|
170
|
+
URI.parse($api_server.generate_url + url)
|
171
171
|
end
|
172
172
|
end
|
173
173
|
|