cosmos 3.1.2 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/Manifest.txt +17 -1
- data/autohotkey/tools/test_runner2.ahk +1 -0
- data/autohotkey/tools/tlm_grapher.ahk +13 -1
- data/data/crc.txt +39 -30
- data/demo/config/data/crc.txt +3 -3
- data/demo/config/targets/TEMPLATED/lib/templated_interface.rb +3 -1
- data/demo/config/tools/cmd_tlm_server/cmd_tlm_server.txt +7 -1
- data/demo/config/tools/cmd_tlm_server/cmd_tlm_server2.txt +6 -1
- data/lib/cosmos.rb +2 -2
- data/lib/cosmos/gui/dialogs/about_dialog.rb +18 -5
- data/lib/cosmos/gui/dialogs/tlm_details_dialog.rb +0 -7
- data/lib/cosmos/gui/line_graph/overview_graph.rb +12 -2
- data/lib/cosmos/gui/utilities/script_module_gui.rb +11 -3
- data/lib/cosmos/interfaces/interface.rb +12 -0
- data/lib/cosmos/interfaces/stream_interface.rb +1 -21
- data/lib/cosmos/interfaces/tcpip_server_interface.rb +10 -0
- data/lib/cosmos/io/json_drb_object.rb +75 -56
- data/lib/cosmos/io/tcpip_server.rb +1 -11
- data/lib/cosmos/packet_logs.rb +1 -0
- data/lib/cosmos/packet_logs/ccsds_log_reader.rb +103 -0
- data/lib/cosmos/packets/packet.rb +70 -1
- data/lib/cosmos/packets/packet_config.rb +59 -611
- data/lib/cosmos/packets/parsers/format_string_parser.rb +58 -0
- data/lib/cosmos/packets/parsers/limits_parser.rb +146 -0
- data/lib/cosmos/packets/parsers/limits_response_parser.rb +52 -0
- data/lib/cosmos/packets/parsers/macro_parser.rb +116 -0
- data/lib/cosmos/packets/parsers/packet_item_parser.rb +215 -0
- data/lib/cosmos/packets/parsers/packet_parser.rb +123 -0
- data/lib/cosmos/packets/parsers/processor_parser.rb +63 -0
- data/lib/cosmos/packets/parsers/state_parser.rb +116 -0
- data/lib/cosmos/packets/structure.rb +59 -22
- data/lib/cosmos/packets/structure_item.rb +1 -1
- data/lib/cosmos/script/script.rb +4 -5
- data/lib/cosmos/streams/serial_stream.rb +5 -0
- data/lib/cosmos/streams/stream.rb +8 -2
- data/lib/cosmos/streams/stream_protocol.rb +1 -0
- data/lib/cosmos/streams/tcpip_client_stream.rb +37 -7
- data/lib/cosmos/streams/tcpip_socket_stream.rb +9 -6
- data/lib/cosmos/system/target.rb +3 -6
- data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_config.rb +57 -48
- data/lib/cosmos/tools/cmd_tlm_server/interface_thread.rb +7 -3
- data/lib/cosmos/tools/limits_monitor/limits_monitor.rb +1 -1
- data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_realtime_thread.rb +7 -1
- data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +1 -2
- data/lib/cosmos/top_level.rb +22 -11
- data/lib/cosmos/utilities/message_log.rb +14 -9
- data/lib/cosmos/version.rb +5 -5
- data/spec/interfaces/cmd_tlm_server_interface_spec.rb +16 -16
- data/spec/interfaces/linc_interface_spec.rb +3 -0
- data/spec/interfaces/tcpip_client_interface_spec.rb +1 -0
- data/spec/interfaces/tcpip_server_interface_spec.rb +9 -0
- data/spec/io/json_drb_object_spec.rb +1 -1
- data/spec/io/serial_driver_spec.rb +0 -1
- data/spec/packet_logs/packet_log_writer_spec.rb +5 -3
- data/spec/packets/packet_config_spec.rb +22 -837
- data/spec/packets/packet_item_spec.rb +10 -10
- data/spec/packets/packet_spec.rb +239 -1
- data/spec/packets/parsers/format_string_parser_spec.rb +122 -0
- data/spec/packets/parsers/limits_parser_spec.rb +282 -0
- data/spec/packets/parsers/limits_response_parser_spec.rb +149 -0
- data/spec/packets/parsers/macro_parser_spec.rb +184 -0
- data/spec/packets/parsers/packet_item_parser_spec.rb +306 -0
- data/spec/packets/parsers/packet_parser_spec.rb +99 -0
- data/spec/packets/parsers/processor_parser_spec.rb +114 -0
- data/spec/packets/parsers/state_parser_spec.rb +156 -0
- data/spec/packets/structure_item_spec.rb +14 -14
- data/spec/packets/structure_spec.rb +162 -16
- data/spec/streams/fixed_stream_protocol_spec.rb +7 -4
- data/spec/streams/length_stream_protocol_spec.rb +3 -0
- data/spec/streams/preidentified_stream_protocol_spec.rb +3 -0
- data/spec/streams/serial_stream_spec.rb +12 -0
- data/spec/streams/stream_protocol_spec.rb +14 -0
- data/spec/streams/stream_spec.rb +1 -0
- data/spec/streams/tcpip_client_stream_spec.rb +3 -0
- data/spec/streams/tcpip_socket_stream_spec.rb +15 -3
- data/spec/streams/template_stream_protocol_spec.rb +5 -0
- data/spec/streams/terminated_stream_protocol_spec.rb +4 -0
- data/spec/tools/cmd_tlm_server/cmd_tlm_server_config_spec.rb +21 -1
- data/spec/tools/cmd_tlm_server/interface_thread_spec.rb +1 -1
- data/spec/tools/cmd_tlm_server/interfaces_spec.rb +1 -1
- metadata +19 -3
@@ -12,8 +12,8 @@ require 'timeout' # For Timeout::Error
|
|
12
12
|
|
13
13
|
module Cosmos
|
14
14
|
|
15
|
-
#
|
16
|
-
# connected? and disconnect. Streams are simply data sources which
|
15
|
+
# Class that implments the following methods: read, write(data),
|
16
|
+
# connect, connected? and disconnect. Streams are simply data sources which
|
17
17
|
# {StreamProtocol} classes read and write to. This separation of concerns
|
18
18
|
# allows Streams to simply focus on getting and sending raw data while the
|
19
19
|
# higher level processing occurs in {StreamProtocol}.
|
@@ -42,12 +42,18 @@ module Cosmos
|
|
42
42
|
raise "write not defined by Stream"
|
43
43
|
end
|
44
44
|
|
45
|
+
# Connects the stream
|
46
|
+
def connect
|
47
|
+
raise "connect not defined by Stream"
|
48
|
+
end
|
49
|
+
|
45
50
|
# @return [Boolean] true if connected or false otherwise
|
46
51
|
def connected?
|
47
52
|
raise "connected? not defined by Stream"
|
48
53
|
end
|
49
54
|
|
50
55
|
# Disconnects the stream
|
56
|
+
# Note that streams are not designed to be reconnected and must be recreated
|
51
57
|
def disconnect
|
52
58
|
raise "disconnect not defined by Stream"
|
53
59
|
end
|
@@ -28,7 +28,7 @@ module Cosmos
|
|
28
28
|
# a write only stream.
|
29
29
|
# @param write_timeout (see TcpipSocketStream#initialize)
|
30
30
|
# @param read_timeout (see TcpipSocketStream#initialize)
|
31
|
-
def initialize(hostname, write_port, read_port, write_timeout, read_timeout)
|
31
|
+
def initialize(hostname, write_port, read_port, write_timeout, read_timeout, connect_timeout = 5.0)
|
32
32
|
@hostname = hostname
|
33
33
|
if (@hostname.to_s.upcase == 'LOCALHOST')
|
34
34
|
@hostname = '127.0.0.1'
|
@@ -41,8 +41,8 @@ module Cosmos
|
|
41
41
|
write_addr = nil
|
42
42
|
read_addr = nil
|
43
43
|
begin
|
44
|
-
write_addr = Socket.pack_sockaddr_in(@write_port, @hostname) if @write_port
|
45
|
-
read_addr
|
44
|
+
@write_addr = Socket.pack_sockaddr_in(@write_port, @hostname) if @write_port
|
45
|
+
@read_addr = Socket.pack_sockaddr_in(@read_port, @hostname) if @read_port
|
46
46
|
rescue => error
|
47
47
|
if error.message =~ /getaddrinfo/
|
48
48
|
raise "Invalid hostname: #{@hostname}"
|
@@ -52,26 +52,56 @@ module Cosmos
|
|
52
52
|
end
|
53
53
|
|
54
54
|
write_socket = nil
|
55
|
-
if write_addr
|
55
|
+
if @write_addr
|
56
56
|
write_socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
57
57
|
write_socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
58
|
-
write_socket.connect(write_addr)
|
59
58
|
end
|
60
59
|
|
61
60
|
read_socket = nil
|
62
|
-
if read_addr
|
61
|
+
if @read_addr
|
63
62
|
if @write_port != @read_port
|
64
63
|
read_socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
65
64
|
read_socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
66
|
-
read_socket.connect(read_addr)
|
67
65
|
else
|
68
66
|
read_socket = write_socket
|
69
67
|
end
|
70
68
|
end
|
71
69
|
|
70
|
+
@connect_timeout = ConfigParser.handle_nil(connect_timeout)
|
71
|
+
@connect_timeout = @connect_timeout.to_f if @connect_timeout
|
72
|
+
|
72
73
|
super(write_socket, read_socket, write_timeout, read_timeout)
|
73
74
|
end
|
74
75
|
|
76
|
+
# Connect the socket(s)
|
77
|
+
def connect
|
78
|
+
connect_nonblock(@write_socket, @write_addr) if @write_socket
|
79
|
+
connect_nonblock(@read_socket, @read_addr) if @read_socket and @read_socket != @write_socket
|
80
|
+
super()
|
81
|
+
end
|
82
|
+
|
83
|
+
protected
|
84
|
+
|
85
|
+
def connect_nonblock(socket, addr)
|
86
|
+
begin
|
87
|
+
socket.connect_nonblock(addr)
|
88
|
+
rescue IO::WaitWritable
|
89
|
+
begin
|
90
|
+
_, sockets, _ = IO.select(nil, [socket], nil, @connect_timeout) # wait 3-way handshake completion
|
91
|
+
rescue Errno::ENOTSOCK
|
92
|
+
raise "Connect canceled"
|
93
|
+
end
|
94
|
+
if sockets and !sockets.empty?
|
95
|
+
begin
|
96
|
+
socket.connect_nonblock(addr) # check connection failure
|
97
|
+
rescue Errno::EISCONN
|
98
|
+
end
|
99
|
+
else
|
100
|
+
raise "Connect timeout"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
75
105
|
end # class TcpipClientStream
|
76
106
|
|
77
107
|
end # module Cosmos
|
@@ -35,7 +35,6 @@ module Cosmos
|
|
35
35
|
@write_timeout = @write_timeout.to_f if @write_timeout
|
36
36
|
@read_timeout = ConfigParser.handle_nil(read_timeout)
|
37
37
|
@read_timeout = @read_timeout.to_f if @read_timeout
|
38
|
-
@connected = true
|
39
38
|
|
40
39
|
# Mutex on write is needed to protect from commands coming in from more
|
41
40
|
# than one tool
|
@@ -120,6 +119,12 @@ module Cosmos
|
|
120
119
|
end
|
121
120
|
end
|
122
121
|
|
122
|
+
# Connect the stream
|
123
|
+
def connect
|
124
|
+
# If called directly this class is acting as a server and does not need to connect the sockets
|
125
|
+
@connected = true
|
126
|
+
end
|
127
|
+
|
123
128
|
# @return [Boolean] Whether the sockets are connected
|
124
129
|
def connected?
|
125
130
|
@connected
|
@@ -127,11 +132,9 @@ module Cosmos
|
|
127
132
|
|
128
133
|
# Disconnect by closing the sockets
|
129
134
|
def disconnect
|
130
|
-
if @
|
131
|
-
|
132
|
-
|
133
|
-
@connected = false
|
134
|
-
end
|
135
|
+
@write_socket.close if @write_socket and !@write_socket.closed?
|
136
|
+
@read_socket.close if @read_socket and !@read_socket.closed?
|
137
|
+
@connected = false
|
135
138
|
end
|
136
139
|
|
137
140
|
end # class TcpipSocketStream
|
data/lib/cosmos/system/target.rb
CHANGED
@@ -135,12 +135,9 @@ module Cosmos
|
|
135
135
|
usage = "#{keyword} <FILENAME>"
|
136
136
|
parser.verify_num_parameters(1, 1, usage)
|
137
137
|
begin
|
138
|
-
|
139
|
-
rescue
|
140
|
-
|
141
|
-
"Ensure #{parameters[0]} is in the COSMOS lib directory."
|
142
|
-
Logger.instance.error msg
|
143
|
-
raise parser.error(msg)
|
138
|
+
Cosmos.require_file(parameters[0])
|
139
|
+
rescue Exception => err
|
140
|
+
raise parser.error(err.message)
|
144
141
|
end
|
145
142
|
@requires << parameters[0]
|
146
143
|
|
@@ -61,9 +61,9 @@ module Cosmos
|
|
61
61
|
# @param recursive [Boolean] Whether process_file is being called
|
62
62
|
# recursively
|
63
63
|
def process_file(filename, recursive = false)
|
64
|
-
|
64
|
+
current_interface_or_router = nil
|
65
|
+
current_type = nil
|
65
66
|
current_interface_log_added = false
|
66
|
-
current_router = nil
|
67
67
|
|
68
68
|
Logger.info "Processing CmdTlmServer configuration in file: #{File.expand_path(filename)}"
|
69
69
|
|
@@ -122,94 +122,103 @@ module Cosmos
|
|
122
122
|
parser.verify_num_parameters(2, nil, usage)
|
123
123
|
interface_class = Cosmos.require_class(params[1])
|
124
124
|
if params[2]
|
125
|
-
|
125
|
+
current_interface_or_router = interface_class.new(*params[2..-1])
|
126
126
|
else
|
127
|
-
|
127
|
+
current_interface_or_router = interface_class.new
|
128
128
|
end
|
129
|
+
current_type = :INTERFACE
|
129
130
|
current_interface_log_added = false
|
130
|
-
|
131
|
-
|
132
|
-
@interfaces[params[0].upcase] =
|
131
|
+
current_interface_or_router.packet_log_writer_pairs << @packet_log_writer_pairs['DEFAULT']
|
132
|
+
current_interface_or_router.name = params[0].upcase
|
133
|
+
@interfaces[params[0].upcase] = current_interface_or_router
|
133
134
|
|
134
|
-
when '
|
135
|
-
raise parser.error("No current interface for #{keyword}") unless
|
135
|
+
when 'LOG', 'DONT_LOG', 'TARGET'
|
136
|
+
raise parser.error("No current interface for #{keyword}") unless current_interface_or_router and current_type == :INTERFACE
|
136
137
|
|
137
138
|
case keyword
|
138
139
|
|
139
|
-
when 'DONT_CONNECT'
|
140
|
-
parser.verify_num_parameters(0, 0, "#{keyword}")
|
141
|
-
current_interface.connect_on_startup = false
|
142
|
-
|
143
|
-
when 'DONT_RECONNECT'
|
144
|
-
parser.verify_num_parameters(0, 0, "#{keyword}")
|
145
|
-
current_interface.auto_reconnect = false
|
146
|
-
|
147
|
-
when 'RECONNECT_DELAY'
|
148
|
-
parser.verify_num_parameters(1, 1, "#{keyword} <Delay in Seconds>")
|
149
|
-
current_interface.reconnect_delay = Float(params[0])
|
150
|
-
|
151
|
-
when 'DISABLE_DISCONNECT'
|
152
|
-
parser.verify_num_parameters(0, 0, "#{keyword}")
|
153
|
-
current_interface.disable_disconnect = true
|
154
|
-
|
155
140
|
when 'LOG'
|
156
141
|
parser.verify_num_parameters(1, 1, "#{keyword} <Packet Log Writer Name>")
|
157
142
|
packet_log_writer_pair = @packet_log_writer_pairs[params[0].upcase]
|
158
143
|
raise parser.error("Unknown packet log writer: #{params[0].upcase}") unless packet_log_writer_pair
|
159
|
-
|
144
|
+
current_interface_or_router.packet_log_writer_pairs.delete(@packet_log_writer_pairs['DEFAULT']) unless current_interface_log_added
|
160
145
|
current_interface_log_added = true
|
161
|
-
|
146
|
+
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)
|
162
147
|
|
163
148
|
when 'DONT_LOG'
|
164
149
|
parser.verify_num_parameters(0, 0, "#{keyword}")
|
165
|
-
|
150
|
+
current_interface_or_router.packet_log_writer_pairs = []
|
166
151
|
|
167
152
|
when 'TARGET'
|
168
153
|
parser.verify_num_parameters(1, 1, "#{keyword} <Target Name>")
|
169
154
|
target_name = params[0].upcase
|
170
155
|
target = System.targets[target_name]
|
171
156
|
if target
|
172
|
-
target.interface =
|
173
|
-
|
157
|
+
target.interface = current_interface_or_router
|
158
|
+
current_interface_or_router.target_names << target_name
|
174
159
|
else
|
175
|
-
raise parser.error("Unknown target #{target_name} mapped to interface #{
|
160
|
+
raise parser.error("Unknown target #{target_name} mapped to interface #{current_interface_or_router.name}")
|
176
161
|
end
|
177
162
|
|
178
|
-
|
163
|
+
end # end case keyword for all keywords that require a current interface
|
164
|
+
|
165
|
+
when 'DONT_CONNECT', 'DONT_RECONNECT', 'RECONNECT_DELAY', 'DISABLE_DISCONNECT', 'LOG_RAW', 'ROUTER_LOG_RAW', 'OPTION'
|
166
|
+
raise parser.error("No current interface or router for #{keyword}") unless current_interface_or_router
|
167
|
+
|
168
|
+
case keyword
|
169
|
+
|
170
|
+
when 'DONT_CONNECT'
|
171
|
+
parser.verify_num_parameters(0, 0, "#{keyword}")
|
172
|
+
current_interface_or_router.connect_on_startup = false
|
173
|
+
|
174
|
+
when 'DONT_RECONNECT'
|
175
|
+
parser.verify_num_parameters(0, 0, "#{keyword}")
|
176
|
+
current_interface_or_router.auto_reconnect = false
|
177
|
+
|
178
|
+
when 'RECONNECT_DELAY'
|
179
|
+
parser.verify_num_parameters(1, 1, "#{keyword} <Delay in Seconds>")
|
180
|
+
current_interface_or_router.reconnect_delay = Float(params[0])
|
181
|
+
|
182
|
+
when 'DISABLE_DISCONNECT'
|
183
|
+
parser.verify_num_parameters(0, 0, "#{keyword}")
|
184
|
+
current_interface_or_router.disable_disconnect = true
|
185
|
+
|
186
|
+
# TODO: Deprecate ROUTER_LOG_RAW
|
187
|
+
when 'LOG_RAW', 'ROUTER_LOG_RAW'
|
179
188
|
parser.verify_num_parameters(0, nil, "#{keyword} <Raw Logger Class File (optional)> <Raw Logger Parameters (optional)>")
|
180
|
-
|
189
|
+
current_interface_or_router.raw_logger_pair = RawLoggerPair.new(current_interface_or_router.name, params)
|
181
190
|
|
182
|
-
|
191
|
+
when 'OPTION'
|
192
|
+
parser.verify_num_parameters(2, nil, "#{keyword} <Option Name> <Option Value 1> <Option Value 2 (optional)> <etc>")
|
193
|
+
current_interface_or_router.set_option(params[0], params[1..-1])
|
194
|
+
|
195
|
+
end # end case keyword for all keywords that require a current interface or router
|
183
196
|
|
184
197
|
when 'ROUTER'
|
185
198
|
usage = "ROUTER <Name> <Filename> <Specific Parameters>"
|
186
199
|
parser.verify_num_parameters(2, nil, usage)
|
187
200
|
router_class = Cosmos.require_class(params[1])
|
188
201
|
if params[2]
|
189
|
-
|
202
|
+
current_interface_or_router = router_class.new(*params[2..-1])
|
190
203
|
else
|
191
|
-
|
204
|
+
current_interface_or_router = router_class.new
|
192
205
|
end
|
193
|
-
|
194
|
-
|
206
|
+
current_type = :ROUTER
|
207
|
+
current_interface_or_router.name = params[0].upcase
|
208
|
+
@routers[params[0].upcase] = current_interface_or_router
|
195
209
|
|
196
210
|
when 'ROUTE'
|
197
|
-
raise parser.error("No current router for #{keyword}") unless
|
211
|
+
raise parser.error("No current router for #{keyword}") unless current_interface_or_router and current_type == :ROUTER
|
198
212
|
usage = "ROUTE <Interface Name>"
|
199
213
|
parser.verify_num_parameters(1, 1, usage)
|
200
214
|
interface_name = params[0].upcase
|
201
215
|
interface = @interfaces[interface_name]
|
202
|
-
raise parser.error("Unknown interface #{interface_name} mapped to router #{
|
203
|
-
unless
|
204
|
-
|
205
|
-
interface.routers <<
|
216
|
+
raise parser.error("Unknown interface #{interface_name} mapped to router #{current_interface_or_router.name}") unless interface
|
217
|
+
unless current_interface_or_router.interfaces.include? interface
|
218
|
+
current_interface_or_router.interfaces << interface
|
219
|
+
interface.routers << current_interface_or_router
|
206
220
|
end
|
207
221
|
|
208
|
-
when 'ROUTER_LOG_RAW'
|
209
|
-
raise parser.error("No current router for #{keyword}") unless current_router
|
210
|
-
parser.verify_num_parameters(0, nil, "#{keyword} <Raw Logger Class File (optional)> <Raw Logger Parameters (optional)>")
|
211
|
-
current_router.raw_logger_pair = RawLoggerPair.new(current_router.name, params)
|
212
|
-
|
213
222
|
when 'BACKGROUND_TASK'
|
214
223
|
usage = "#{keyword} <Filename> <Specific Parameters>"
|
215
224
|
parser.verify_num_parameters(1, nil, usage)
|
@@ -183,12 +183,16 @@ module Cosmos
|
|
183
183
|
if @connection_failed_callback
|
184
184
|
@connection_failed_callback.call(connect_error)
|
185
185
|
else
|
186
|
-
Logger.error "#{@interface.name} Connection Failed: #{connect_error.
|
186
|
+
Logger.error "#{@interface.name} Connection Failed: #{connect_error.class}:#{connect_error.message}"
|
187
187
|
case connect_error
|
188
|
-
when Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT
|
188
|
+
when Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::ENOTSOCK
|
189
189
|
# Do not write an exception file for these extremely common cases
|
190
190
|
else
|
191
|
-
|
191
|
+
if RuntimeError === connect_error and (connect_error.message =~ /canceled/ or connect_error.message =~ /timeout/)
|
192
|
+
# Do not write an exception file for these extremely common cases
|
193
|
+
else
|
194
|
+
Cosmos.write_exception_file(connect_error)
|
195
|
+
end
|
192
196
|
end
|
193
197
|
end
|
194
198
|
disconnect()
|
@@ -64,7 +64,13 @@ module Cosmos
|
|
64
64
|
end # def kill
|
65
65
|
|
66
66
|
def graceful_kill
|
67
|
-
#
|
67
|
+
# Allow the callbacks a chance to update the GUI so that they can die gracefully
|
68
|
+
if defined? Qt and Thread.current == Thread.main
|
69
|
+
5.times do
|
70
|
+
Qt::CoreApplication.instance.processEvents
|
71
|
+
sleep(0.05)
|
72
|
+
end
|
73
|
+
end
|
68
74
|
end
|
69
75
|
|
70
76
|
end # class TabbedPlotsRealtimeThread
|
@@ -433,9 +433,8 @@ module Cosmos
|
|
433
433
|
event.ignore()
|
434
434
|
else
|
435
435
|
# Close any open screens
|
436
|
-
script_disconnect()
|
437
|
-
Screen.close_all_screens(self)
|
438
436
|
shutdown_cmd_tlm()
|
437
|
+
Screen.close_all_screens(self)
|
439
438
|
@json_drb.stop_service if @json_drb
|
440
439
|
super(event)
|
441
440
|
end
|
data/lib/cosmos/top_level.rb
CHANGED
@@ -527,13 +527,7 @@ module Cosmos
|
|
527
527
|
def self.require_class(class_filename)
|
528
528
|
class_name = class_filename.filename_to_class_name
|
529
529
|
return class_name.to_class if class_name.to_class and defined? class_name.to_class
|
530
|
-
|
531
|
-
require class_filename
|
532
|
-
rescue LoadError => err
|
533
|
-
msg = "Unable to require #{class_filename} due to #{err.message}. Ensure #{class_filename} is in the COSMOS lib directory."
|
534
|
-
Logger.error msg
|
535
|
-
raise msg
|
536
|
-
end
|
530
|
+
self.require_file(class_filename)
|
537
531
|
klass = class_name.to_class
|
538
532
|
if klass
|
539
533
|
return klass
|
@@ -542,6 +536,19 @@ module Cosmos
|
|
542
536
|
end
|
543
537
|
end
|
544
538
|
|
539
|
+
# Requires a file with a standard error message if it fails
|
540
|
+
#
|
541
|
+
# @param filename [String] The name of the file to require
|
542
|
+
def self.require_file(filename)
|
543
|
+
begin
|
544
|
+
require filename
|
545
|
+
rescue Exception => err
|
546
|
+
msg = "Unable to require #{filename} due to #{err.message}. Ensure #{filename} is in the COSMOS lib directory."
|
547
|
+
Logger.error msg
|
548
|
+
raise msg
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
545
552
|
# @param filename [String] Name of the file to open in the editor
|
546
553
|
def self.open_in_text_editor(filename)
|
547
554
|
if filename
|
@@ -621,10 +628,14 @@ module Cosmos
|
|
621
628
|
def self.kill_thread(owner, thread, graceful_timeout = 1, timeout_interval = 0.01, hard_timeout = 1)
|
622
629
|
if thread
|
623
630
|
if owner and owner.respond_to? :graceful_kill
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
631
|
+
if Thread.current != thread
|
632
|
+
owner.graceful_kill
|
633
|
+
end_time = Time.now + graceful_timeout
|
634
|
+
while thread.alive? && ((end_time - Time.now) > 0)
|
635
|
+
sleep(timeout_interval)
|
636
|
+
end
|
637
|
+
else
|
638
|
+
Logger.warn "Threads cannot graceful_kill themselves"
|
628
639
|
end
|
629
640
|
elsif owner
|
630
641
|
Logger.info "Thread owner #{owner.class} does not support graceful_kill"
|