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.
- 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
|