cosmos 4.0.3 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +5 -5
- data/Manifest.txt +11 -1
- data/README.md +3 -2
- data/Rakefile +18 -4
- data/appveyor.yml +19 -0
- data/cosmos.gemspec +14 -3
- data/data/config/cmd_tlm_server.yaml +3 -0
- data/data/crc.txt +63 -60
- data/demo/config/targets/INST/cmd_tlm_server.txt +1 -0
- data/demo/config/targets/INST/cmd_tlm_server2.txt +7 -0
- data/demo/config/tools/cmd_sequence/cmd_sequence.txt +2 -0
- data/demo/config/tools/cmd_tlm_server/cmd_tlm_server.txt +8 -12
- data/demo/config/tools/cmd_tlm_server/cmd_tlm_server2.txt +7 -9
- data/demo/lib/cmd_sequence_exporter.rb +52 -0
- data/demo/lib/example_background_task.rb +1 -0
- data/demo/procedures/replay_test.rb +32 -0
- data/ext/cosmos/ext/structure/structure.c +39 -3
- data/install/config/tools/cmd_tlm_server/cmd_tlm_server.txt +1 -0
- data/install/config/tools/launcher/launcher.txt +2 -0
- data/lib/cosmos/config/config_parser.rb +2 -0
- data/lib/cosmos/core_ext/io.rb +89 -60
- data/lib/cosmos/gui/qt.rb +5 -8
- data/lib/cosmos/gui/qt_tool.rb +8 -8
- data/lib/cosmos/gui/text/ruby_editor.rb +12 -12
- data/lib/cosmos/gui/utilities/script_module_gui.rb +9 -9
- data/lib/cosmos/gui/widgets/realtime_button_bar.rb +18 -17
- data/lib/cosmos/interfaces/protocols/fixed_protocol.rb +2 -2
- data/lib/cosmos/interfaces/protocols/template_protocol.rb +3 -0
- data/lib/cosmos/interfaces/udp_interface.rb +27 -14
- data/lib/cosmos/io/buffered_file.rb +0 -1
- data/lib/cosmos/io/json_drb.rb +134 -214
- data/lib/cosmos/io/json_drb_object.rb +22 -61
- data/lib/cosmos/io/json_drb_rack.rb +79 -0
- data/lib/cosmos/io/json_rpc.rb +27 -0
- data/lib/cosmos/io/udp_sockets.rb +102 -58
- data/lib/cosmos/packets/commands.rb +1 -1
- data/lib/cosmos/packets/structure.rb +1 -1
- data/lib/cosmos/packets/structure_item.rb +37 -5
- data/lib/cosmos/script/cmd_tlm_server.rb +76 -2
- data/lib/cosmos/script/replay.rb +60 -0
- data/lib/cosmos/script/script.rb +20 -2
- data/lib/cosmos/script/scripting.rb +9 -9
- data/lib/cosmos/script/tools.rb +14 -0
- data/lib/cosmos/system/system.rb +185 -92
- data/lib/cosmos/system/target.rb +1 -1
- data/lib/cosmos/tools/cmd_sequence/cmd_sequence.rb +44 -4
- data/lib/cosmos/tools/cmd_sequence/sequence_item.rb +4 -0
- data/lib/cosmos/tools/cmd_sequence/sequence_list.rb +7 -0
- data/lib/cosmos/tools/cmd_tlm_server/api.rb +347 -20
- data/lib/cosmos/tools/cmd_tlm_server/background_tasks.rb +3 -0
- data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server.rb +329 -111
- data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_config.rb +13 -0
- data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_gui.rb +261 -95
- data/lib/cosmos/tools/cmd_tlm_server/gui/interfaces_tab.rb +46 -35
- data/lib/cosmos/tools/cmd_tlm_server/gui/logging_tab.rb +18 -8
- data/lib/cosmos/tools/cmd_tlm_server/gui/packets_tab.rb +39 -28
- data/lib/cosmos/tools/cmd_tlm_server/gui/replay_tab.rb +242 -0
- data/lib/cosmos/tools/cmd_tlm_server/gui/status_tab.rb +24 -8
- data/lib/cosmos/tools/cmd_tlm_server/gui/targets_tab.rb +18 -6
- data/lib/cosmos/tools/cmd_tlm_server/limits_groups_background_task.rb +5 -4
- data/lib/cosmos/tools/cmd_tlm_server/replay_backend.rb +375 -0
- data/lib/cosmos/tools/cmd_tlm_server/routers.rb +10 -2
- data/lib/cosmos/tools/data_viewer/data_viewer.rb +40 -5
- data/lib/cosmos/tools/handbook_creator/handbook_creator_config.rb +18 -20
- data/lib/cosmos/tools/launcher/launcher_config.rb +5 -16
- data/lib/cosmos/tools/limits_monitor/limits_monitor.rb +65 -39
- data/lib/cosmos/tools/packet_viewer/packet_viewer.rb +19 -0
- data/lib/cosmos/tools/replay/replay.rb +5 -505
- data/lib/cosmos/tools/script_runner/script_audit.rb +1 -0
- data/lib/cosmos/tools/script_runner/script_runner.rb +3 -4
- data/lib/cosmos/tools/script_runner/script_runner_config.rb +3 -4
- data/lib/cosmos/tools/script_runner/script_runner_frame.rb +44 -23
- data/lib/cosmos/tools/test_runner/results_writer.rb +4 -0
- data/lib/cosmos/tools/test_runner/test_runner.rb +0 -3
- data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_realtime_thread.rb +6 -2
- data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_tool.rb +26 -1
- data/lib/cosmos/tools/tlm_viewer/screen.rb +24 -1
- data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +25 -0
- data/lib/cosmos/tools/tlm_viewer/tlm_viewer_config.rb +24 -14
- data/lib/cosmos/top_level.rb +34 -24
- data/lib/cosmos/utilities/csv.rb +60 -8
- data/lib/cosmos/version.rb +5 -5
- data/spec/config/config_parser_spec.rb +10 -1
- data/spec/core_ext/socket_spec.rb +4 -2
- data/spec/gui/utilities/script_module_gui_spec.rb +102 -0
- data/spec/install/config/data/data.txt +1 -0
- data/spec/install/config/targets/INST/cmd_tlm/inst_cmds.txt +2 -0
- data/spec/interfaces/cmd_tlm_server_interface_spec.rb +1 -2
- data/spec/interfaces/protocols/template_protocol_spec.rb +72 -2
- data/spec/interfaces/serial_interface_spec.rb +1 -1
- data/spec/interfaces/udp_interface_spec.rb +14 -0
- data/spec/io/buffered_file_spec.rb +37 -0
- data/spec/io/json_drb_object_spec.rb +2 -15
- data/spec/io/json_drb_spec.rb +61 -121
- data/spec/io/udp_sockets_spec.rb +42 -2
- data/spec/packet_logs/packet_log_reader_spec.rb +5 -2
- data/spec/packets/binary_accessor_spec.rb +1 -1
- data/spec/packets/packet_item_spec.rb +1 -1
- data/spec/packets/structure_item_spec.rb +5 -6
- data/spec/script/cmd_tlm_server_spec.rb +39 -4
- data/spec/script/commands_disconnect_spec.rb +1 -1
- data/spec/script/commands_spec.rb +2 -1
- data/spec/script/scripting_spec.rb +18 -3
- data/spec/script/telemetry_spec.rb +5 -0
- data/spec/spec_helper.rb +43 -26
- data/spec/streams/tcpip_socket_stream_spec.rb +2 -2
- data/spec/system/system_spec.rb +11 -9
- data/spec/system/target_spec.rb +3 -0
- data/spec/tools/cmd_tlm_server/api_spec.rb +543 -29
- data/spec/tools/cmd_tlm_server/background_task_spec.rb +2 -2
- data/spec/tools/cmd_tlm_server/background_tasks_spec.rb +31 -75
- data/spec/tools/cmd_tlm_server/cmd_tlm_server_config_spec.rb +199 -66
- data/spec/tools/cmd_tlm_server/cmd_tlm_server_spec.rb +85 -9
- data/spec/tools/cmd_tlm_server/interface_thread_spec.rb +29 -127
- data/spec/tools/cmd_tlm_server/router_thread_spec.rb +10 -50
- data/spec/tools/launcher/launcher_config_spec.rb +1 -1
- data/spec/tools/table_manager/table_item_spec.rb +1 -1
- data/spec/tools/table_manager/tablemanager_core_spec.rb +4 -4
- data/spec/top_level/top_level_spec.rb +151 -3
- data/spec/utilities/csv_spec.rb +24 -5
- metadata +61 -9
- data/lib/cosmos/tools/replay/replay_server.rb +0 -91
@@ -34,6 +34,8 @@ module Cosmos
|
|
34
34
|
# calling the 'call' method once.
|
35
35
|
# @param index [Integer] Which background task to start
|
36
36
|
def start(index)
|
37
|
+
raise "No task at index #{index}. There are #{@config.background_tasks.length} total tasks." unless index < @config.background_tasks.length
|
38
|
+
return if @threads[index] # Don't re-create a running thread. They must call stop first.
|
37
39
|
@threads[index] = Thread.new do
|
38
40
|
@config.background_tasks[index].thread = Thread.current
|
39
41
|
begin
|
@@ -56,6 +58,7 @@ module Cosmos
|
|
56
58
|
# Ruby thread.
|
57
59
|
# @param index [Integer] Which background task to stop
|
58
60
|
def stop(index)
|
61
|
+
raise "No task at index #{index}. There are #{@config.background_tasks.length} total tasks." unless index < @config.background_tasks.length
|
59
62
|
begin
|
60
63
|
@config.background_tasks[index].stop
|
61
64
|
rescue
|
@@ -17,6 +17,7 @@ require 'cosmos/tools/cmd_tlm_server/commanding'
|
|
17
17
|
require 'cosmos/tools/cmd_tlm_server/interfaces'
|
18
18
|
require 'cosmos/tools/cmd_tlm_server/packet_logging'
|
19
19
|
require 'cosmos/tools/cmd_tlm_server/routers'
|
20
|
+
require 'cosmos/tools/cmd_tlm_server/replay_backend'
|
20
21
|
|
21
22
|
module Cosmos
|
22
23
|
|
@@ -39,6 +40,8 @@ module Cosmos
|
|
39
40
|
instance_attr_reader :packet_logging
|
40
41
|
# @return [Routers] Access to the routers
|
41
42
|
instance_attr_reader :routers
|
43
|
+
# @return [ReplayBackend] Access to replay logic
|
44
|
+
instance_attr_reader :replay_backend
|
42
45
|
# @return [MessageLog] Message log for the CmdTlmServer
|
43
46
|
instance_attr_reader :message_log
|
44
47
|
# @return [JsonDRb] Provides access to the server for all tools both
|
@@ -46,6 +49,8 @@ module Cosmos
|
|
46
49
|
instance_attr_accessor :json_drb
|
47
50
|
# @return [String] CmdTlmServer title as set in the config file
|
48
51
|
instance_attr_accessor :title
|
52
|
+
# @return [Symbol] mode :CMD_TLM_SERVER or :REPLAY
|
53
|
+
instance_attr_accessor :mode
|
49
54
|
|
50
55
|
# attr_reader attributes are only used by CmdTlmServer internally and are
|
51
56
|
# thus only available as attributes on the singleton
|
@@ -70,6 +75,18 @@ module Cosmos
|
|
70
75
|
# subscribe_packet_data is called. This ID must be used in the
|
71
76
|
# packet_data_queues hash to access the queue.
|
72
77
|
attr_accessor :next_packet_data_queue_id
|
78
|
+
# @return [Mutex] Synchronization object around server messages
|
79
|
+
attr_reader :server_message_queue_mutex
|
80
|
+
# @return [Hash<Integer, Array<Queue, Integer>>] The server message queues
|
81
|
+
# hashed by id. Returns an array containing the queue followed by the
|
82
|
+
# queue size.
|
83
|
+
attr_reader :server_message_queues
|
84
|
+
# @return [Integer] The next server message queue id when
|
85
|
+
# subscribe_server_messages is called. This ID must be used in the
|
86
|
+
# server_message_queues hash to access the queue.
|
87
|
+
attr_accessor :next_server_message_queue_id
|
88
|
+
# @return [Boolean] Whether the server was created in disconnect mode
|
89
|
+
attr_reader :disconnect
|
73
90
|
|
74
91
|
# The default configuration file name
|
75
92
|
DEFAULT_CONFIG_FILE = File.join(Cosmos::USERPATH, 'config', 'tools', 'cmd_tlm_server', 'cmd_tlm_server.txt')
|
@@ -79,6 +96,9 @@ module Cosmos
|
|
79
96
|
# The maximum number of packets that are queued. Used when subscribing to
|
80
97
|
# packet data.
|
81
98
|
DEFAULT_PACKET_DATA_QUEUE_SIZE = 1000
|
99
|
+
# The maximum number of server messages that are queued. Used when subscribing to
|
100
|
+
# server messages.
|
101
|
+
DEFAULT_SERVER_MESSAGES_QUEUE_SIZE = 1000
|
82
102
|
|
83
103
|
@@instance = nil
|
84
104
|
@@meta_callback = nil
|
@@ -93,7 +113,8 @@ module Cosmos
|
|
93
113
|
@@meta_callback = meta_callback
|
94
114
|
end
|
95
115
|
|
96
|
-
# Constructor for a CmdTlmServer
|
116
|
+
# Constructor for a CmdTlmServer. Initializes all internal state and
|
117
|
+
# starts up the sever
|
97
118
|
#
|
98
119
|
# @param config_file [String] The name of the server configuration file
|
99
120
|
# which must be in the config/tools/cmd_tlm_server directory.
|
@@ -104,13 +125,16 @@ module Cosmos
|
|
104
125
|
# stand-alone mode which does not actually use the interfaces to send and
|
105
126
|
# receive data. This is useful for testing scripts when actual hardware
|
106
127
|
# is not available.
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
128
|
+
# @param mode [Symbol] :CMD_TLM_SERVER or :REPLAY - Defines overall mode
|
129
|
+
def initialize(
|
130
|
+
config_file = DEFAULT_CONFIG_FILE,
|
131
|
+
production = false,
|
132
|
+
disconnect = false,
|
133
|
+
mode = :CMD_TLM_SERVER)
|
134
|
+
|
111
135
|
@@instance = self
|
112
136
|
@packet_logging = nil # Removes warnings
|
113
|
-
@
|
137
|
+
@mode = mode
|
114
138
|
|
115
139
|
super() # For Api
|
116
140
|
|
@@ -121,10 +145,12 @@ module Cosmos
|
|
121
145
|
@limits_event_queue_mutex = Mutex.new
|
122
146
|
@limits_event_queues = {}
|
123
147
|
@next_limits_event_queue_id = 1
|
124
|
-
|
125
148
|
@packet_data_queue_mutex = Mutex.new
|
126
149
|
@packet_data_queues = {}
|
127
150
|
@next_packet_data_queue_id = 1
|
151
|
+
@server_message_queue_mutex = Mutex.new
|
152
|
+
@server_message_queues = {}
|
153
|
+
@next_server_message_queue_id = 1
|
128
154
|
|
129
155
|
# Process cmd_tlm_server.txt
|
130
156
|
@config = CmdTlmServerConfig.new(config_file)
|
@@ -133,8 +159,13 @@ module Cosmos
|
|
133
159
|
@interfaces = Interfaces.new(@config, method(:identified_packet_callback))
|
134
160
|
@packet_logging = PacketLogging.new(@config)
|
135
161
|
@routers = Routers.new(@config)
|
162
|
+
@replay_backend = ReplayBackend.new(@config)
|
136
163
|
@title = @config.title
|
164
|
+
if @mode != :CMD_TLM_SERVER
|
165
|
+
@title.gsub!("Command and Telemetry Server", "Replay")
|
166
|
+
end
|
137
167
|
@stop_callback = nil
|
168
|
+
@reload_callback = nil
|
138
169
|
|
139
170
|
# Set Threads to kill CTS if they throw an exception
|
140
171
|
Thread.abort_on_exception = true
|
@@ -142,113 +173,98 @@ module Cosmos
|
|
142
173
|
# Don't start the DRb service or the telemetry monitoring thread
|
143
174
|
# if we started the server in disconnect mode
|
144
175
|
@json_drb = nil
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
if production
|
167
|
-
@packet_logging.start
|
168
|
-
@api_whitelist.delete('stop_logging')
|
169
|
-
@api_whitelist.delete('stop_cmd_log')
|
170
|
-
@api_whitelist.delete('stop_tlm_log')
|
171
|
-
@interfaces.all.each do |name, interface|
|
172
|
-
interface.disable_disconnect = true
|
173
|
-
end
|
174
|
-
@routers.all.each do |name, interface|
|
175
|
-
interface.disable_disconnect = true
|
176
|
+
unless @disconnect
|
177
|
+
System.telemetry # Make sure definitions are loaded by starting anything
|
178
|
+
|
179
|
+
@@meta_callback.call() if @@meta_callback and @config.metadata
|
180
|
+
|
181
|
+
# Start DRb with access control
|
182
|
+
@json_drb = JsonDRb.new
|
183
|
+
@json_drb.acl = System.acl if System.acl
|
184
|
+
|
185
|
+
# In production we start logging and don't allow the user to stop it
|
186
|
+
# We also disallow setting telemetry and disconnecting from interfaces
|
187
|
+
if production
|
188
|
+
@api_whitelist.delete('stop_logging')
|
189
|
+
@api_whitelist.delete('stop_cmd_log')
|
190
|
+
@api_whitelist.delete('stop_tlm_log')
|
191
|
+
@interfaces.all.each do |name, interface|
|
192
|
+
interface.disable_disconnect = true
|
193
|
+
end
|
194
|
+
@routers.all.each do |name, interface|
|
195
|
+
interface.disable_disconnect = true
|
196
|
+
end
|
176
197
|
end
|
177
|
-
|
178
|
-
@json_drb.method_whitelist = @api_whitelist
|
179
|
-
begin
|
180
|
-
@json_drb.start_service(System.listen_hosts['CTS_API'], System.ports['CTS_API'], self)
|
181
|
-
rescue Exception
|
182
|
-
# Call packet_logging shutdown here to explicitly kill the logging
|
183
|
-
# threads since this CTS is not going to launch
|
184
|
-
@packet_logging.shutdown
|
185
|
-
raise FatalError.new("Error starting JsonDRb on port #{System.ports['CTS_API']}.\nPerhaps a Command and Telemetry Server is already running?")
|
186
|
-
end
|
187
|
-
|
188
|
-
@routers.add_preidentified('PREIDENTIFIED_ROUTER', System.instance.ports['CTS_PREIDENTIFIED'])
|
189
|
-
@routers.add_cmd_preidentified('PREIDENTIFIED_CMD_ROUTER', System.instance.ports['CTS_CMD_ROUTER'])
|
190
|
-
System.telemetry.limits_change_callback = method(:limits_change_callback)
|
191
|
-
@interfaces.start
|
192
|
-
@routers.start
|
193
|
-
@background_tasks.start_all
|
194
|
-
|
195
|
-
# Start staleness monitor thread
|
196
|
-
@sleeper = Sleeper.new
|
197
|
-
@staleness_monitor_thread = Thread.new do
|
198
|
+
@json_drb.method_whitelist = @api_whitelist
|
198
199
|
begin
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
#
|
210
|
-
|
211
|
-
|
212
|
-
pkt_name = [packet.target_name, packet.packet_name]
|
213
|
-
stale << pkt_name
|
214
|
-
post_limits_event(:STALE_PACKET, pkt_name) unless prev_stale.include?(pkt_name)
|
215
|
-
end
|
216
|
-
|
217
|
-
# Send :STALE_PACKET_RCVD events for all packets that were stale
|
218
|
-
# but are no longer stale.
|
219
|
-
prev_stale.each do |pkt_name|
|
220
|
-
post_limits_event(:STALE_PACKET_RCVD, pkt_name) unless stale.include?(pkt_name)
|
221
|
-
end
|
222
|
-
prev_stale = stale.dup
|
223
|
-
|
224
|
-
broken = @sleeper.sleep(10)
|
225
|
-
break if broken
|
200
|
+
if @mode == :CMD_TLM_SERVER
|
201
|
+
@json_drb.start_service(System.listen_hosts['CTS_API'], System.ports['CTS_API'], self)
|
202
|
+
else
|
203
|
+
@json_drb.start_service(System.listen_hosts['REPLAY_API'], System.ports['REPLAY_API'], self)
|
204
|
+
end
|
205
|
+
rescue Exception
|
206
|
+
# Call packet_logging shutdown here to explicitly kill the logging
|
207
|
+
# threads since this CTS is not going to launch
|
208
|
+
@packet_logging.shutdown
|
209
|
+
if @mode == :CMD_TLM_SERVER
|
210
|
+
raise FatalError.new("Error starting JsonDRb on port #{System.ports['CTS_API']}.\nPerhaps a Command and Telemetry Server is already running?")
|
211
|
+
else
|
212
|
+
raise FatalError.new("Error starting JsonDRb on port #{System.ports['REPLAY_API']}.\nPerhaps another Replay is already running?")
|
226
213
|
end
|
227
|
-
rescue Exception => err
|
228
|
-
Logger.fatal "Staleness Monitor thread unexpectedly died"
|
229
|
-
Cosmos.handle_fatal_exception(err)
|
230
214
|
end
|
231
|
-
|
232
|
-
|
215
|
+
|
216
|
+
if @mode == :CMD_TLM_SERVER
|
217
|
+
@routers.add_preidentified('PREIDENTIFIED_ROUTER', System.ports['CTS_PREIDENTIFIED'])
|
218
|
+
@routers.add_cmd_preidentified('PREIDENTIFIED_CMD_ROUTER', System.ports['CTS_CMD_ROUTER'])
|
219
|
+
else
|
220
|
+
@routers.all.clear
|
221
|
+
@routers.add_preidentified('PREIDENTIFIED_ROUTER', System.ports['REPLAY_PREIDENTIFIED'])
|
222
|
+
@routers.add_cmd_preidentified('PREIDENTIFIED_CMD_ROUTER', System.ports['REPLAY_CMD_ROUTER'])
|
223
|
+
end
|
224
|
+
System.telemetry.limits_change_callback = method(:limits_change_callback)
|
225
|
+
@routers.start
|
226
|
+
|
227
|
+
start(production)
|
228
|
+
end
|
229
|
+
end # end def initialize
|
233
230
|
|
234
231
|
# Properly shuts down the command and telemetry server by stoping the
|
235
232
|
# JSON-RPC server, background tasks, routers, and interfaces. Also kills
|
236
|
-
# the packet staleness monitor thread.
|
233
|
+
# the packet staleness monitor thread. This is final and the server cannot be
|
234
|
+
# restarted, it must be recreated
|
237
235
|
def stop
|
238
|
-
#
|
239
|
-
@
|
236
|
+
# Break long pollers
|
237
|
+
@limits_event_queues.dup.each do |id, data|
|
238
|
+
queue, queue_size = @limits_event_queues.delete(id)
|
239
|
+
queue << nil if queue
|
240
|
+
end
|
240
241
|
|
241
|
-
|
242
|
-
|
242
|
+
@server_message_queues.dup.each do |id, data|
|
243
|
+
queue, queue_size = @server_message_queues.delete(id)
|
244
|
+
queue << nil if queue
|
245
|
+
end
|
246
|
+
|
247
|
+
@packet_data_queues.dup.each do |id, data|
|
248
|
+
queue, packets, queue_size = @packet_data_queues.delete(id)
|
249
|
+
queue << nil if queue
|
250
|
+
end
|
243
251
|
|
244
|
-
|
252
|
+
# Shutdown DRb
|
253
|
+
@json_drb.stop_service if @json_drb
|
245
254
|
@routers.stop
|
246
|
-
|
247
|
-
@
|
255
|
+
|
256
|
+
if @mode == :CMD_TLM_SERVER
|
257
|
+
# Shutdown staleness monitor thread
|
258
|
+
Cosmos.kill_thread(self, @staleness_monitor_thread)
|
259
|
+
|
260
|
+
@background_tasks.stop_all
|
261
|
+
@interfaces.stop
|
262
|
+
@packet_logging.shutdown
|
263
|
+
else
|
264
|
+
@replay_backend.shutdown
|
265
|
+
end
|
248
266
|
@stop_callback.call if @stop_callback
|
249
267
|
@message_log.stop if @message_log
|
250
|
-
|
251
|
-
@json_drb = nil
|
252
268
|
end
|
253
269
|
|
254
270
|
# Set a stop callback
|
@@ -256,6 +272,21 @@ module Cosmos
|
|
256
272
|
@stop_callback = stop_callback
|
257
273
|
end
|
258
274
|
|
275
|
+
# Reload the default configuration
|
276
|
+
def reload
|
277
|
+
@replay_backend.shutdown if @mode != :CMD_TLM_SERVER
|
278
|
+
if @reload_callback
|
279
|
+
@reload_callback.call(false)
|
280
|
+
else
|
281
|
+
System.reset
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# Set a reload callback
|
286
|
+
def reload_callback= (reload_callback)
|
287
|
+
@reload_callback = reload_callback
|
288
|
+
end
|
289
|
+
|
259
290
|
# Gracefully kill the staleness monitor thread
|
260
291
|
def graceful_kill
|
261
292
|
@sleeper.cancel
|
@@ -289,13 +320,15 @@ module Cosmos
|
|
289
320
|
|
290
321
|
post_limits_event(:LIMITS_CHANGE, [packet.target_name, packet.packet_name, item.name, old_limits_state, item.limits.state])
|
291
322
|
|
292
|
-
if
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
323
|
+
if @mode == :CMD_TLM_SERVER
|
324
|
+
if item.limits.response
|
325
|
+
begin
|
326
|
+
item.limits.response.call(packet, item, old_limits_state)
|
327
|
+
rescue Exception => err
|
328
|
+
Logger.error "#{packet.target_name} #{packet.packet_name} #{item.name} Limits Response Exception!"
|
329
|
+
Logger.error "Called with old_state = #{old_limits_state}, new_state = #{item.limits.state}"
|
330
|
+
Logger.error err.formatted
|
331
|
+
end
|
299
332
|
end
|
300
333
|
end
|
301
334
|
end
|
@@ -352,6 +385,10 @@ module Cosmos
|
|
352
385
|
# @return [Integer] The queue ID returned from the CmdTlmServer. Use this
|
353
386
|
# ID when calling {#get_limits_event} and {#unsubscribe_limits_events}.
|
354
387
|
def self.subscribe_limits_events(queue_size = DEFAULT_LIMITS_EVENT_QUEUE_SIZE)
|
388
|
+
unless queue_size.is_a? Integer and queue_size > 0
|
389
|
+
raise ArgumentError, "Invalid queue size for subscribe_limits_events: #{queue_size}"
|
390
|
+
end
|
391
|
+
|
355
392
|
id = nil
|
356
393
|
@@instance.limits_event_queue_mutex.synchronize do
|
357
394
|
id = @@instance.next_limits_event_queue_id
|
@@ -461,12 +498,22 @@ module Cosmos
|
|
461
498
|
upcase_packets = []
|
462
499
|
|
463
500
|
# Upper case packet names
|
501
|
+
need_meta = false
|
464
502
|
packets.length.times do |index|
|
465
503
|
upcase_packets << []
|
466
504
|
upcase_packets[index][0] = packets[index][0].upcase
|
467
505
|
upcase_packets[index][1] = packets[index][1].upcase
|
506
|
+
|
468
507
|
# Get the packet to ensure it exists
|
469
|
-
@@instance.
|
508
|
+
if @@instance.disconnect
|
509
|
+
@last_subscribed_packet = System.telemetry.packet(upcase_packets[index][0], upcase_packets[index][1])
|
510
|
+
else
|
511
|
+
@@instance.get_tlm_packet(upcase_packets[index][0], upcase_packets[index][1])
|
512
|
+
end
|
513
|
+
|
514
|
+
if upcase_packets[index][0] == 'SYSTEM' and upcase_packets[index][1] == 'META'
|
515
|
+
need_meta = true
|
516
|
+
end
|
470
517
|
end
|
471
518
|
|
472
519
|
@@instance.packet_data_queue_mutex.synchronize do
|
@@ -474,6 +521,15 @@ module Cosmos
|
|
474
521
|
@@instance.packet_data_queues[id] =
|
475
522
|
[Queue.new, upcase_packets, queue_size]
|
476
523
|
@@instance.next_packet_data_queue_id += 1
|
524
|
+
|
525
|
+
# Send the current meta packet first if requested
|
526
|
+
if need_meta
|
527
|
+
packet = System.telemetry.packet('SYSTEM', 'META')
|
528
|
+
received_time = packet.received_time
|
529
|
+
received_time ||= Time.now.sys
|
530
|
+
@@instance.packet_data_queues[id][0] << [packet.buffer, 'SYSTEM', 'META',
|
531
|
+
received_time.tv_sec, received_time.tv_usec, packet.received_count]
|
532
|
+
end
|
477
533
|
end
|
478
534
|
return id
|
479
535
|
end
|
@@ -509,17 +565,117 @@ module Cosmos
|
|
509
565
|
queue, _, _ = @@instance.packet_data_queues[id]
|
510
566
|
end
|
511
567
|
if queue
|
512
|
-
|
568
|
+
if @@instance.disconnect
|
569
|
+
begin
|
570
|
+
return queue.pop(true)
|
571
|
+
rescue ThreadError
|
572
|
+
received_time ||= Time.now.sys
|
573
|
+
return [@last_subscribed_packet.buffer, @last_subscribed_packet.target_name,
|
574
|
+
@last_subscribed_packet.packet_name, received_time.tv_sec, received_time.tv_usec, @last_subscribed_packet.received_count]
|
575
|
+
end
|
576
|
+
else
|
577
|
+
return queue.pop(non_block)
|
578
|
+
end
|
513
579
|
else
|
514
580
|
raise "Packet data queue with id #{id} not found"
|
515
581
|
end
|
516
582
|
end
|
517
583
|
|
584
|
+
# Post a server message to all subscribed server message listeners.
|
585
|
+
# Messages are formatted as [Text, Color], e.g. ["Msg1","RED"]
|
586
|
+
#
|
587
|
+
# @param message [Array<String, String>] Server message
|
588
|
+
def post_server_message(message)
|
589
|
+
if @server_message_queues.length > 0
|
590
|
+
queues_to_drop = []
|
591
|
+
|
592
|
+
@server_message_queue_mutex.synchronize do
|
593
|
+
# Post event to active queues
|
594
|
+
@server_message_queues.each do |id, data|
|
595
|
+
queue = data[0]
|
596
|
+
queue_size = data[1]
|
597
|
+
queue << message
|
598
|
+
if queue.length > queue_size
|
599
|
+
# Drop queue
|
600
|
+
queues_to_drop << id
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
# Drop queues which are not being serviced
|
605
|
+
queues_to_drop.each do |id|
|
606
|
+
# Remove the queue to stop servicing it. Nil is added to unblock any client threads
|
607
|
+
# that might otherwise be left blocking forever for something on the queue
|
608
|
+
queue, queue_size = @server_message_queues.delete(id)
|
609
|
+
queue << nil if queue
|
610
|
+
end
|
611
|
+
end
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
# Create a queue on the CmdTlmServer that gets populated with every message
|
616
|
+
# in the system.
|
617
|
+
#
|
618
|
+
# @param queue_size [Integer] The number of server messages to accumulate
|
619
|
+
# before the queue will be dropped due to inactivity.
|
620
|
+
# @return [Integer] The queue ID returned from the CmdTlmServer. Use this
|
621
|
+
# ID when calling {#get_server_message} and {#unsubscribe_server_messages}.
|
622
|
+
def self.subscribe_server_messages(queue_size = DEFAULT_SERVER_MESSAGES_QUEUE_SIZE)
|
623
|
+
unless queue_size.is_a? Integer and queue_size > 0
|
624
|
+
raise ArgumentError, "Invalid queue size for subscribe_server_messages: #{queue_size}"
|
625
|
+
end
|
626
|
+
|
627
|
+
id = nil
|
628
|
+
@@instance.server_message_queue_mutex.synchronize do
|
629
|
+
id = @@instance.next_server_message_queue_id
|
630
|
+
@@instance.server_message_queues[id] = [Queue.new, queue_size]
|
631
|
+
@@instance.next_server_message_queue_id += 1
|
632
|
+
end
|
633
|
+
return id
|
634
|
+
end
|
635
|
+
|
636
|
+
# Unsubscribe from being notified for every server message in the system.
|
637
|
+
# This deletes the queue and further calls to {#get_server_message} will
|
638
|
+
# raise an exception.
|
639
|
+
#
|
640
|
+
# @param id [Integer] The queue ID received from calling
|
641
|
+
# {#subscribe_server_messages}
|
642
|
+
def self.unsubscribe_server_messages(id)
|
643
|
+
queue = nil
|
644
|
+
@@instance.server_message_queue_mutex.synchronize do
|
645
|
+
# Remove the queue to stop servicing it. Nil is added to unblock any client threads
|
646
|
+
# that might otherwise be left blocking forever for something on the queue
|
647
|
+
queue, queue_size = @@instance.server_message_queues.delete(id)
|
648
|
+
queue << nil if queue
|
649
|
+
end
|
650
|
+
end
|
651
|
+
|
652
|
+
# Get a server message from the queue created by {#subscribe_server_messages}.
|
653
|
+
#
|
654
|
+
# Each server message consists of a String
|
655
|
+
#
|
656
|
+
# @param id [Integer] The queue ID received from calling
|
657
|
+
# {#subscribe_server_messages}
|
658
|
+
# @param non_block [Boolean] Whether to wait on the queue for the next
|
659
|
+
# server message before returning. Default is to block waiting for the next
|
660
|
+
# message. NOTE: If you pass true and there is no data on the queue, a
|
661
|
+
# ThreadError exception is raised.
|
662
|
+
def self.get_server_message(id, non_block = false)
|
663
|
+
queue = nil
|
664
|
+
@@instance.server_message_queue_mutex.synchronize do
|
665
|
+
queue, _ = @@instance.server_message_queues[id]
|
666
|
+
end
|
667
|
+
if queue
|
668
|
+
return queue.pop(non_block)
|
669
|
+
else
|
670
|
+
raise "Server message queue with id #{id} not found"
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
518
674
|
# Calls clear_counters on the System, interfaces, routers, and sets the
|
519
675
|
# request_count on json_drb to 0.
|
520
676
|
def self.clear_counters
|
521
677
|
System.clear_counters
|
522
|
-
self.instance.interfaces.clear_counters
|
678
|
+
self.instance.interfaces.clear_counters if self.instance.interfaces
|
523
679
|
self.instance.routers.clear_counters
|
524
680
|
self.instance.json_drb.request_count = 0
|
525
681
|
end
|
@@ -533,5 +689,67 @@ module Cosmos
|
|
533
689
|
packet.check_limits(System.limits_set)
|
534
690
|
post_packet(packet)
|
535
691
|
end
|
692
|
+
|
693
|
+
private
|
694
|
+
|
695
|
+
# Start up the system by starting the JSON-RPC server, interfaces, routers,
|
696
|
+
# and background tasks. Starts a thread to monitor all packets for
|
697
|
+
# staleness so other tools (such as Packet Viewer or Telemetry Viewer) can
|
698
|
+
# react accordingly.
|
699
|
+
#
|
700
|
+
# This method is shoudl only called by initialize which is why it is private
|
701
|
+
#
|
702
|
+
# @param start_packet_logging [Boolean] Whether to start logging data or not
|
703
|
+
def start(start_packet_logging = false)
|
704
|
+
if @mode == :CMD_TLM_SERVER
|
705
|
+
@replay_backend = nil # Remove access to Replay
|
706
|
+
@message_log = MessageLog.new('server')
|
707
|
+
@packet_logging.start if start_packet_logging
|
708
|
+
@interfaces.start
|
709
|
+
@background_tasks.start_all
|
710
|
+
|
711
|
+
# Start staleness monitor thread
|
712
|
+
@sleeper = Sleeper.new
|
713
|
+
@staleness_monitor_thread = Thread.new do
|
714
|
+
begin
|
715
|
+
stale = []
|
716
|
+
prev_stale = []
|
717
|
+
while true
|
718
|
+
# The check_stale method drives System.telemetry to iterate through
|
719
|
+
# the packets and mark them stale as necessary.
|
720
|
+
System.telemetry.check_stale
|
721
|
+
|
722
|
+
# Get all stale packets that include limits items.
|
723
|
+
stale_pkts = System.telemetry.stale(true)
|
724
|
+
|
725
|
+
# Send :STALE_PACKET events for all newly stale packets.
|
726
|
+
stale = []
|
727
|
+
stale_pkts.each do |packet|
|
728
|
+
pkt_name = [packet.target_name, packet.packet_name]
|
729
|
+
stale << pkt_name
|
730
|
+
post_limits_event(:STALE_PACKET, pkt_name) unless prev_stale.include?(pkt_name)
|
731
|
+
end
|
732
|
+
|
733
|
+
# Send :STALE_PACKET_RCVD events for all packets that were stale
|
734
|
+
# but are no longer stale.
|
735
|
+
prev_stale.each do |pkt_name|
|
736
|
+
post_limits_event(:STALE_PACKET_RCVD, pkt_name) unless stale.include?(pkt_name)
|
737
|
+
end
|
738
|
+
prev_stale = stale.dup
|
739
|
+
|
740
|
+
broken = @sleeper.sleep(10)
|
741
|
+
break if broken
|
742
|
+
end
|
743
|
+
rescue Exception => err
|
744
|
+
Logger.fatal "Staleness Monitor thread unexpectedly died"
|
745
|
+
Cosmos.handle_fatal_exception(err)
|
746
|
+
end
|
747
|
+
end # end Thread.new
|
748
|
+
else
|
749
|
+
# Prevent access to interfaces or packet_logging
|
750
|
+
@interfaces = nil
|
751
|
+
@packet_logging = nil
|
752
|
+
end
|
753
|
+
end
|
536
754
|
end
|
537
755
|
end
|