openc3 5.4.2 → 5.4.3.pre.beta0
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 +5 -10
- data/bin/openc3cli +8 -1
- data/data/config/_interfaces.yaml +6 -71
- data/data/config/interface_modifiers.yaml +0 -23
- data/data/config/microservice.yaml +8 -0
- data/data/config/target.yaml +1 -0
- data/data/config/widgets.yaml +6 -23
- data/lib/openc3/core_ext/string.rb +2 -2
- data/lib/openc3/io/json_rpc.rb +6 -2
- data/lib/openc3/microservices/plugin_microservice.rb +10 -1
- data/lib/openc3/models/cvt_model.rb +10 -8
- data/lib/openc3/models/metadata_model.rb +1 -1
- data/lib/openc3/models/microservice_model.rb +14 -2
- data/lib/openc3/models/model.rb +3 -11
- data/lib/openc3/models/note_model.rb +1 -1
- data/lib/openc3/models/timeline_model.rb +2 -3
- data/lib/openc3/models/traefik_model.rb +47 -0
- data/lib/openc3/models/trigger_group_model.rb +2 -3
- data/lib/openc3/script/storage.rb +4 -3
- data/lib/openc3/top_level.rb +1 -16
- data/lib/openc3/utilities/authorization.rb +4 -2
- data/lib/openc3/utilities/aws_bucket.rb +4 -0
- data/lib/openc3/utilities/bucket.rb +4 -0
- data/lib/openc3/utilities/process_manager.rb +6 -5
- data/lib/openc3/version.rb +6 -6
- data/lib/openc3.rb +3 -0
- metadata +5 -6
- data/data/config/cmd_tlm_server.yaml +0 -136
- data/lib/openc3/tools/cmd_tlm_server/cmd_tlm_server_config.rb +0 -323
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 859879129f5646a4e8fd10a916e1e88859a7b5ed178fe2983f42df4e7e8c1a52
|
4
|
+
data.tar.gz: 745a188c5e9277e0bb793207f383f85d6ec6d571d41d6115530f6f78bd671817
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0582ba269983955ae39c8d0c941666d2fcc65811416e3c1ae7026811fa00d9699ecffc09e195909f30cac3c8571f6b83889349fed9c96ec89c95091aff088a61'
|
7
|
+
data.tar.gz: a9c9c554d6595cd7718dc58b1e2e07e0ff9a440cd6545674b47250a3542164a41d27bad1bf237c8c121f170dd04380eeaa125769abc9423c0de2e610578f9799
|
data/Gemfile
CHANGED
@@ -6,13 +6,8 @@ gem 'ruby-termios', '>= 0.9' if RbConfig::CONFIG['target_os'] !~ /mswin|mingw|cy
|
|
6
6
|
|
7
7
|
gemspec :name => 'openc3'
|
8
8
|
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# # Delete the config: bundle config --delete local.mock_redis
|
15
|
-
# group :development do
|
16
|
-
# gem 'mock_redis', github: 'jasonatball/mock_redis', branch: 'master'
|
17
|
-
# end
|
18
|
-
# end
|
9
|
+
# Include the rails gems for the convenience of custom microservice plugins
|
10
|
+
gem 'rails', '~> 7.0.0'
|
11
|
+
gem 'bootsnap', '>= 1.9.3', require: false
|
12
|
+
gem 'rack-cors', '~> 1.1'
|
13
|
+
gem 'tzinfo-data'
|
data/bin/openc3cli
CHANGED
@@ -43,6 +43,8 @@ require 'redis'
|
|
43
43
|
require 'erb'
|
44
44
|
require 'pp'
|
45
45
|
# require 'psych' # Psych code is commented out
|
46
|
+
require "irb"
|
47
|
+
require "irb/completion"
|
46
48
|
|
47
49
|
$redis_url = "redis://#{ENV['OPENC3_REDIS_HOSTNAME']}:#{ENV['OPENC3_REDIS_PORT']}"
|
48
50
|
# Initialize the connection to the bucket library
|
@@ -64,13 +66,14 @@ def print_usage
|
|
64
66
|
puts "Usage:"
|
65
67
|
puts " cli help # Displays this information"
|
66
68
|
puts " cli rake # Runs rake in the local directory"
|
69
|
+
puts " cli irb # Runs irb in the local directory"
|
67
70
|
puts " cli validate /PATH/FILENAME.gem SCOPE variables.txt # Validate a COSMOS plugin gem file"
|
68
71
|
puts " cli load /PATH/FILENAME.gem SCOPE variables.txt # Loads a COSMOS plugin gem file"
|
69
72
|
puts " cli generate plugin PLUGIN_NAME # Generate a COSMOS plugin"
|
70
73
|
puts " #{MIGRATE_PARSER}"
|
71
74
|
puts " cli bridge CONFIG_FILENAME # Run COSMOS host bridge"
|
72
75
|
puts " cli bridgesetup CONFIG_FILENAME # Create a default config file"
|
73
|
-
puts " cli geminstall GEMFILENAME SCOPE
|
76
|
+
puts " cli geminstall GEMFILENAME SCOPE # Install loaded gem to /gems"
|
74
77
|
puts " cli rubysloc # Counts Ruby SLOC recursively. Run with --help for more info."
|
75
78
|
puts " cli xtce_converter # Convert to and from the XTCE format. Run with --help for more info."
|
76
79
|
puts " cli cstol_converter # Converts CSTOL files (.prc) to COSMOS. Run with --help for more info."
|
@@ -526,6 +529,10 @@ if not ARGV[0].nil? # argument(s) given
|
|
526
529
|
# Handle each task
|
527
530
|
case ARGV[0].downcase
|
528
531
|
|
532
|
+
when 'irb'
|
533
|
+
ARGV.clear
|
534
|
+
IRB.start
|
535
|
+
|
529
536
|
when 'rake'
|
530
537
|
puts `rake #{ARGV[1..-1].join(' ')}`
|
531
538
|
|
@@ -27,7 +27,8 @@ tcpip_client_interface.rb:
|
|
27
27
|
- name: Protocol Type
|
28
28
|
required: true
|
29
29
|
description: Protocol to apply on the interface data
|
30
|
-
|
30
|
+
# prettier-ignore
|
31
|
+
values:
|
31
32
|
<%= MetaConfigParser.load('protocols.yaml').to_meta_config_yaml(8) %>
|
32
33
|
tcpip_server_interface.rb:
|
33
34
|
parameters:
|
@@ -52,7 +53,8 @@ tcpip_server_interface.rb:
|
|
52
53
|
- name: Protocol Type
|
53
54
|
required: true
|
54
55
|
description: Protocol to apply on the interface data
|
55
|
-
|
56
|
+
# prettier-ignore
|
57
|
+
values:
|
56
58
|
<%= MetaConfigParser.load('protocols.yaml').to_meta_config_yaml(8) %>
|
57
59
|
udp_interface.rb:
|
58
60
|
description: The UDP interface uses UDP packets to send and receive telemetry
|
@@ -142,73 +144,6 @@ serial_interface.rb:
|
|
142
144
|
- name: Protocol Type
|
143
145
|
required: true
|
144
146
|
description: Protocol to apply on the interface data
|
145
|
-
|
147
|
+
# prettier-ignore
|
148
|
+
values:
|
146
149
|
<%= MetaConfigParser.load('protocols.yaml').to_meta_config_yaml(8) %>
|
147
|
-
cmd_tlm_server_interface.rb:
|
148
|
-
description: Provides a connection to the OpenC3 Command and Telemetry Server.
|
149
|
-
This allows scripts and other OpenC3 tools to send commands to the CmdTlmServer
|
150
|
-
to enable and disable logging. It also allows scripts and other tools to
|
151
|
-
receive a OpenC3 version information packet and a limits change packet which
|
152
|
-
is sent when any telemetry items change limits states. The CmdTlmServer
|
153
|
-
interface can be used by any OpenC3 configuration.
|
154
|
-
linc_interface.rb:
|
155
|
-
description: The LINC interface uses a single TCPIP socket to talk to a Ball
|
156
|
-
Aerospace LINC Labview target.
|
157
|
-
parameters:
|
158
|
-
- name: Host
|
159
|
-
required: true
|
160
|
-
description: Machine name to connect to. Can be either a named
|
161
|
-
machine (DNS entry) or IP address.
|
162
|
-
values: .+
|
163
|
-
- name: Port
|
164
|
-
required: true
|
165
|
-
description: Port to write commands to and read telemetry from
|
166
|
-
values: \d{2,5}
|
167
|
-
- name: Handshake Enabled
|
168
|
-
required: false
|
169
|
-
description: Enable command handshaking where commands block until the
|
170
|
-
corresponding handshake message is received. Default is true.
|
171
|
-
values: ["true", "false"]
|
172
|
-
- name: Response Timeout
|
173
|
-
required: false
|
174
|
-
description: Number of seconds to wait for a handshaking response.
|
175
|
-
Default is 5 seconds
|
176
|
-
values: \d+
|
177
|
-
- name: Read Timeout
|
178
|
-
required: true
|
179
|
-
description: Number of seconds to wait before aborting the read.
|
180
|
-
Pass 'nil' to block on read. Default is nil.
|
181
|
-
values: .+
|
182
|
-
- name: Write Timeout
|
183
|
-
required: true
|
184
|
-
description: Number of seconds to wait before aborting the write.
|
185
|
-
Pass 'nil' to block on write. Default is 5 seconds.
|
186
|
-
values: .+
|
187
|
-
- name: Length Bit Offset
|
188
|
-
required: false
|
189
|
-
description: The bit offset of the length field. Every packet using this
|
190
|
-
interface must have the same structure such that the length field is the
|
191
|
-
same size at the same location. Default is 0.
|
192
|
-
values: \d+
|
193
|
-
- name: Length Bit Size
|
194
|
-
required: false
|
195
|
-
description: The size in bits of the length field. Default is 16 bits.
|
196
|
-
values: \d+
|
197
|
-
- name: Length Value Offset
|
198
|
-
required: false
|
199
|
-
description: The offset to apply to the length field value. For example if
|
200
|
-
the length field indicates packet length minus one, this value should be one.
|
201
|
-
Default is 4.
|
202
|
-
values: \d+
|
203
|
-
- name: GUID Fieldname
|
204
|
-
required: false
|
205
|
-
description: Fieldname of the GUID field. Default is 'HDR_GUID'.
|
206
|
-
values: .+
|
207
|
-
- name: Length Endianness
|
208
|
-
required: false
|
209
|
-
description: The endianness of the length field. Default is BIG_ENDIAN.
|
210
|
-
values: ["BIG_ENDIAN", "LITTLE_ENDIAN"]
|
211
|
-
- name: Length Fieldname
|
212
|
-
required: false
|
213
|
-
description: Fieldname of the length field. Defaults is 'HDR_LENGTH'.
|
214
|
-
values: .+
|
@@ -41,29 +41,6 @@ DISABLE_DISCONNECT:
|
|
41
41
|
Use this keyword to prevent the user from disconnecting from the interface.
|
42
42
|
This is typically used in a 'production' environment where you would not want
|
43
43
|
the user to inadvertantly disconnect from a target.
|
44
|
-
LOG:
|
45
|
-
summary: Enable logging on the interface by the specified log writer
|
46
|
-
description:
|
47
|
-
LOG is only required if you want a log writer other than the default
|
48
|
-
to log commands and telemetry on this interface
|
49
|
-
warning: Choosing a custom log writer can prevent OpenC3 from reading back your log files
|
50
|
-
parameters:
|
51
|
-
- name: Name
|
52
|
-
required: true
|
53
|
-
description: Log writer name as defined by PACKET_LOG_WRITER
|
54
|
-
values: \D\S*
|
55
|
-
LOG_STORED:
|
56
|
-
summary: Enable logging of stored telemetry on the interface by the specified log writer
|
57
|
-
description:
|
58
|
-
LOG_STORED allows you to specify a different log writer for stored telemetry
|
59
|
-
(telemetry which has the 'stored' flag set in the packet). By default the stored telemetry
|
60
|
-
is intermingled in the normal log file.
|
61
|
-
warning: Choosing a custom log writer can prevent OpenC3 from reading back your log files
|
62
|
-
parameters:
|
63
|
-
- name: Name
|
64
|
-
required: true
|
65
|
-
description: Log writer name as defined by PACKET_LOG_WRITER
|
66
|
-
values: \D\S*
|
67
44
|
DONT_LOG:
|
68
45
|
summary: Disable logging commands and telemetry on this interface
|
69
46
|
LOG_RAW:
|
@@ -106,3 +106,11 @@ MICROSERVICE:
|
|
106
106
|
required: true
|
107
107
|
description: Environment variable name or file path to store secret
|
108
108
|
values: .*
|
109
|
+
ROUTE_PREFIX:
|
110
|
+
summary: Prefix of route
|
111
|
+
description: Prefix of route to the microservice to expose externally with Traefik
|
112
|
+
parameters:
|
113
|
+
- name: Route Prefix
|
114
|
+
required: true
|
115
|
+
description: Route prefix. Must be unique across all scopes. Something like /myprefix
|
116
|
+
values: .*
|
data/data/config/target.yaml
CHANGED
@@ -156,6 +156,7 @@ TARGET:
|
|
156
156
|
values: \d+
|
157
157
|
TARGET_MICROSERVICE:
|
158
158
|
summary: Breaks a target microservice out into its own process.
|
159
|
+
description:
|
159
160
|
Can be used to give more resources to processing that is falling behind.
|
160
161
|
If defined multiple times for the same type, will create multiple processes.
|
161
162
|
Each process can be given specific packets to process with the PACKET keyword.
|
data/data/config/widgets.yaml
CHANGED
@@ -162,26 +162,6 @@ TITLE:
|
|
162
162
|
required: true
|
163
163
|
description: Text to display above the horizontal line
|
164
164
|
values: .*
|
165
|
-
# SPACER:
|
166
|
-
# summary: Inserts a spacer into a layout. This can be used to
|
167
|
-
# separate or align other widgets.
|
168
|
-
# parameters:
|
169
|
-
# - name: Width
|
170
|
-
# required: true
|
171
|
-
# description: The width of the spacer in pixels.
|
172
|
-
# values: .*
|
173
|
-
# - name: Height
|
174
|
-
# required: true
|
175
|
-
# description: The height of the spacer in pixels.
|
176
|
-
# values: .*
|
177
|
-
# - name: Horizontal Policy
|
178
|
-
# required: false
|
179
|
-
# description: The horizontal size policy of the spacer. Default is MINIMUM.
|
180
|
-
# values: <%= %w(FIXED MINIMUM MAXIMUM PREFERRED EXPANDING MINIMUMEXPANDING IGNORED) %>
|
181
|
-
# - name: Vertical Policy
|
182
|
-
# required: false
|
183
|
-
# description: The vertical size policy of the spacer. Default is MINIMUM.
|
184
|
-
# values: <%= %w(FIXED MINIMUM MAXIMUM PREFERRED EXPANDING MINIMUMEXPANDING IGNORED) %>
|
185
165
|
ARRAY:
|
186
166
|
summary: Displays ARRAY data organized into rows and space separated
|
187
167
|
parameters:
|
@@ -303,7 +283,8 @@ BLOCK:
|
|
303
283
|
# values: .*
|
304
284
|
FORMATVALUE:
|
305
285
|
summary: Displays a box with a formatted value
|
306
|
-
description:
|
286
|
+
description:
|
287
|
+
Data is formatted by the specified string rather than by a format string given in
|
307
288
|
the telemetry definition files. The white portion of the box darkens to gray
|
308
289
|
while the value remains stagnant, then brightens to white each time the value
|
309
290
|
changes. Additionally the value is colored based on the items limits state
|
@@ -615,7 +596,8 @@ LABELVALUERANGEBAR:
|
|
615
596
|
# values: .*
|
616
597
|
LED:
|
617
598
|
summary: Displays a LED which changes color based on telemetry values
|
618
|
-
description:
|
599
|
+
description:
|
600
|
+
By default TRUE is green and FALSE is red and all other values are black.
|
619
601
|
Additional values can be added by using the LED_COLOR setting. For example
|
620
602
|
LED INST PARAMS VALUE3 RAW can be followed by SETTING LED_COLOR 0 GREEN,
|
621
603
|
SETTING LED_COLOR 1 RED, and SETTING LED_COLOR ANY ORANGE.
|
@@ -1153,7 +1135,8 @@ TEXTBOX:
|
|
1153
1135
|
# values: .*
|
1154
1136
|
VALUE:
|
1155
1137
|
summary: Displays a box with a telemetry item value
|
1156
|
-
description:
|
1138
|
+
description:
|
1139
|
+
The white portion of the box darkens to gray while the value remains stagnant, then
|
1157
1140
|
brightens to white each time the value changes. Additionally the value is
|
1158
1141
|
colored based on the items limits state (Red for example if it is out of limits).
|
1159
1142
|
parameters:
|
@@ -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/packets/binary_accessor'
|
@@ -28,7 +28,7 @@ class String
|
|
28
28
|
# The printable range of ASCII characters
|
29
29
|
PRINTABLE_RANGE = 32..126
|
30
30
|
# Regular expression to identify a character that is not in the printable range
|
31
|
-
NON_PRINTABLE_REGEX = /[^\s
|
31
|
+
NON_PRINTABLE_REGEX = /[^\s!-~]/
|
32
32
|
# Regular expression to identify a String as a floating point number
|
33
33
|
FLOAT_CHECK_REGEX = /\A\s*[-+]?\d*\.\d+\s*\z/
|
34
34
|
# Regular expression to identify a String as a floating point number in
|
data/lib/openc3/io/json_rpc.rb
CHANGED
@@ -56,11 +56,15 @@ end
|
|
56
56
|
|
57
57
|
class String
|
58
58
|
NON_ASCII_PRINTABLE = /[^\x21-\x7e\s]/
|
59
|
-
|
59
|
+
NON_UTF8_PRINTABLE = /[\x00-\x08\x0E-\x1F\x7F]/
|
60
60
|
def as_json(options = nil)
|
61
61
|
as_utf8 = self.dup.force_encoding('UTF-8')
|
62
62
|
if as_utf8.valid_encoding?
|
63
|
-
|
63
|
+
if as_utf8 =~ NON_UTF8_PRINTABLE
|
64
|
+
return self.to_json_raw_object
|
65
|
+
else
|
66
|
+
return as_utf8
|
67
|
+
end
|
64
68
|
else
|
65
69
|
return self.to_json_raw_object
|
66
70
|
end
|
@@ -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/microservices/microservice'
|
@@ -31,6 +31,15 @@ module OpenC3
|
|
31
31
|
|
32
32
|
def run
|
33
33
|
Dir.chdir @work_dir
|
34
|
+
begin
|
35
|
+
if @config["cmd"][0] != 'ruby'
|
36
|
+
# Try to make sure the cmd is executable
|
37
|
+
FileUtils.chmod 0777, @config["cmd"][0]
|
38
|
+
end
|
39
|
+
rescue Exception
|
40
|
+
# Its ok if this fails
|
41
|
+
end
|
42
|
+
|
34
43
|
# Fortify: Process Control
|
35
44
|
# This is dangerous! However, plugins need to be able to run whatever they want.
|
36
45
|
# Only admins can install plugins and they need to be vetted for content.
|
@@ -42,22 +42,24 @@ module OpenC3
|
|
42
42
|
|
43
43
|
# Set an item in the current value table
|
44
44
|
def self.set_item(target_name, packet_name, item_name, value, type:, scope: $openc3_scope)
|
45
|
+
hash = JSON.parse(Store.hget("#{scope}__tlm__#{target_name}", packet_name), :allow_nan => true, :create_additions => true)
|
45
46
|
case type
|
46
47
|
when :WITH_UNITS
|
47
|
-
|
48
|
-
value = value.to_s # WITH_UNITS should always be a string
|
48
|
+
hash["#{item_name}__U"] = value.to_s # WITH_UNITS should always be a string
|
49
49
|
when :FORMATTED
|
50
|
-
|
51
|
-
value = value.to_s # FORMATTED should always be a string
|
50
|
+
hash["#{item_name}__F"] = value.to_s # FORMATTED should always be a string
|
52
51
|
when :CONVERTED
|
53
|
-
|
52
|
+
hash["#{item_name}__C"] = value
|
54
53
|
when :RAW
|
55
|
-
|
54
|
+
hash[item_name] = value
|
55
|
+
when :ALL
|
56
|
+
hash["#{item_name}__U"] = value.to_s # WITH_UNITS should always be a string
|
57
|
+
hash["#{item_name}__F"] = value.to_s # FORMATTED should always be a string
|
58
|
+
hash["#{item_name}__C"] = value
|
59
|
+
hash[item_name] = value
|
56
60
|
else
|
57
61
|
raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}"
|
58
62
|
end
|
59
|
-
hash = JSON.parse(Store.hget("#{scope}__tlm__#{target_name}", packet_name), :allow_nan => true, :create_additions => true)
|
60
|
-
hash[field] = value
|
61
63
|
Store.hset("#{scope}__tlm__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true)))
|
62
64
|
end
|
63
65
|
|
@@ -69,7 +69,7 @@ module OpenC3
|
|
69
69
|
if @color.nil?
|
70
70
|
@color = '#%06x' % (rand * 0xffffff)
|
71
71
|
end
|
72
|
-
unless @color =~ /(#*)([0-9,a-f,A-
|
72
|
+
unless @color =~ /(#*)([0-9,a-f,A-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?('#')
|
@@ -23,6 +23,7 @@
|
|
23
23
|
require 'openc3/top_level'
|
24
24
|
require 'openc3/models/model'
|
25
25
|
require 'openc3/models/metric_model'
|
26
|
+
require 'openc3/models/traefik_model'
|
26
27
|
require 'openc3/utilities/bucket'
|
27
28
|
|
28
29
|
module OpenC3
|
@@ -41,6 +42,7 @@ module OpenC3
|
|
41
42
|
attr_accessor :ports
|
42
43
|
attr_accessor :parent
|
43
44
|
attr_accessor :secrets
|
45
|
+
attr_accessor :prefix
|
44
46
|
|
45
47
|
# NOTE: The following three class methods are used by the ModelController
|
46
48
|
# and are reimplemented to enable various Model class methods to work
|
@@ -99,6 +101,7 @@ module OpenC3
|
|
99
101
|
plugin: nil,
|
100
102
|
needs_dependencies: false,
|
101
103
|
secrets: [],
|
104
|
+
prefix: nil,
|
102
105
|
scope:
|
103
106
|
)
|
104
107
|
parts = name.split("__")
|
@@ -122,6 +125,7 @@ module OpenC3
|
|
122
125
|
@container = container
|
123
126
|
@needs_dependencies = needs_dependencies
|
124
127
|
@secrets = secrets
|
128
|
+
@prefix = prefix
|
125
129
|
@bucket = Bucket.getClient()
|
126
130
|
end
|
127
131
|
|
@@ -141,7 +145,8 @@ module OpenC3
|
|
141
145
|
'updated_at' => @updated_at,
|
142
146
|
'plugin' => @plugin,
|
143
147
|
'needs_dependencies' => @needs_dependencies,
|
144
|
-
'secrets' => @secrets.as_json(*a)
|
148
|
+
'secrets' => @secrets.as_json(*a),
|
149
|
+
'prefix' => @prefix
|
145
150
|
}
|
146
151
|
end
|
147
152
|
|
@@ -190,6 +195,9 @@ module OpenC3
|
|
190
195
|
when 'SECRET'
|
191
196
|
parser.verify_num_parameters(3, 3, "#{keyword} <Secret Type: ENV or FILE> <Secret Name> <Environment Variable Name or File Path>")
|
192
197
|
@secrets << parameters.dup
|
198
|
+
when 'ROUTE_PREFIX'
|
199
|
+
parser.verify_num_parameters(1, 1, "#{keyword} <Route Prefix>")
|
200
|
+
@prefix = parameters[0]
|
193
201
|
else
|
194
202
|
raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Microservice: #{keyword} #{parameters.join(" ")}")
|
195
203
|
end
|
@@ -214,9 +222,12 @@ module OpenC3
|
|
214
222
|
end
|
215
223
|
unless validate_only
|
216
224
|
@bucket.put_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: key, body: data)
|
217
|
-
ConfigTopic.write({ kind: 'created', type: 'microservice', name: @name, plugin: @plugin }, scope: @scope)
|
218
225
|
end
|
219
226
|
end
|
227
|
+
unless validate_only
|
228
|
+
TraefikModel.register_route(microservice_name: @name, port: @ports[0][0], prefix: @prefix) if @ports[0] and ports[0][0] and @prefix
|
229
|
+
ConfigTopic.write({ kind: 'created', type: 'microservice', name: @name, plugin: @plugin }, scope: @scope)
|
230
|
+
end
|
220
231
|
end
|
221
232
|
|
222
233
|
def undeploy
|
@@ -224,6 +235,7 @@ module OpenC3
|
|
224
235
|
@bucket.list_objects(bucket: ENV['OPENC3_CONFIG_BUCKET'], prefix: prefix).each do |object|
|
225
236
|
@bucket.delete_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: object.key)
|
226
237
|
end
|
238
|
+
TraefikModel.unregister_route(microservice_name: @name, port: @ports[0][0], prefix: @prefix) if @ports[0] and ports[0][0] and @prefix
|
227
239
|
ConfigTopic.write({ kind: 'deleted', type: 'microservice', name: @name, plugin: @plugin }, scope: @scope)
|
228
240
|
rescue Exception => error
|
229
241
|
Logger.error("Error undeploying microservice model #{@name} in scope #{@scope} due to #{error}")
|
data/lib/openc3/models/model.rb
CHANGED
@@ -63,20 +63,12 @@ module OpenC3
|
|
63
63
|
# END NOTE
|
64
64
|
|
65
65
|
# Loops over all items and returns objects that match a key value pair
|
66
|
-
def self.filter(key, value, scope:,
|
66
|
+
def self.filter(key, value, scope:, substr: false)
|
67
67
|
filtered = {}
|
68
68
|
results = all(scope: scope)
|
69
|
-
regex = nil
|
70
|
-
regex = Regexp.new(value) if use_regex
|
71
69
|
results.each do |name, result|
|
72
|
-
if
|
73
|
-
|
74
|
-
filtered[name] = result
|
75
|
-
end
|
76
|
-
else
|
77
|
-
if result[key] == value
|
78
|
-
filtered[name] = result
|
79
|
-
end
|
70
|
+
if result[key] == value || (substr && result[key].include?(value))
|
71
|
+
filtered[name] = result
|
80
72
|
end
|
81
73
|
end
|
82
74
|
return filtered
|
@@ -78,7 +78,7 @@ module OpenC3
|
|
78
78
|
if @color.nil?
|
79
79
|
@color = '#%06x' % (rand * 0xffffff)
|
80
80
|
end
|
81
|
-
unless @color =~ /(#*)([0-9,a-f,A-
|
81
|
+
unless @color =~ /(#*)([0-9,a-f,A-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?('#')
|
@@ -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/models/model'
|
@@ -91,11 +91,10 @@ module OpenC3
|
|
91
91
|
if color.nil?
|
92
92
|
color = '#%06x' % (rand * 0xffffff)
|
93
93
|
end
|
94
|
-
valid_color = color =~ /
|
94
|
+
valid_color = color =~ /[0-9,a-f,A-F]{6}/
|
95
95
|
if valid_color.nil?
|
96
96
|
raise RuntimeError.new "invalid color but in hex format. #FF0000"
|
97
97
|
end
|
98
|
-
|
99
98
|
unless color.start_with?('#')
|
100
99
|
color = "##{color}"
|
101
100
|
end
|
@@ -0,0 +1,47 @@
|
|
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/utilities/store'
|
20
|
+
|
21
|
+
module OpenC3
|
22
|
+
class TraefikModel
|
23
|
+
def self.register_route(microservice_name:, port:, prefix:, priority: 20)
|
24
|
+
prefix = '/' + prefix unless prefix[0] == '/'
|
25
|
+
if ENV['KUBERNETES_SERVICE_HOST']
|
26
|
+
url = "http://#{microservice_name.gsub('__', '-')}:#{port}"
|
27
|
+
else
|
28
|
+
url = "http://openc3-operator:#{port}"
|
29
|
+
end
|
30
|
+
service_name = microservice_name
|
31
|
+
router_name = microservice_name
|
32
|
+
Store.set("traefik/http/services/#{service_name}/loadbalancer/servers/0/url", url)
|
33
|
+
Store.set("traefik/http/routers/#{router_name}/service", service_name)
|
34
|
+
Store.set("traefik/http/routers/#{router_name}/priority", priority)
|
35
|
+
Store.set("traefik/http/routers/#{router_name}/rule", "PathPrefix(`#{prefix}`)")
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.unregister_route(microservice_name:)
|
39
|
+
service_name = microservice_name
|
40
|
+
router_name = microservice_name
|
41
|
+
Store.del("traefik/http/routers/#{router_name}/rule")
|
42
|
+
Store.del("traefik/http/routers/#{router_name}/priority")
|
43
|
+
Store.del("traefik/http/routers/#{router_name}/service")
|
44
|
+
Store.del("traefik/http/services/#{service_name}/loadbalancer/servers/0/url")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -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/models/model'
|
@@ -92,11 +92,10 @@ module OpenC3
|
|
92
92
|
if color.nil?
|
93
93
|
color = '#%06x' % (rand * 0xffffff)
|
94
94
|
end
|
95
|
-
valid_color = color =~ /
|
95
|
+
valid_color = color =~ /[0-9,a-f,A-F]{6}/
|
96
96
|
if valid_color.nil?
|
97
97
|
raise TriggerGroupInputError.new "invalid color must be in hex format. #FF0000"
|
98
98
|
end
|
99
|
-
|
100
99
|
unless color.start_with?('#')
|
101
100
|
color = "##{color}"
|
102
101
|
end
|
@@ -65,7 +65,7 @@ module OpenC3
|
|
65
65
|
|
66
66
|
endpoint = "/openc3-api/storage/upload/#{upload_path}"
|
67
67
|
result = _get_presigned_request(endpoint, scope: scope)
|
68
|
-
OpenC3::Logger.info "Writing #{upload_path}
|
68
|
+
OpenC3::Logger.info "Writing #{upload_path}"
|
69
69
|
|
70
70
|
# Try to put the file
|
71
71
|
begin
|
@@ -104,11 +104,12 @@ module OpenC3
|
|
104
104
|
if part == "targets_modified" and ENV['OPENC3_LOCAL_MODE']
|
105
105
|
local_file = OpenC3::LocalMode.open_local_file(path, scope: scope)
|
106
106
|
if local_file
|
107
|
+
OpenC3::Logger.info "Reading local #{scope}/#{path}"
|
107
108
|
file = Tempfile.new('target', binmode: true)
|
108
109
|
file.write(local_file.read)
|
109
110
|
local_file.close
|
110
111
|
file.rewind
|
111
|
-
return file
|
112
|
+
return file
|
112
113
|
end
|
113
114
|
end
|
114
115
|
|
@@ -133,7 +134,7 @@ module OpenC3
|
|
133
134
|
|
134
135
|
endpoint = "/openc3-api/storage/download/#{scope}/#{path}"
|
135
136
|
result = _get_presigned_request(endpoint, scope: scope)
|
136
|
-
OpenC3::Logger.info "Reading #{scope}/#{path}
|
137
|
+
OpenC3::Logger.info "Reading #{scope}/#{path}"
|
137
138
|
|
138
139
|
# Try to get the file
|
139
140
|
uri = _get_uri(result['url'])
|
data/lib/openc3/top_level.rb
CHANGED
@@ -461,22 +461,7 @@ module OpenC3
|
|
461
461
|
|
462
462
|
# @param filename [String] Name of the file to open in the web browser
|
463
463
|
def self.open_in_web_browser(filename)
|
464
|
-
|
465
|
-
if Kernel.is_windows?
|
466
|
-
self.run_process("cmd /c \"start \"\" \"#{filename.gsub('/', '\\')}\"\"")
|
467
|
-
elsif Kernel.is_mac?
|
468
|
-
self.run_process("open -a Safari \"#{filename}\"")
|
469
|
-
else
|
470
|
-
which_firefox = `which firefox`.chomp
|
471
|
-
if which_firefox =~ /Command not found/i or which_firefox =~ /no .* in/i
|
472
|
-
raise "Firefox not found"
|
473
|
-
else
|
474
|
-
system_call = "#{which_firefox} \"#{filename}\""
|
475
|
-
end
|
476
|
-
|
477
|
-
self.run_process(system_call)
|
478
|
-
end
|
479
|
-
end
|
464
|
+
puts "open_in_web_browser is DEPRECATED"
|
480
465
|
end
|
481
466
|
|
482
467
|
# Temporarily set the working directory during a block
|
@@ -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/models/auth_model'
|
@@ -42,7 +42,9 @@ rescue LoadError
|
|
42
42
|
|
43
43
|
if $openc3_authorize
|
44
44
|
raise AuthError.new("Token is required") unless token
|
45
|
-
|
45
|
+
unless OpenC3::AuthModel.verify(token, permission: permission)
|
46
|
+
raise AuthError.new("Current role is invalid for '#{permission}' permission")
|
47
|
+
end
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
@@ -119,6 +119,8 @@ module OpenC3
|
|
119
119
|
end
|
120
120
|
# Array of objects with key and size methods
|
121
121
|
result
|
122
|
+
rescue Aws::S3::Errors::NoSuchBucket => error
|
123
|
+
raise NotFound, "Bucket '#{bucket}' does not exist."
|
122
124
|
end
|
123
125
|
|
124
126
|
# Lists the files under a specified path
|
@@ -164,6 +166,8 @@ module OpenC3
|
|
164
166
|
token = resp.next_continuation_token
|
165
167
|
end
|
166
168
|
result
|
169
|
+
rescue Aws::S3::Errors::NoSuchBucket => error
|
170
|
+
raise NotFound, "Bucket '#{bucket}' does not exist."
|
167
171
|
end
|
168
172
|
|
169
173
|
# put_object fires off the request to store but does not confirm
|
@@ -21,6 +21,10 @@ ENV['OPENC3_CLOUD'] ||= 'local'
|
|
21
21
|
# Interface class implemented by each cloud provider: AWS, GCS, Azure
|
22
22
|
module OpenC3
|
23
23
|
class Bucket
|
24
|
+
# Raised when the underlying bucket does not exist
|
25
|
+
class NotFound < RuntimeError
|
26
|
+
end
|
27
|
+
|
24
28
|
def self.getClient
|
25
29
|
raise 'OPENC3_CLOUD environment variable is required' unless ENV['OPENC3_CLOUD']
|
26
30
|
# Base is AwsBucket which works with MINIO, Enterprise implements additional
|
@@ -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/operators/operator'
|
@@ -52,7 +52,7 @@ module OpenC3
|
|
52
52
|
|
53
53
|
# Spawns short lived processes and ensures they complete
|
54
54
|
class ProcessManager
|
55
|
-
MONITOR_CYCLE_SECONDS =
|
55
|
+
MONITOR_CYCLE_SECONDS = 5
|
56
56
|
CLEANUP_CYCLE_SECONDS = 600
|
57
57
|
|
58
58
|
@@instance = nil
|
@@ -68,6 +68,7 @@ module OpenC3
|
|
68
68
|
begin
|
69
69
|
monitor()
|
70
70
|
rescue => err
|
71
|
+
Logger.error("ProcessManager unexpectedly died\n#{err.formatted}", scope: 'DEFAULT')
|
71
72
|
raise "ProcessManager unexpectedly died\n#{err.formatted}"
|
72
73
|
end
|
73
74
|
end
|
@@ -111,10 +112,10 @@ module OpenC3
|
|
111
112
|
end
|
112
113
|
processes_to_delete.each do |process|
|
113
114
|
if process.status.state == "Complete"
|
114
|
-
Logger.info
|
115
|
+
Logger.info("Process #{process.status.name}:#{process.process_type}:#{process.detail} completed with state #{process.status.state}", scope: process.scope)
|
115
116
|
else
|
116
|
-
Logger.error
|
117
|
-
Logger.error
|
117
|
+
Logger.error("Process #{process.status.name}:#{process.process_type}:#{process.detail} completed with state #{process.status.state}", scope: process.scope)
|
118
|
+
Logger.error("Process Output:\n#{process.status.output}", scope: process.scope)
|
118
119
|
end
|
119
120
|
|
120
121
|
@processes.delete(process)
|
data/lib/openc3/version.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
-
OPENC3_VERSION = '5.4.
|
3
|
+
OPENC3_VERSION = '5.4.3-beta0'
|
4
4
|
module OpenC3
|
5
5
|
module Version
|
6
6
|
MAJOR = '5'
|
7
7
|
MINOR = '4'
|
8
|
-
PATCH = '
|
9
|
-
OTHER = ''
|
10
|
-
BUILD = '
|
8
|
+
PATCH = '3'
|
9
|
+
OTHER = 'pre.beta0'
|
10
|
+
BUILD = '3eeed1efa129ce27f14bffc2b6bab4428a20c500'
|
11
11
|
end
|
12
|
-
VERSION = '5.4.
|
13
|
-
GEM_VERSION = '5.4.
|
12
|
+
VERSION = '5.4.3-beta0'
|
13
|
+
GEM_VERSION = '5.4.3.pre.beta0'
|
14
14
|
end
|
data/lib/openc3.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openc3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.4.
|
4
|
+
version: 5.4.3.pre.beta0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Melton
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-02-
|
12
|
+
date: 2023-02-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -669,7 +669,6 @@ files:
|
|
669
669
|
- data/config/_interfaces.yaml
|
670
670
|
- data/config/_items.yaml
|
671
671
|
- data/config/_params.yaml
|
672
|
-
- data/config/cmd_tlm_server.yaml
|
673
672
|
- data/config/command.yaml
|
674
673
|
- data/config/command_modifiers.yaml
|
675
674
|
- data/config/command_telemetry.yaml
|
@@ -872,6 +871,7 @@ files:
|
|
872
871
|
- lib/openc3/models/timeline_model.rb
|
873
872
|
- lib/openc3/models/tool_config_model.rb
|
874
873
|
- lib/openc3/models/tool_model.rb
|
874
|
+
- lib/openc3/models/traefik_model.rb
|
875
875
|
- lib/openc3/models/trigger_group_model.rb
|
876
876
|
- lib/openc3/models/trigger_model.rb
|
877
877
|
- lib/openc3/models/widget_model.rb
|
@@ -925,7 +925,6 @@ files:
|
|
925
925
|
- lib/openc3/system/system_config.rb
|
926
926
|
- lib/openc3/system/target.rb
|
927
927
|
- lib/openc3/tools/cmd_tlm_server/api.rb
|
928
|
-
- lib/openc3/tools/cmd_tlm_server/cmd_tlm_server_config.rb
|
929
928
|
- lib/openc3/tools/cmd_tlm_server/interface_thread.rb
|
930
929
|
- lib/openc3/tools/table_manager/table.rb
|
931
930
|
- lib/openc3/tools/table_manager/table_config.rb
|
@@ -1012,9 +1011,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
1012
1011
|
version: '2.7'
|
1013
1012
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
1014
1013
|
requirements:
|
1015
|
-
- - "
|
1014
|
+
- - ">"
|
1016
1015
|
- !ruby/object:Gem::Version
|
1017
|
-
version:
|
1016
|
+
version: 1.3.1
|
1018
1017
|
requirements: []
|
1019
1018
|
rubygems_version: 3.3.14
|
1020
1019
|
signing_key:
|
@@ -1,136 +0,0 @@
|
|
1
|
-
---
|
2
|
-
TITLE:
|
3
|
-
summary: Sets the Command and Telemetry Server window title
|
4
|
-
parameters:
|
5
|
-
- name: Text
|
6
|
-
required: true
|
7
|
-
description: Text to put in the title of the Command and Telemetry Server window
|
8
|
-
values: .+
|
9
|
-
PACKET_LOG_WRITER:
|
10
|
-
summary: Declare a packet log writer
|
11
|
-
description: Packet log writer is used to declare a packet log writer class and
|
12
|
-
give it a name which can be referenced by an interface. This is required if you
|
13
|
-
want interfaces to have their own dedicated log writers or want to combine
|
14
|
-
various interfaces into a single log file. By default, OpenC3 logs all data
|
15
|
-
on all interfaces into a single command log and a single telemetry log.
|
16
|
-
This keyword can also be used if you want to declare a different log file class
|
17
|
-
to create log files.
|
18
|
-
warning: You should NOT override the default without consulting a OpenC3 expert
|
19
|
-
as this may break the ability to successfully read and write log files.
|
20
|
-
example: |
|
21
|
-
PACKET_LOG_WRITER DEFAULT packet_log_writer.rb # Default
|
22
|
-
# The default logger filename will be <DATE>_openc3tlm.bin and will create a new log every 1MB
|
23
|
-
PACKET_LOG_WRITER DEFAULT packet_log_writer.rb openc3 true nil 1000000
|
24
|
-
# Create a logger named OPENC3_LOG which creates a new log every 5 min (600s)
|
25
|
-
PACKET_LOG_WRITER OPENC3_LOG packet_log_writer.rb openc3 true 600
|
26
|
-
parameters:
|
27
|
-
- name: Log Writer Name
|
28
|
-
required: true
|
29
|
-
description: The name of the log writer as reference by other cmd_tlm_server keywords.
|
30
|
-
This name also appears in the Logging tab on the Command and Telemetry Server.
|
31
|
-
values: .+
|
32
|
-
- name: Filename
|
33
|
-
required: true
|
34
|
-
description: Ruby file to use when instantiating a new log writer
|
35
|
-
values: '.+\.rb'
|
36
|
-
- name: Parameters
|
37
|
-
required: false
|
38
|
-
description: Optional parameters to pass to the log writer class when instantiating it.
|
39
|
-
AUTO_INTERFACE_TARGETS:
|
40
|
-
summary: Automatically use each target's cmd_tlm_server.txt file to define the interface
|
41
|
-
description: Look for a cmd_tlm_server.txt file at the top level
|
42
|
-
of each target directory and use this file to configure the interface for that target.
|
43
|
-
This is a good way of keeping the knowledge of how to interface to a target
|
44
|
-
within that target. However, if you use substitute target names (by using DECLARE_TARGET)
|
45
|
-
or use different IP addresses then this will not work and you'll have to use the
|
46
|
-
INTERFACE_TARGET or INTERFACE keyword.
|
47
|
-
INTERFACE_TARGET:
|
48
|
-
summary: Load the specified target's cmd_tlm_server.txt configuration file
|
49
|
-
example: |
|
50
|
-
INTERFACE_TARGET OPENC3 # Look in the OPENC3 target directory for cmd_tlm_server.txt
|
51
|
-
INTERFACE_TARGET OPENC3 config.txt # Look in the OPENC3 target directory for config.txt
|
52
|
-
parameters:
|
53
|
-
- name: Target Name
|
54
|
-
required: true
|
55
|
-
description: Name of the target
|
56
|
-
values: .+
|
57
|
-
- name: Configuration File
|
58
|
-
required: false
|
59
|
-
description: Configuration filename which contains the interface configuration.
|
60
|
-
Defaults to 'cmd_tlm_server.txt'.
|
61
|
-
values: .+
|
62
|
-
INTERFACE:
|
63
|
-
modifiers:
|
64
|
-
<%= MetaConfigParser.load('interface_modifiers.yaml').to_meta_config_yaml(4) %>
|
65
|
-
summary: Defines a connection to a physical target
|
66
|
-
description: Interfaces are what OpenC3 uses to talk to a particular piece
|
67
|
-
of hardware. Interfaces require a Ruby file which implements all the interface
|
68
|
-
methods necessary to talk to the hardware. OpenC3 defines many built in interfaces
|
69
|
-
or you can define your own as long as it implements the interface protocol.
|
70
|
-
parameters:
|
71
|
-
- name: Interface Name
|
72
|
-
required: true
|
73
|
-
description: Name of the interface. This name will appear in the
|
74
|
-
Interfaces tab of the Server and is also referenced by other keywords.
|
75
|
-
The OpenC3 convention is to name interfaces after their targets with
|
76
|
-
'_INT' appended to the name, e.g. INST_INT for the INST target.
|
77
|
-
values: \D\S*
|
78
|
-
- name: Filename
|
79
|
-
required: true
|
80
|
-
description: Ruby file to use when instantiating the interface.
|
81
|
-
values:
|
82
|
-
<%= MetaConfigParser.load('_interfaces.yaml').to_meta_config_yaml(8) %>
|
83
|
-
documentation: Additional parameters are required. Please see the [Interfaces](/docs/v5/interfaces)
|
84
|
-
documentation for more details.
|
85
|
-
ROUTER:
|
86
|
-
modifiers:
|
87
|
-
ROUTE:
|
88
|
-
summary: Map an interface to a router
|
89
|
-
description: Once an interface has been mapped to a router, all its received telemetry
|
90
|
-
will be sent out through the router.
|
91
|
-
parameters:
|
92
|
-
- name: Interface
|
93
|
-
required: true
|
94
|
-
description: Name of the interface
|
95
|
-
values: .+
|
96
|
-
<%= MetaConfigParser.load('interface_modifiers.yaml').to_meta_config_yaml(4) %>
|
97
|
-
summary: Create an interface which reverses cmd/tlm data
|
98
|
-
description: Router creates an interface which receives command packets from
|
99
|
-
their remote targets and send them out their interfaces. They receive telemetry
|
100
|
-
packets from their interfaces and send them to their remote targets. This allows
|
101
|
-
routers to be intermediaries between an external client and an actual device.
|
102
|
-
parameters:
|
103
|
-
- name: Name
|
104
|
-
required: true
|
105
|
-
description: Name of the router
|
106
|
-
values: .+
|
107
|
-
- name: Filename
|
108
|
-
required: true
|
109
|
-
description: Ruby file to use when instantiating the interface.
|
110
|
-
values:
|
111
|
-
<%= MetaConfigParser.load('_interfaces.yaml').to_meta_config_yaml(8) %>
|
112
|
-
documentation: Additional parameters are required. Please see the [Interfaces](/docs/v5/interfaces)
|
113
|
-
documentation for more details.
|
114
|
-
COLLECT_METADATA:
|
115
|
-
summary: Prompts the user for meta data when starting the Command and Telemetry Server
|
116
|
-
BACKGROUND_TASK:
|
117
|
-
modifiers:
|
118
|
-
STOPPED:
|
119
|
-
summary: Indicate the background task should not be automatically started
|
120
|
-
summary: Create a background task in the Command and Telemetry Server
|
121
|
-
description: The Server instantiates the class which must inherit from BackgroundTask
|
122
|
-
and then calls the call() method which the class must implement. The call() method
|
123
|
-
is only called once so if your background task is supposed to live on while the
|
124
|
-
Server is running, you must implement your code in a loop with a sleep to not
|
125
|
-
use all the CPU.
|
126
|
-
example: BACKGROUND_TASK example_background_task.rb
|
127
|
-
parameters:
|
128
|
-
- name: Filename
|
129
|
-
required: true
|
130
|
-
description: Ruby file which contains the background task implementation.
|
131
|
-
Must inherit from BackgroundTask and implement the call method.
|
132
|
-
values: '.+\.rb'
|
133
|
-
- name: Optional Arguments
|
134
|
-
required: false
|
135
|
-
description: Optional arguments to the background task constructor
|
136
|
-
values: .+
|
@@ -1,323 +0,0 @@
|
|
1
|
-
# encoding: ascii-8bit
|
2
|
-
|
3
|
-
# Copyright 2022 Ball Aerospace & Technologies Corp.
|
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
|
-
# 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/config/config_parser'
|
24
|
-
|
25
|
-
module OpenC3
|
26
|
-
# Reads an ascii file that defines the configuration settings used to
|
27
|
-
# configure the Command/Telemetry Server.
|
28
|
-
class CmdTlmServerConfig
|
29
|
-
# @return [Hash<String, Interface>] Interfaces hash
|
30
|
-
attr_accessor :interfaces
|
31
|
-
# @return [Hash<String, Interface>] Routers hash
|
32
|
-
attr_accessor :routers
|
33
|
-
# @return [Hash<String, PacketLogWriterPair>] Packet log writer hash. Each
|
34
|
-
# pair encapsulates a command and telemetry log writer.
|
35
|
-
attr_accessor :packet_log_writer_pairs
|
36
|
-
# @return [Array<BackgroundTask>] Array of background tasks
|
37
|
-
attr_accessor :background_tasks
|
38
|
-
# @return [String] Command and Telemetry Server title
|
39
|
-
attr_accessor :title
|
40
|
-
# @return [Boolean] Flag indicating if meta data should be collected
|
41
|
-
attr_accessor :metadata
|
42
|
-
|
43
|
-
# Create a default pair of packet log writers and parses the
|
44
|
-
# configuration file.
|
45
|
-
#
|
46
|
-
# @param filename [String] The name of the configuration file to parse
|
47
|
-
def initialize(filename, system_config)
|
48
|
-
@system_config = system_config
|
49
|
-
@interfaces = {}
|
50
|
-
@routers = {}
|
51
|
-
@packet_log_writer_pairs = {}
|
52
|
-
# cmd_log_writer = System.default_packet_log_writer.new(:CMD, *System.default_packet_log_writer_params)
|
53
|
-
# tlm_log_writer = System.default_packet_log_writer.new(:TLM, *System.default_packet_log_writer_params)
|
54
|
-
# @packet_log_writer_pairs['DEFAULT'] = PacketLogWriterPair.new(cmd_log_writer, tlm_log_writer)
|
55
|
-
@background_tasks = []
|
56
|
-
@title = nil
|
57
|
-
@metadata = false
|
58
|
-
process_file(filename)
|
59
|
-
end
|
60
|
-
|
61
|
-
protected
|
62
|
-
|
63
|
-
def get_target_interface_name(target_name)
|
64
|
-
@interfaces.each do |interface_name, interface|
|
65
|
-
return interface_name if interface.target_names.include?(target_name)
|
66
|
-
end
|
67
|
-
nil
|
68
|
-
end
|
69
|
-
|
70
|
-
def setup_interface_or_router
|
71
|
-
current_interface_or_router = OpenStruct.new
|
72
|
-
current_interface_or_router.interfaces = []
|
73
|
-
current_interface_or_router.routers = []
|
74
|
-
current_interface_or_router.target_names = []
|
75
|
-
current_interface_or_router
|
76
|
-
end
|
77
|
-
|
78
|
-
# Processes a file and adds in the configuration defined in the file
|
79
|
-
#
|
80
|
-
# @param filename [String] The name of the configuration file to parse
|
81
|
-
# @param recursive [Boolean] Whether process_file is being called
|
82
|
-
# recursively
|
83
|
-
def process_file(filename, recursive = false)
|
84
|
-
current_interface_or_router = nil
|
85
|
-
current_type = nil
|
86
|
-
current_interface_log_added = false
|
87
|
-
|
88
|
-
Logger.info "Processing CmdTlmServer configuration in file: #{File.expand_path(filename)}"
|
89
|
-
|
90
|
-
parser = ConfigParser.new("https://openc3.com/docs/v5")
|
91
|
-
parser.parse_file(filename) do |keyword, params|
|
92
|
-
case keyword
|
93
|
-
when 'TITLE'
|
94
|
-
raise parser.error("#{keyword} not allowed in target #{filename}") if recursive
|
95
|
-
|
96
|
-
parser.verify_num_parameters(1, 1, "#{keyword} <Title Text>")
|
97
|
-
@title = params[0]
|
98
|
-
|
99
|
-
when 'PACKET_LOG_WRITER'
|
100
|
-
# usage = "PACKET_LOG_WRITER <Name> <Filename> <Specific Parameters>"
|
101
|
-
# parser.verify_num_parameters(2, nil, usage)
|
102
|
-
# packet_log_writer_name = params[0].upcase
|
103
|
-
# packet_log_writer_class = OpenC3.require_class(params[1])
|
104
|
-
|
105
|
-
# # Verify not overridding a packet log writer that is already associated with an interface
|
106
|
-
# packet_log_writer_pair = @packet_log_writer_pairs[packet_log_writer_name]
|
107
|
-
# if packet_log_writer_pair
|
108
|
-
# @interfaces.each do |interface_name, interface|
|
109
|
-
# if interface.packet_log_writer_pairs.include?(packet_log_writer_pair)
|
110
|
-
# raise parser.error("Redefining Packet Log Writer #{packet_log_writer_name} not allowed after it is associated with an interface")
|
111
|
-
# end
|
112
|
-
# end
|
113
|
-
# end
|
114
|
-
|
115
|
-
# if params[2]
|
116
|
-
# cmd_log_writer = packet_log_writer_class.new(:CMD, *params[2..-1])
|
117
|
-
# tlm_log_writer = packet_log_writer_class.new(:TLM, *params[2..-1])
|
118
|
-
# @packet_log_writer_pairs[packet_log_writer_name] = PacketLogWriterPair.new(cmd_log_writer, tlm_log_writer)
|
119
|
-
# else
|
120
|
-
# cmd_log_writer = packet_log_writer_class.new(:CMD)
|
121
|
-
# tlm_log_writer = packet_log_writer_class.new(:TLM)
|
122
|
-
# @packet_log_writer_pairs[packet_log_writer_name] = PacketLogWriterPair.new(cmd_log_writer, tlm_log_writer)
|
123
|
-
# end
|
124
|
-
|
125
|
-
when 'AUTO_INTERFACE_TARGETS'
|
126
|
-
raise parser.error("#{keyword} not allowed in target #{filename}") if recursive
|
127
|
-
|
128
|
-
usage = "#{keyword}"
|
129
|
-
parser.verify_num_parameters(0, 0, usage)
|
130
|
-
@system_config.targets.each do |target_name, target|
|
131
|
-
target_filename = File.join(target.dir, 'cmd_tlm_server.txt')
|
132
|
-
if File.exist?(target_filename)
|
133
|
-
# Skip this target if it's already been assigned an interface
|
134
|
-
next if get_target_interface_name(target.name)
|
135
|
-
raise parser.error("Cannot use #{keyword} with target name substitutions: #{target.name} != #{target.original_name}") if target.name != target.original_name
|
136
|
-
|
137
|
-
process_file(target_filename, true)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
when 'INTERFACE_TARGET'
|
142
|
-
raise parser.error("#{keyword} not allowed in target #{filename}") if recursive
|
143
|
-
|
144
|
-
usage = "#{keyword} <Target Name> <Config File (defaults to cmd_tlm_server.txt)>"
|
145
|
-
parser.verify_num_parameters(1, 2, usage)
|
146
|
-
target = @system_config.targets[params[0].upcase]
|
147
|
-
raise parser.error("Unknown target: #{params[0].upcase}") unless target
|
148
|
-
|
149
|
-
interface_name = get_target_interface_name(target.name)
|
150
|
-
raise parser.error("Target #{target.name} already mapped to interface #{interface_name}") if interface_name
|
151
|
-
|
152
|
-
target_filename = params[1]
|
153
|
-
target_filename = 'cmd_tlm_server.txt' unless target_filename
|
154
|
-
target_filename = File.join(target.dir, target_filename)
|
155
|
-
if File.exist?(target_filename)
|
156
|
-
process_file(target_filename, true)
|
157
|
-
else
|
158
|
-
raise parser.error("#{target_filename} does not exist")
|
159
|
-
end
|
160
|
-
|
161
|
-
when 'INTERFACE'
|
162
|
-
usage = "INTERFACE <Name> <Filename> <Specific Parameters>"
|
163
|
-
parser.verify_num_parameters(2, nil, usage)
|
164
|
-
interface_name = params[0].upcase
|
165
|
-
raise parser.error("Interface '#{interface_name}' defined twice") if @interfaces[interface_name]
|
166
|
-
|
167
|
-
# interface_class = OpenC3.require_class(params[1])
|
168
|
-
# if params[2]
|
169
|
-
# current_interface_or_router = interface_class.new(*params[2..-1])
|
170
|
-
# else
|
171
|
-
# current_interface_or_router = interface_class.new
|
172
|
-
# end
|
173
|
-
current_interface_or_router = setup_interface_or_router()
|
174
|
-
current_type = :INTERFACE
|
175
|
-
current_interface_log_added = false
|
176
|
-
# current_interface_or_router.packet_log_writer_pairs << @packet_log_writer_pairs['DEFAULT']
|
177
|
-
current_interface_or_router.name = interface_name
|
178
|
-
current_interface_or_router.config_params = params[1..-1]
|
179
|
-
@interfaces[interface_name] = current_interface_or_router
|
180
|
-
|
181
|
-
when 'LOG', 'LOG_STORED', 'DONT_LOG', 'TARGET'
|
182
|
-
raise parser.error("No current interface for #{keyword}") unless current_interface_or_router and current_type == :INTERFACE
|
183
|
-
|
184
|
-
case keyword
|
185
|
-
|
186
|
-
when 'LOG'
|
187
|
-
parser.verify_num_parameters(1, 1, "#{keyword} <Packet Log Writer Name>")
|
188
|
-
# packet_log_writer_pair = @packet_log_writer_pairs[params[0].upcase]
|
189
|
-
# raise parser.error("Unknown packet log writer: #{params[0].upcase}") unless packet_log_writer_pair
|
190
|
-
# current_interface_or_router.packet_log_writer_pairs.delete(@packet_log_writer_pairs['DEFAULT']) unless current_interface_log_added
|
191
|
-
current_interface_log_added = true
|
192
|
-
# current_interface_or_router.packet_log_writer_pairs << packet_log_writer_pair unless current_interface_or_router.packet_log_writer_pairs.include?(packet_log_writer_pair)
|
193
|
-
|
194
|
-
when 'LOG_STORED'
|
195
|
-
parser.verify_num_parameters(1, 1, "#{keyword} <Packet Log Writer Name>")
|
196
|
-
# packet_log_writer_pair = @packet_log_writer_pairs[params[0].upcase]
|
197
|
-
# raise parser.error("Unknown packet log writer: #{params[0].upcase}") unless packet_log_writer_pair
|
198
|
-
# current_interface_or_router.stored_packet_log_writer_pairs << packet_log_writer_pair unless current_interface_or_router.stored_packet_log_writer_pairs.include?(packet_log_writer_pair)
|
199
|
-
|
200
|
-
when 'DONT_LOG'
|
201
|
-
parser.verify_num_parameters(0, 0, "#{keyword}")
|
202
|
-
# current_interface_or_router.packet_log_writer_pairs = []
|
203
|
-
|
204
|
-
when 'TARGET'
|
205
|
-
parser.verify_num_parameters(1, 1, "#{keyword} <Target Name>")
|
206
|
-
target_name = params[0].upcase
|
207
|
-
target = @system_config.targets[target_name]
|
208
|
-
if target
|
209
|
-
interface_name = get_target_interface_name(target.name)
|
210
|
-
raise parser.error("Target #{target.name} already mapped to interface #{interface_name}") if interface_name
|
211
|
-
|
212
|
-
target.interface = current_interface_or_router
|
213
|
-
current_interface_or_router.target_names << target_name
|
214
|
-
else
|
215
|
-
raise parser.error("Unknown target #{target_name} mapped to interface #{current_interface_or_router.name}")
|
216
|
-
end
|
217
|
-
|
218
|
-
end # end case keyword for all keywords that require a current interface
|
219
|
-
|
220
|
-
when 'DONT_CONNECT', 'DONT_RECONNECT', 'RECONNECT_DELAY', 'DISABLE_DISCONNECT', 'LOG_RAW', 'OPTION', 'PROTOCOL'
|
221
|
-
raise parser.error("No current interface or router for #{keyword}") unless current_interface_or_router
|
222
|
-
|
223
|
-
case keyword
|
224
|
-
|
225
|
-
when 'DONT_CONNECT'
|
226
|
-
parser.verify_num_parameters(0, 0, "#{keyword}")
|
227
|
-
current_interface_or_router.connect_on_startup = false
|
228
|
-
|
229
|
-
when 'DONT_RECONNECT'
|
230
|
-
parser.verify_num_parameters(0, 0, "#{keyword}")
|
231
|
-
current_interface_or_router.auto_reconnect = false
|
232
|
-
|
233
|
-
when 'RECONNECT_DELAY'
|
234
|
-
parser.verify_num_parameters(1, 1, "#{keyword} <Delay in Seconds>")
|
235
|
-
current_interface_or_router.reconnect_delay = Float(params[0])
|
236
|
-
|
237
|
-
when 'DISABLE_DISCONNECT'
|
238
|
-
parser.verify_num_parameters(0, 0, "#{keyword}")
|
239
|
-
current_interface_or_router.disable_disconnect = true
|
240
|
-
|
241
|
-
when 'LOG_RAW',
|
242
|
-
parser.verify_num_parameters(0, nil, "#{keyword} <Raw Logger Class File (optional)> <Raw Logger Parameters (optional)>")
|
243
|
-
# current_interface_or_router.raw_logger_pair = RawLoggerPair.new(current_interface_or_router.name, params)
|
244
|
-
# current_interface_or_router.start_raw_logging
|
245
|
-
|
246
|
-
when 'OPTION'
|
247
|
-
parser.verify_num_parameters(2, nil, "#{keyword} <Option Name> <Option Value 1> <Option Value 2 (optional)> <etc>")
|
248
|
-
# current_interface_or_router.set_option(params[0], params[1..-1])
|
249
|
-
|
250
|
-
when 'PROTOCOL'
|
251
|
-
usage = "#{keyword} <READ WRITE READ_WRITE> <protocol filename or classname> <Protocol specific parameters>"
|
252
|
-
parser.verify_num_parameters(2, nil, usage)
|
253
|
-
unless %w(READ WRITE READ_WRITE).include? params[0].upcase
|
254
|
-
raise parser.error("Invalid protocol type: #{params[0]}", usage)
|
255
|
-
end
|
256
|
-
|
257
|
-
begin
|
258
|
-
# klass = OpenC3.require_class(params[1])
|
259
|
-
# current_interface_or_router.add_protocol(klass, params[2..-1], params[0].upcase.intern)
|
260
|
-
rescue LoadError, StandardError => error
|
261
|
-
raise parser.error(error.message, usage)
|
262
|
-
end
|
263
|
-
|
264
|
-
end # end case keyword for all keywords that require a current interface or router
|
265
|
-
|
266
|
-
when 'ROUTER'
|
267
|
-
usage = "ROUTER <Name> <Filename> <Specific Parameters>"
|
268
|
-
parser.verify_num_parameters(2, nil, usage)
|
269
|
-
router_name = params[0].upcase
|
270
|
-
raise parser.error("Router '#{router_name}' defined twice") if @routers[router_name]
|
271
|
-
|
272
|
-
# router_class = OpenC3.require_class(params[1])
|
273
|
-
# if params[2]
|
274
|
-
# current_interface_or_router = router_class.new(*params[2..-1])
|
275
|
-
# else
|
276
|
-
# current_interface_or_router = router_class.new
|
277
|
-
# end
|
278
|
-
current_interface_or_router = setup_interface_or_router()
|
279
|
-
current_type = :ROUTER
|
280
|
-
current_interface_or_router.name = router_name
|
281
|
-
@routers[router_name] = current_interface_or_router
|
282
|
-
|
283
|
-
when 'ROUTE'
|
284
|
-
raise parser.error("No current router for #{keyword}") unless current_interface_or_router and current_type == :ROUTER
|
285
|
-
|
286
|
-
usage = "ROUTE <Interface Name>"
|
287
|
-
parser.verify_num_parameters(1, 1, usage)
|
288
|
-
interface_name = params[0].upcase
|
289
|
-
interface = @interfaces[interface_name]
|
290
|
-
raise parser.error("Unknown interface #{interface_name} mapped to router #{current_interface_or_router.name}") unless interface
|
291
|
-
|
292
|
-
unless current_interface_or_router.interfaces.include? interface
|
293
|
-
current_interface_or_router.interfaces << interface
|
294
|
-
interface.routers << current_interface_or_router
|
295
|
-
end
|
296
|
-
|
297
|
-
when 'BACKGROUND_TASK'
|
298
|
-
usage = "#{keyword} <Filename> <Specific Parameters>"
|
299
|
-
parser.verify_num_parameters(1, nil, usage)
|
300
|
-
# background_task = OpenC3.require_class(params[0])
|
301
|
-
if params[1]
|
302
|
-
@background_tasks << params
|
303
|
-
else
|
304
|
-
@background_tasks << params
|
305
|
-
end
|
306
|
-
|
307
|
-
when 'STOPPED'
|
308
|
-
parser.verify_num_parameters(0, 0, "#{keyword}")
|
309
|
-
raise parser.error("No BACKGROUND_TASK defined") if @background_tasks.empty?
|
310
|
-
# @background_tasks[-1].stopped = true
|
311
|
-
|
312
|
-
when 'COLLECT_METADATA'
|
313
|
-
parser.verify_num_parameters(0, 0, "#{keyword}")
|
314
|
-
@metadata = true
|
315
|
-
|
316
|
-
else
|
317
|
-
# blank lines will have a nil keyword and should not raise an exception
|
318
|
-
raise parser.error("Unknown keyword: #{keyword}") unless keyword.nil?
|
319
|
-
end # case
|
320
|
-
end # loop
|
321
|
-
end
|
322
|
-
end
|
323
|
-
end
|