cosmos 3.4.2 → 3.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Manifest.txt +69 -11
- data/autohotkey/config/targets/INST/cmd_tlm/inst_tlm.txt +23 -0
- data/autohotkey/tools/TestRunnerAHK5 +17 -0
- data/autohotkey/tools/TestRunnerAHK6 +17 -0
- data/autohotkey/tools/cmd_extractor.ahk +6 -0
- data/autohotkey/tools/data_viewer.ahk +6 -0
- data/autohotkey/tools/limits_monitor.ahk +67 -14
- data/autohotkey/tools/replay.ahk +6 -0
- data/autohotkey/tools/test_runner5.ahk +8 -0
- data/autohotkey/tools/test_runner6.ahk +5 -0
- data/autohotkey/tools/tlm_extractor.ahk +25 -1
- data/autohotkey/tools/tlm_grapher.ahk +6 -0
- data/cosmos.gemspec +19 -19
- data/data/crc.txt +46 -46
- data/data/critical.wav +0 -0
- data/data/information.wav +0 -0
- data/data/input.wav +0 -0
- data/data/message.wav +0 -0
- data/data/question.wav +0 -0
- data/data/warning.wav +0 -0
- data/demo/Gemfile +5 -1
- data/demo/Launcher +5 -4
- data/demo/Launcher.bat +6 -56
- data/demo/config/data/crc.txt +73 -55
- data/demo/config/system/system.txt +1 -0
- data/demo/config/targets/EXAMPLE/cmd_tlm/example_tlm.txt +2 -0
- data/demo/procedures/example_test.rb +17 -16
- data/demo/tools/CmdExtractor +6 -5
- data/demo/tools/CmdExtractor.bat +6 -56
- data/demo/tools/CmdSender +6 -5
- data/demo/tools/CmdSender.bat +6 -56
- data/demo/tools/CmdTlmServer +6 -5
- data/demo/tools/CmdTlmServer.bat +6 -56
- data/demo/tools/DataViewer +6 -5
- data/demo/tools/DataViewer.bat +6 -56
- data/demo/tools/ExampleTarget +6 -5
- data/demo/tools/ExampleTarget.bat +6 -56
- data/demo/tools/HandbookCreator +6 -5
- data/demo/tools/HandbookCreator.bat +6 -58
- data/demo/tools/Launcher +6 -5
- data/demo/tools/Launcher.bat +6 -56
- data/demo/tools/LimitsMonitor +6 -5
- data/demo/tools/LimitsMonitor.bat +6 -56
- data/demo/tools/OpenGLBuilder +6 -5
- data/demo/tools/OpenGLBuilder.bat +6 -56
- data/demo/tools/PacketViewer +6 -5
- data/demo/tools/PacketViewer.bat +6 -56
- data/demo/tools/Replay +6 -5
- data/demo/tools/Replay.bat +6 -56
- data/demo/tools/ScpiTarget +6 -5
- data/demo/tools/ScpiTarget.bat +6 -56
- data/demo/tools/ScriptRunner +6 -5
- data/demo/tools/ScriptRunner.bat +6 -56
- data/demo/tools/TableManager +6 -5
- data/demo/tools/TableManager.bat +6 -56
- data/demo/tools/TestRunner +6 -5
- data/demo/tools/TestRunner.bat +6 -56
- data/demo/tools/TlmExtractor +6 -5
- data/demo/tools/TlmExtractor.bat +6 -56
- data/demo/tools/TlmGrapher +6 -5
- data/demo/tools/TlmGrapher.bat +6 -56
- data/demo/tools/TlmViewer +6 -5
- data/demo/tools/TlmViewer.bat +6 -56
- data/demo/tools/ToolLaunch.bat +63 -0
- data/demo/tools/mac/CmdExtractor.app/Contents/MacOS/CmdExtractor.rb +6 -5
- data/demo/tools/mac/CmdExtractor.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/CmdSender.app/Contents/MacOS/CmdSender.rb +6 -5
- data/demo/tools/mac/CmdSender.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/CmdTlmServer.app/Contents/MacOS/CmdTlmServer.rb +6 -5
- data/demo/tools/mac/CmdTlmServer.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/DataViewer.app/Contents/MacOS/DataViewer.rb +6 -5
- data/demo/tools/mac/DataViewer.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/HandbookCreator.app/Contents/MacOS/HandbookCreator.rb +6 -5
- data/demo/tools/mac/HandbookCreator.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/Launcher.app/Contents/MacOS/Launcher.rb +6 -5
- data/demo/tools/mac/Launcher.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/LimitsMonitor.app/Contents/MacOS/LimitsMonitor.rb +6 -5
- data/demo/tools/mac/LimitsMonitor.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/OpenGLBuilder.app/Contents/MacOS/OpenGLBuilder.rb +6 -5
- data/demo/tools/mac/OpenGLBuilder.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/PacketViewer.app/Contents/MacOS/PacketViewer.rb +6 -5
- data/demo/tools/mac/PacketViewer.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/Replay.app/Contents/MacOS/Replay.rb +6 -5
- data/demo/tools/mac/Replay.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/ScriptRunner.app/Contents/MacOS/ScriptRunner.rb +6 -5
- data/demo/tools/mac/ScriptRunner.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/TableManager.app/Contents/MacOS/TableManager.rb +6 -5
- data/demo/tools/mac/TableManager.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/TestRunner.app/Contents/MacOS/TestRunner.rb +6 -5
- data/demo/tools/mac/TestRunner.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/TlmExtractor.app/Contents/MacOS/TlmExtractor.rb +6 -5
- data/demo/tools/mac/TlmExtractor.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/TlmGrapher.app/Contents/MacOS/TlmGrapher.rb +6 -5
- data/demo/tools/mac/TlmGrapher.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/TlmViewer.app/Contents/MacOS/TlmViewer.rb +6 -5
- data/demo/tools/mac/TlmViewer.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/tool_launch.rb +38 -0
- data/install/Gemfile +5 -1
- data/install/Launcher +5 -3
- data/install/Launcher.bat +6 -56
- data/install/config/data/crc.txt +67 -49
- data/install/config/tools/launcher/launcher.txt +1 -0
- data/install/tools/CmdExtractor +6 -5
- data/install/tools/CmdExtractor.bat +6 -56
- data/install/tools/CmdSender +6 -5
- data/install/tools/CmdSender.bat +6 -56
- data/install/tools/CmdTlmServer +6 -5
- data/install/tools/CmdTlmServer.bat +6 -56
- data/install/tools/DataViewer +6 -5
- data/install/tools/DataViewer.bat +6 -56
- data/install/tools/HandbookCreator +6 -5
- data/install/tools/HandbookCreator.bat +6 -58
- data/install/tools/Launcher +6 -5
- data/install/tools/Launcher.bat +6 -56
- data/install/tools/LimitsMonitor +6 -5
- data/install/tools/LimitsMonitor.bat +6 -56
- data/install/tools/OpenGLBuilder +6 -5
- data/install/tools/OpenGLBuilder.bat +6 -56
- data/install/tools/PacketViewer +6 -5
- data/install/tools/PacketViewer.bat +6 -56
- data/install/tools/Replay +6 -5
- data/install/tools/Replay.bat +6 -56
- data/install/tools/ScriptRunner +6 -5
- data/install/tools/ScriptRunner.bat +6 -56
- data/install/tools/TableManager +6 -5
- data/install/tools/TableManager.bat +6 -56
- data/install/tools/TestRunner +6 -5
- data/install/tools/TestRunner.bat +6 -56
- data/install/tools/TlmExtractor +6 -5
- data/install/tools/TlmExtractor.bat +6 -56
- data/install/tools/TlmGrapher +6 -5
- data/install/tools/TlmGrapher.bat +6 -56
- data/install/tools/TlmViewer +6 -5
- data/install/tools/TlmViewer.bat +6 -56
- data/install/tools/ToolLaunch.bat +63 -0
- data/install/tools/mac/CmdExtractor.app/Contents/MacOS/CmdExtractor.rb +6 -5
- data/install/tools/mac/CmdExtractor.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/CmdSender.app/Contents/MacOS/CmdSender.rb +6 -5
- data/install/tools/mac/CmdSender.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/CmdTlmServer.app/Contents/MacOS/CmdTlmServer.rb +6 -5
- data/install/tools/mac/CmdTlmServer.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/DataViewer.app/Contents/MacOS/DataViewer.rb +6 -5
- data/install/tools/mac/DataViewer.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/HandbookCreator.app/Contents/MacOS/HandbookCreator.rb +6 -5
- data/install/tools/mac/HandbookCreator.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/Launcher.app/Contents/MacOS/Launcher.rb +6 -5
- data/install/tools/mac/Launcher.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/LimitsMonitor.app/Contents/MacOS/LimitsMonitor.rb +6 -5
- data/install/tools/mac/LimitsMonitor.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/OpenGLBuilder.app/Contents/MacOS/OpenGLBuilder.rb +6 -5
- data/install/tools/mac/OpenGLBuilder.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/PacketViewer.app/Contents/MacOS/PacketViewer.rb +6 -5
- data/install/tools/mac/PacketViewer.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/Replay.app/Contents/MacOS/Replay.rb +6 -5
- data/install/tools/mac/Replay.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/ScriptRunner.app/Contents/MacOS/ScriptRunner.rb +6 -5
- data/install/tools/mac/ScriptRunner.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/TableManager.app/Contents/MacOS/TableManager.rb +6 -5
- data/install/tools/mac/TableManager.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/TestRunner.app/Contents/MacOS/TestRunner.rb +6 -5
- data/install/tools/mac/TestRunner.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/TlmExtractor.app/Contents/MacOS/TlmExtractor.rb +6 -5
- data/install/tools/mac/TlmExtractor.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/TlmGrapher.app/Contents/MacOS/TlmGrapher.rb +6 -5
- data/install/tools/mac/TlmGrapher.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/TlmViewer.app/Contents/MacOS/TlmViewer.rb +6 -5
- data/install/tools/mac/TlmViewer.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/tool_launch.rb +38 -0
- data/lib/cosmos/core_ext/string.rb +3 -2
- data/lib/cosmos/gui/dialogs/about_dialog.rb +3 -7
- data/lib/cosmos/gui/dialogs/find_replace_dialog.rb +200 -136
- data/lib/cosmos/gui/opengl/gl_viewer.rb +8 -8
- data/lib/cosmos/gui/qt.rb +56 -27
- data/lib/cosmos/gui/qt_tool.rb +3 -1
- data/lib/cosmos/gui/text/ruby_editor.rb +130 -110
- data/lib/cosmos/gui/utilities/script_module_gui.rb +150 -4
- data/lib/cosmos/io/json_drb.rb +1 -1
- data/lib/cosmos/io/win32_serial_driver.rb +2 -4
- data/lib/cosmos/packet_logs/ccsds_log_reader.rb +1 -0
- data/lib/cosmos/packet_logs/packet_log_reader.rb +13 -4
- data/lib/cosmos/packets/limits.rb +6 -3
- data/lib/cosmos/packets/telemetry.rb +34 -3
- data/lib/cosmos/processors/new_packet_log_processor.rb +1 -1
- data/lib/cosmos/script/commands.rb +20 -2
- data/lib/cosmos/script/extract.rb +11 -3
- data/lib/cosmos/script/limits.rb +6 -0
- data/lib/cosmos/script/scripting.rb +17 -9
- data/lib/cosmos/system/system.rb +73 -17
- data/lib/cosmos/system/target.rb +10 -5
- data/lib/cosmos/tools/cmd_extractor/cmd_extractor.rb +1 -0
- data/lib/cosmos/tools/cmd_tlm_server/api.rb +95 -0
- data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server.rb +8 -4
- data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_gui.rb +55 -0
- data/lib/cosmos/tools/data_viewer/data_viewer.rb +5 -12
- data/lib/cosmos/tools/data_viewer/data_viewer_component.rb +14 -48
- data/lib/cosmos/tools/handbook_creator/handbook_creator_config.rb +1 -5
- data/lib/cosmos/tools/launcher/launcher.rb +4 -0
- data/lib/cosmos/tools/launcher/launcher_config.rb +50 -0
- data/lib/cosmos/tools/launcher/launcher_tool.rb +21 -9
- data/lib/cosmos/tools/limits_monitor/limits_monitor.rb +607 -566
- data/lib/cosmos/tools/replay/replay.rb +51 -45
- data/lib/cosmos/tools/script_runner/script_runner.rb +13 -5
- data/lib/cosmos/tools/script_runner/script_runner_frame.rb +8 -109
- data/lib/cosmos/tools/test_runner/test.rb +65 -6
- data/lib/cosmos/tools/test_runner/test_runner.rb +4 -4
- data/lib/cosmos/tools/tlm_extractor/tlm_extractor.rb +5 -0
- data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_logfile_thread.rb +3 -0
- data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +3 -2
- data/lib/cosmos/tools/tlm_viewer/tlm_viewer_config.rb +9 -6
- data/lib/cosmos/tools/tlm_viewer/widgets/array_widget.rb +1 -5
- data/lib/cosmos/tools/tlm_viewer/widgets/block_widget.rb +1 -5
- data/lib/cosmos/top_level.rb +86 -3
- data/lib/cosmos/version.rb +5 -5
- data/lib/cosmos/win32/win32_main.rb +7 -1
- data/spec/packet_logs/packet_log_reader_spec.rb +67 -7
- data/spec/packets/limits_spec.rb +19 -1
- data/spec/packets/telemetry_spec.rb +44 -1
- data/spec/script/commands_spec.rb +14 -0
- data/spec/script/scripting_spec.rb +5 -1
- data/spec/script/telemetry_spec.rb +38 -3
- data/spec/system/system_spec.rb +24 -4
- data/spec/tools/cmd_tlm_server/api_spec.rb +30 -0
- data/test/benchmarks/gsub_benchmark.rb +42 -4
- data/test/benchmarks/is_a_benchmark.rb +34 -0
- data/test/performance/config/data/crc.txt +161 -171
- data/test/performance/config/system/system_packets.txt +10 -10
- data/test/performance/config/system/system_threads.txt +30 -30
- data/test/performance/config/targets/COSMOS/cmd_tlm/cosmos_server_cmds.txt +7 -2
- data/test/performance/config/targets/PACKET/cmd_tlm/packet_cmds.txt +20 -0
- data/test/performance/config/targets/PACKET/cmd_tlm/packet_tlm.txt +98 -0
- data/test/performance/config/targets/{EXAMPLE → PACKET}/cmd_tlm_server.txt +2 -2
- data/test/performance/config/targets/PACKET/lib/packet_interface.rb +22 -0
- data/test/performance/config/targets/PACKET/lib/packet_limits_response.rb +24 -0
- data/test/performance/config/targets/PACKET/screens/status.txt +25 -0
- data/test/performance/config/targets/PACKET/target.txt +28 -0
- data/test/performance/config/targets/{EXAMPLE/cmd_tlm/example_cmds.txt → THREAD/cmd_tlm/thread_cmds.txt} +1 -1
- data/test/performance/config/targets/{EXAMPLE/cmd_tlm/example_tlm.txt → THREAD/cmd_tlm/thread_tlm.txt} +1 -1
- data/test/performance/config/targets/THREAD/cmd_tlm_server.txt +6 -0
- data/test/performance/config/targets/{EXAMPLE/lib/example_interface.rb → THREAD/lib/thread_interface.rb} +1 -1
- data/test/performance/config/targets/THREAD/screens/status.txt +25 -0
- data/test/performance/config/targets/{EXAMPLE → THREAD}/target.txt +0 -0
- data/test/performance/config/tools/cmd_tlm_server/cmd_tlm_server_packets.txt +24 -30
- data/test/performance/config/tools/cmd_tlm_server/cmd_tlm_server_threads.txt +31 -31
- data/test/performance/config/tools/launcher/launcher_packets.txt +16 -11
- data/test/performance/config/tools/launcher/launcher_threads.txt +41 -35
- data/test/performance/config/tools/tlm_grapher/tlm_grapher.txt +204 -0
- data/test/performance/config/tools/tlm_viewer/tlm_viewer.txt +10 -38
- data/test/performance/lib/packet_target.rb +126 -0
- data/test/performance/lib/{example_target.rb → thread_target.rb} +9 -9
- data/test/performance/tools/CmdTlmServerMemProf +1 -1
- data/test/performance/tools/{ExampleTarget → PacketTarget} +2 -2
- data/test/performance/tools/{ExampleTarget.bat → PacketTarget.bat} +0 -0
- data/test/performance/tools/ThreadTarget +14 -0
- data/test/performance/tools/ThreadTarget.bat +59 -0
- data/test/performance/tools/TlmGrapherMemProf +1 -1
- data/test/performance/tools/TlmViewerMemProf +19 -0
- data/{autohotkey/tools/Replay.bat → test/performance/tools/TlmViewerMemProf.bat} +1 -1
- metadata +107 -55
- data/test/performance/lib/example_background_task.rb +0 -57
- data/test/performance/lib/scpi_target.rb +0 -74
@@ -180,28 +180,20 @@ module Cosmos
|
|
180
180
|
end
|
181
181
|
|
182
182
|
def find
|
183
|
-
|
184
|
-
|
185
|
-
@find_dialog.connect(SIGNAL('find_next()')) do
|
186
|
-
current_component do |component|
|
187
|
-
component.find(@find_dialog)
|
188
|
-
end
|
189
|
-
end
|
183
|
+
current_component do |component|
|
184
|
+
FindReplaceDialog.show_find(component.text)
|
190
185
|
end
|
191
|
-
@find_dialog.show
|
192
|
-
@find_dialog.raise
|
193
|
-
@find_dialog.activateWindow
|
194
186
|
end
|
195
187
|
|
196
188
|
def find_next
|
197
189
|
current_component do |component|
|
198
|
-
|
190
|
+
FindReplaceDialog.find_next(component.text)
|
199
191
|
end
|
200
192
|
end
|
201
193
|
|
202
194
|
def find_previous
|
203
195
|
current_component do |component|
|
204
|
-
|
196
|
+
FindReplaceDialog.find_previous(component.text)
|
205
197
|
end
|
206
198
|
end
|
207
199
|
|
@@ -435,6 +427,7 @@ module Cosmos
|
|
435
427
|
file_size = File.size(filename).to_f
|
436
428
|
dialog.append_text("Processing: #{filename}")
|
437
429
|
|
430
|
+
Cosmos.check_log_configuration(@packet_log_reader, filename)
|
438
431
|
@packet_log_reader.each(filename, true, @time_start, @time_end) do |packet|
|
439
432
|
break if @cancel_progress
|
440
433
|
progress = @packet_log_reader.bytes_read.to_f / file_size
|
@@ -15,8 +15,12 @@ module Cosmos
|
|
15
15
|
class DataViewerComponent < Qt::Widget
|
16
16
|
attr_reader :tab_name
|
17
17
|
attr_reader :packets
|
18
|
+
attr_reader :text
|
18
19
|
|
19
|
-
#
|
20
|
+
# Create a component to go inside the DataViewer
|
21
|
+
#
|
22
|
+
# @param parent [Qt::Widget] Parent widget
|
23
|
+
# @param tab_name [String] Name of the tab which displays this widget
|
20
24
|
def initialize(parent, tab_name)
|
21
25
|
super(parent)
|
22
26
|
@tab_name = tab_name
|
@@ -28,6 +32,9 @@ module Cosmos
|
|
28
32
|
end
|
29
33
|
|
30
34
|
# Adds a packet to the list of packets this components processes
|
35
|
+
#
|
36
|
+
# @param target_name [String] Name of the target
|
37
|
+
# @param packet_name [String] Name of the packet
|
31
38
|
def add_packet(target_name, packet_name)
|
32
39
|
@packets << [target_name, packet_name]
|
33
40
|
end
|
@@ -39,11 +46,7 @@ module Cosmos
|
|
39
46
|
@text = Qt::PlainTextEdit.new
|
40
47
|
@text.setReadOnly(true)
|
41
48
|
@text.setMaximumBlockCount(@max_block_count)
|
42
|
-
|
43
|
-
@text.font = Cosmos.getFont("courier", 9)
|
44
|
-
else
|
45
|
-
@text.font = Cosmos.getFont("courier", 12)
|
46
|
-
end
|
49
|
+
@text.font = Cosmos.get_default_small_font
|
47
50
|
@text.setWordWrapMode(Qt::TextOption::NoWrap)
|
48
51
|
@top_layout.addWidget(@text)
|
49
52
|
|
@@ -93,49 +96,12 @@ module Cosmos
|
|
93
96
|
@text.setPlainText("")
|
94
97
|
end
|
95
98
|
|
96
|
-
def find(dialog)
|
97
|
-
found = @text.find(dialog.find_text, dialog.find_flags)
|
98
|
-
if not found and dialog.wrap_around?
|
99
|
-
cursor = @text.textCursor
|
100
|
-
if dialog.find_up?
|
101
|
-
cursor.movePosition(Qt::TextCursor::End)
|
102
|
-
else
|
103
|
-
cursor.movePosition(Qt::TextCursor::Start)
|
104
|
-
end
|
105
|
-
@text.setTextCursor(cursor)
|
106
|
-
@text.find(dialog.find_text, dialog.find_flags)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def find_next(dialog)
|
111
|
-
flags = dialog.find_flags
|
112
|
-
flags &= ~Qt::TextDocument::FindBackward.to_i
|
113
|
-
found = @text.find(dialog.find_text, flags)
|
114
|
-
if not found and dialog.wrap_around?
|
115
|
-
cursor = @text.textCursor
|
116
|
-
cursor.movePosition(Qt::TextCursor::Start)
|
117
|
-
@text.setTextCursor(cursor)
|
118
|
-
@text.find(dialog.find_text, flags)
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
def find_previous(dialog)
|
123
|
-
flags = dialog.find_flags
|
124
|
-
flags |= Qt::TextDocument::FindBackward.to_i
|
125
|
-
found = @text.find(dialog.find_text, flags)
|
126
|
-
if not found and dialog.wrap_around?
|
127
|
-
cursor = @text.textCursor
|
128
|
-
cursor.movePosition(Qt::TextCursor::End)
|
129
|
-
@text.setTextCursor(cursor)
|
130
|
-
@text.find(dialog.find_text, flags)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
99
|
def showEvent(event)
|
135
|
-
# When the tab is shown we want to ensure the scroll bar is at the
|
136
|
-
# the PlainTextArea to automatically hold the scroll
|
137
|
-
#
|
138
|
-
#
|
100
|
+
# When the tab is shown we want to ensure the scroll bar is at the
|
101
|
+
# maximum to allow the PlainTextArea to automatically hold the scroll
|
102
|
+
# at the bottom of the display while appending things.
|
103
|
+
# If this is not done, switching tabs will cause the scroll bar
|
104
|
+
# to "stick" and not stay at the bottom with the newest text.
|
139
105
|
@timer.start(100)
|
140
106
|
end
|
141
107
|
|
@@ -83,11 +83,7 @@ module Cosmos
|
|
83
83
|
if @pdf
|
84
84
|
if progress_dialog
|
85
85
|
Qt.execute_in_main_thread(true) do
|
86
|
-
|
87
|
-
progress_dialog.set_text_font(Cosmos.getFont("courier",10))
|
88
|
-
else
|
89
|
-
progress_dialog.set_text_font(Cosmos.getFont("courier",14))
|
90
|
-
end
|
86
|
+
progress_dialog.set_text_font(Cosmos.get_default_font)
|
91
87
|
end
|
92
88
|
end
|
93
89
|
Cosmos.set_working_dir do
|
@@ -23,6 +23,10 @@ module Cosmos
|
|
23
23
|
|
24
24
|
def initialize(options)
|
25
25
|
super(options) # MUST BE FIRST - All code before super is executed twice in RubyQt Based classes
|
26
|
+
|
27
|
+
# Set environment variable of COSMOS_USERPATH so that all launched apps know where to find the configuration
|
28
|
+
ENV['COSMOS_USERPATH'] = Cosmos::USERPATH
|
29
|
+
|
26
30
|
Cosmos.load_cosmos_icon("launcher.png")
|
27
31
|
layout.setSizeConstraint(Qt::Layout::SetFixedSize)
|
28
32
|
|
@@ -11,6 +11,7 @@
|
|
11
11
|
require 'cosmos'
|
12
12
|
require 'cosmos/config/config_parser'
|
13
13
|
require 'ostruct'
|
14
|
+
require 'bundler'
|
14
15
|
|
15
16
|
module Cosmos
|
16
17
|
|
@@ -62,6 +63,9 @@ module Cosmos
|
|
62
63
|
# Handle each keyword
|
63
64
|
case keyword
|
64
65
|
|
66
|
+
when 'AUTO_GEM_TOOLS'
|
67
|
+
parse_gem_tool(parser)
|
68
|
+
|
65
69
|
when 'TOOL'
|
66
70
|
parse_tool(parser, params, multitool)
|
67
71
|
|
@@ -140,6 +144,25 @@ module Cosmos
|
|
140
144
|
|
141
145
|
protected
|
142
146
|
|
147
|
+
def parse_gem_tool(parser)
|
148
|
+
parser.verify_num_parameters(0, 0, parser.keyword)
|
149
|
+
Bundler.load.specs.each do |spec|
|
150
|
+
spec_name_split = spec.name.split('-')
|
151
|
+
if spec_name_split.length > 1 and spec_name_split[0] == 'cosmos'
|
152
|
+
# Filter to just tools and not targets
|
153
|
+
if File.exist?(File.join(spec.gem_dir, 'tools'))
|
154
|
+
Dir[File.join(spec.gem_dir, 'tools', '*')].each do |filename|
|
155
|
+
if File.extname(filename) == ''
|
156
|
+
@items << [:TOOL, File.basename(filename), format_shell_command(parser, "LAUNCH_GEM #{File.basename(filename)}"), true, File.basename(filename).class_name_to_filename(false) + '.png', nil]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
rescue Bundler::GemfileNotFound
|
163
|
+
# No Gemfile - so no gem based tools
|
164
|
+
end
|
165
|
+
|
143
166
|
def parse_tool(parser, params, multitool)
|
144
167
|
if multitool
|
145
168
|
parser.verify_num_parameters(1, 1, "TOOL <Shell command>")
|
@@ -165,6 +188,8 @@ module Cosmos
|
|
165
188
|
formatted_command = parse_launch(shell_command)
|
166
189
|
when 'LAUNCH_TERMINAL'
|
167
190
|
formatted_command = parse_launch_terminal(shell_command)
|
191
|
+
when 'LAUNCH_GEM'
|
192
|
+
formatted_command = parse_launch_gem(shell_command)
|
168
193
|
else
|
169
194
|
# Nothing to do if they aren't using our keywords
|
170
195
|
formatted_command = shell_command
|
@@ -201,6 +226,31 @@ module Cosmos
|
|
201
226
|
formatted
|
202
227
|
end
|
203
228
|
|
229
|
+
def parse_launch_gem(command)
|
230
|
+
split = command.split
|
231
|
+
|
232
|
+
# Find the gem with this file
|
233
|
+
begin
|
234
|
+
Bundler.load.specs.each do |spec|
|
235
|
+
spec_name_split = spec.name.split('-')
|
236
|
+
if spec_name_split.length > 1 and spec_name_split[0] == 'cosmos'
|
237
|
+
# Filter to just tools and not targets
|
238
|
+
if File.exist?(File.join(spec.gem_dir, 'tools', split[1]))
|
239
|
+
if Kernel.is_mac? and File.exist?(File.join(USERPATH, 'tools', 'mac'))
|
240
|
+
return "open #{spec.gem_dir}/tools/mac/#{split[1]}.app --args #{split[2..-1].join(' ')}".strip
|
241
|
+
else
|
242
|
+
return "RUBYW #{spec.gem_dir}/tools/#{split[1]} #{split[2..-1].join(' ')}".strip
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
rescue Bundler::GemfileNotFound
|
248
|
+
# No Gemfile - so no gem based tools
|
249
|
+
end
|
250
|
+
|
251
|
+
raise "Could not find gem containing tool: #{split[1]} - Make sure the appropriate gem is in your Gemfile"
|
252
|
+
end
|
253
|
+
|
204
254
|
end # class LauncherConfig
|
205
255
|
|
206
256
|
end # module Cosmos
|
@@ -12,7 +12,6 @@ require 'cosmos'
|
|
12
12
|
require 'cosmos/gui/qt'
|
13
13
|
|
14
14
|
module Cosmos
|
15
|
-
|
16
15
|
class LauncherTool < Qt::Object
|
17
16
|
slots 'button_clicked()'
|
18
17
|
|
@@ -26,7 +25,7 @@ module Cosmos
|
|
26
25
|
|
27
26
|
def button_clicked
|
28
27
|
if @variable_parameters
|
29
|
-
parameters = parameters_dialog
|
28
|
+
parameters = parameters_dialog
|
30
29
|
if parameters
|
31
30
|
if @capture_io
|
32
31
|
Cosmos.run_process_check_output(@shell_command + ' ' + parameters)
|
@@ -44,7 +43,7 @@ module Cosmos
|
|
44
43
|
end
|
45
44
|
|
46
45
|
def parameters_dialog
|
47
|
-
dialog = Qt::Dialog.new(
|
46
|
+
dialog = Qt::Dialog.new(parent)
|
48
47
|
dialog.window_title = "#{@button_text} Options"
|
49
48
|
layout = Qt::VBoxLayout.new
|
50
49
|
dialog.layout = layout
|
@@ -53,10 +52,24 @@ module Cosmos
|
|
53
52
|
@variable_parameters.each do |parameter_name, parameter_value|
|
54
53
|
hlayout = Qt::HBoxLayout.new
|
55
54
|
hlayout.addWidget(Qt::Label.new(parameter_name))
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
parameter_options = parameter_value.split('|')
|
56
|
+
if parameter_options.length > 1
|
57
|
+
combo_box = Qt::ComboBox.new
|
58
|
+
combo_box.setEditable(true)
|
59
|
+
idx = 0
|
60
|
+
parameter_options.each do |option|
|
61
|
+
combo_box.insertItem(idx, option)
|
62
|
+
idx += 1
|
63
|
+
end
|
64
|
+
combo_box.setCurrentIndex(0) # Default Option is the first one
|
65
|
+
hlayout.addWidget(combo_box)
|
66
|
+
widgets << combo_box
|
67
|
+
else
|
68
|
+
line_edit = Qt::LineEdit.new
|
69
|
+
line_edit.setText(parameter_value)
|
70
|
+
hlayout.addWidget(line_edit)
|
71
|
+
widgets << line_edit
|
72
|
+
end
|
60
73
|
layout.addLayout(hlayout)
|
61
74
|
end
|
62
75
|
|
@@ -85,7 +98,7 @@ module Cosmos
|
|
85
98
|
if result == Qt::Dialog::Accepted
|
86
99
|
parameters = ''
|
87
100
|
index = 0
|
88
|
-
@variable_parameters.each do |parameter_name,
|
101
|
+
@variable_parameters.each do |parameter_name, _parameter_value|
|
89
102
|
parameters << parameter_name
|
90
103
|
parameters << ' '
|
91
104
|
parameters << widgets[index].text
|
@@ -100,5 +113,4 @@ module Cosmos
|
|
100
113
|
end
|
101
114
|
end
|
102
115
|
end
|
103
|
-
|
104
116
|
end
|
@@ -15,232 +15,460 @@ Cosmos.catch_fatal_exception do
|
|
15
15
|
require 'cosmos/gui/dialogs/cmd_tlm_raw_dialog'
|
16
16
|
require 'cosmos/script'
|
17
17
|
require 'cosmos/tools/tlm_viewer/widgets/labelvaluelimitsbar_widget'
|
18
|
+
require 'cosmos/tools/tlm_viewer/widgets/label_widget'
|
19
|
+
require 'pathname'
|
18
20
|
end
|
19
21
|
|
22
|
+
# Extend Array to search for and delete telemetry items.
|
23
|
+
# Telemetry items are Arrays of [target name, packet name, item name].
|
20
24
|
class Array
|
21
25
|
def includes_item?(item)
|
22
|
-
|
23
|
-
|
24
|
-
return true
|
25
|
-
end
|
26
|
-
end
|
27
|
-
return false
|
26
|
+
found, index = find_item(item)
|
27
|
+
return found
|
28
28
|
end
|
29
29
|
|
30
30
|
def delete_item(item)
|
31
|
+
found, index = find_item(item)
|
32
|
+
self.delete_at(index) if found
|
33
|
+
return index
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def find_item(item)
|
38
|
+
found = false
|
31
39
|
index = 0
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
40
|
+
self.each do |target_name, packet_name, item_name|
|
41
|
+
if ((target_name == item[0]) &&
|
42
|
+
(packet_name == item[1]) &&
|
43
|
+
# If the item name is nil we're dealing with a packet
|
44
|
+
(item_name == item[2] || item_name.nil?))
|
45
|
+
found = true
|
36
46
|
break
|
37
47
|
end
|
38
48
|
index += 1
|
39
49
|
end
|
40
|
-
|
41
|
-
self.delete_at(delete_index) if delete_index
|
42
|
-
return delete_index
|
50
|
+
return found, index
|
43
51
|
end
|
44
52
|
end
|
45
53
|
|
46
54
|
module Cosmos
|
47
55
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
@out_of_limits_items = []
|
56
|
+
class LimitsItems
|
57
|
+
# @return [Array<String,String,String>] Target name, packet name, item name
|
58
|
+
attr_reader :ignored
|
59
|
+
# @return [Boolean] Whether the limits items have been fetched from the server
|
60
|
+
attr_reader :initialized
|
61
|
+
|
62
|
+
UNKNOWN_ARRAY = ['UNKNOWN', 'UNKNOWN', nil]
|
63
|
+
|
64
|
+
# @param new_item_callback [Proc] Method to create a new item in the GUI
|
65
|
+
# @param update_item_callback [Proc] Method to update an item in the GUI
|
66
|
+
# @param clear_items_callback [Proc] Method to clear all items in the GUI
|
67
|
+
def initialize(new_item_callback, update_item_callback, clear_items_callback)
|
68
|
+
@new_item_callback = new_item_callback
|
69
|
+
@update_item_callback = update_item_callback
|
70
|
+
@clear_items_callback = clear_items_callback
|
71
|
+
@ignored = []
|
72
|
+
@items = {}
|
73
|
+
@out_of_limits = []
|
68
74
|
@queue_id = nil
|
69
|
-
@limits_set =
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
+
@limits_set = :DEFAULT
|
76
|
+
request_reset()
|
77
|
+
end
|
78
|
+
|
79
|
+
# Request that the limits items be refreshed from the server
|
80
|
+
def request_reset
|
75
81
|
@initialized = false
|
76
|
-
|
77
|
-
@ignored_items = []
|
78
|
-
@ignored_filename = nil
|
79
|
-
@colorblind = false
|
80
|
-
@new_widgets = []
|
81
|
-
@buttons = []
|
82
|
-
@cancel_thread = false
|
83
|
-
@limits_sleeper = Sleeper.new
|
84
|
-
@value_sleeper = Sleeper.new
|
82
|
+
end
|
85
83
|
|
86
|
-
|
84
|
+
# Ignore an item. Don't display it in the GUI if it goes out of limits
|
85
|
+
# and don't have it count towards the overall limit state. Still display
|
86
|
+
# its limits transitions in the log.
|
87
|
+
#
|
88
|
+
# @param item [Array<String,String,String>] Target name, packet name,
|
89
|
+
# item name to ignore
|
90
|
+
def ignore(item)
|
91
|
+
index = @out_of_limits.delete_item(item)
|
92
|
+
@items.delete("#{item[0]} #{item[1]} #{item[2]}") if index
|
93
|
+
unless @ignored.includes_item?(item)
|
94
|
+
@ignored << item
|
95
|
+
end
|
96
|
+
end
|
87
97
|
|
88
|
-
|
89
|
-
|
90
|
-
|
98
|
+
# Remove an item from the ignored list to have it be displayed and
|
99
|
+
# count towards the overall limits state.
|
100
|
+
#
|
101
|
+
# @param item [Array<String,String,String> Target name, packet name,
|
102
|
+
# item name to remove from ignored list
|
103
|
+
def remove_ignored(item)
|
104
|
+
index = @ignored.delete_item(item)
|
105
|
+
if index
|
106
|
+
# If we deleted a packet we need to recalculate the stale packets
|
107
|
+
if item[2].empty?
|
108
|
+
get_stale(true).each do |target, packet|
|
109
|
+
stale_packet(target, packet)
|
110
|
+
end
|
111
|
+
# We deleted an item so get all the current out of limit items
|
112
|
+
else
|
113
|
+
get_out_of_limits().each do |target, packet, item, state|
|
114
|
+
limits_change(target, packet, item, state)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
rescue DRb::DRbConnError
|
119
|
+
# Do nothing
|
120
|
+
end
|
91
121
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
122
|
+
# @return [Boolean] Whether there are any items being ignored
|
123
|
+
def ignored_items?
|
124
|
+
!@ignored.empty?
|
125
|
+
end
|
96
126
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
@
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
@state_label = Qt::Label.new('Monitored Limits State: ')
|
127
|
+
# @return [Symbol] The overall limits state. Returns :STALE if there
|
128
|
+
# is no connection to the server.
|
129
|
+
def overall_state
|
130
|
+
get_overall_limits_state(@ignored)
|
131
|
+
rescue DRb::DRbConnError
|
132
|
+
:STALE
|
133
|
+
end
|
105
134
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
135
|
+
# Calls get_limits_event to process all the server limits events that
|
136
|
+
# were subscribed to. This method should be called continuously until
|
137
|
+
# it returns nil which indicates no more events.
|
138
|
+
#
|
139
|
+
# @return [Array<String,Symbol] String describing the event and a symbol
|
140
|
+
# indicating how the event string should be colored (:BLACK, :BLUE,
|
141
|
+
# :GREEN, :YELLOW, or :RED)
|
142
|
+
def process_events
|
143
|
+
result = nil
|
144
|
+
type = nil
|
145
|
+
data = nil
|
146
|
+
begin
|
147
|
+
reset() unless @initialized
|
148
|
+
# Get events non-blocking which is why we rescue ThreadError
|
149
|
+
type, data = get_limits_event(@queue_id, true)
|
150
|
+
rescue ThreadError
|
151
|
+
# Do nothing (nominal exception if there are no events)
|
152
|
+
rescue DRb::DRbConnError
|
153
|
+
# The server is down so request a reset
|
154
|
+
request_reset()
|
155
|
+
end
|
156
|
+
return result unless type
|
157
|
+
|
158
|
+
case type
|
159
|
+
when :LIMITS_CHANGE
|
160
|
+
# The most common event: target, packet, item, state
|
161
|
+
result = limits_change(data[0], data[1], data[2], data[4])
|
162
|
+
|
163
|
+
when :LIMITS_SET
|
164
|
+
# Check if the overall limits set changed. If so we need to reset
|
165
|
+
# to incorporate all the new limits.
|
166
|
+
if @limits_set != data
|
167
|
+
request_reset()
|
168
|
+
result = ["INFO: Limits Set Changed to: #{data}\n", :BLACK]
|
169
|
+
end
|
115
170
|
|
116
|
-
|
171
|
+
when :LIMITS_SETTINGS
|
172
|
+
# The limits settings for an individual item changed. Set our local tool
|
173
|
+
# knowledge of the limits to match the server.
|
174
|
+
begin
|
175
|
+
System.limits.set(data[0], data[1], data[2], data[6], data[7], data[8], data[9], data[10], data[11], data[3], data[4], data[5])
|
176
|
+
result = ["INFO: Limits Settings Changed: #{data}\n", :BLACK]
|
177
|
+
rescue
|
178
|
+
# This can fail if we missed setting the DEFAULT limits set earlier
|
179
|
+
end
|
117
180
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
181
|
+
when :STALE_PACKET
|
182
|
+
# A packet has gone stale: target, packet
|
183
|
+
result = stale_packet(data[0], data[1])
|
184
|
+
end
|
185
|
+
result
|
186
|
+
end
|
122
187
|
|
123
|
-
|
188
|
+
# Update the values for all the out of limits items being tracked.
|
189
|
+
def update_values
|
190
|
+
# Reject any out of limits packets as they don't have values
|
191
|
+
items = @out_of_limits.reject {|item| item[2].nil? }
|
124
192
|
|
125
|
-
|
126
|
-
|
193
|
+
values, limits_states, limits_settings, limits_set = get_tlm_values(items, :WITH_UNITS)
|
194
|
+
index = 0
|
195
|
+
items.each do |target_name, packet_name, item_name|
|
196
|
+
begin
|
197
|
+
# Update the limits settings each time we update values
|
198
|
+
# to stay in sync with the Server. Responding to :LIMITS_SETTINGS
|
199
|
+
# events isn't enough since we don't get those upon startup.
|
200
|
+
System.limits.set(target_name, packet_name, item_name,
|
201
|
+
limits_settings[index][0], limits_settings[index][1],
|
202
|
+
limits_settings[index][2], limits_settings[index][3],
|
203
|
+
limits_settings[index][4], limits_settings[index][5],
|
204
|
+
limits_set) if limits_settings[index]
|
205
|
+
rescue
|
206
|
+
# This can fail if we missed setting the DEFAULT limits set earlier
|
207
|
+
end
|
208
|
+
name = "#{target_name} #{packet_name} #{item_name}"
|
209
|
+
@update_item_callback.call(@items[name], values[index], limits_states[index], limits_set)
|
210
|
+
index += 1
|
211
|
+
end
|
212
|
+
rescue DRb::DRbConnError
|
213
|
+
# Do nothing
|
214
|
+
end
|
127
215
|
|
128
|
-
|
129
|
-
|
130
|
-
|
216
|
+
# Load a new configuration of ignored items and packets and reset
|
217
|
+
#
|
218
|
+
# @param config_file [String] Configuration file base name which will be
|
219
|
+
# expanded to find a file in the config/tools/limits_monitor dir.
|
220
|
+
# @return [String] Message indicating success or fail
|
221
|
+
def open_config(filename)
|
222
|
+
return "" unless filename
|
223
|
+
|
224
|
+
unless Pathname.new(filename).absolute?
|
225
|
+
filename = File.join(::Cosmos::USERPATH, 'config', 'tools', 'limits_monitor', filename)
|
226
|
+
end
|
227
|
+
return "Configuration file #{filename} not found!" unless File.exist?(filename)
|
131
228
|
|
132
|
-
@
|
133
|
-
|
229
|
+
@ignored = []
|
230
|
+
begin
|
231
|
+
parser = ConfigParser.new
|
232
|
+
parser.parse_file(filename) do |keyword, params|
|
233
|
+
case keyword
|
234
|
+
# TODO: Eventually we can deprecate 'IGNORE' in favor
|
235
|
+
# of 'IGNORE_ITEM' now that we also have 'IGNORE_PACKET'
|
236
|
+
when 'IGNORE', 'IGNORE_ITEM'
|
237
|
+
@ignored << ([params[0], params[1], params[2]])
|
238
|
+
when 'IGNORE_PACKET'
|
239
|
+
@ignored << ([params[0], params[1], nil])
|
240
|
+
end
|
241
|
+
end
|
242
|
+
result = "#{filename} loaded. "
|
243
|
+
result << "Warning: Some items ignored" if ignored_items?
|
244
|
+
rescue => e
|
245
|
+
result = "Error loading configuration : #{e.message}"
|
246
|
+
end
|
247
|
+
# Since we may have loaded new ignored items we need to reset
|
248
|
+
request_reset()
|
249
|
+
result
|
250
|
+
end
|
134
251
|
|
135
|
-
|
136
|
-
|
252
|
+
# Save the current configuration of ignored items and packets.
|
253
|
+
#
|
254
|
+
# @param config_file [String] Configuration file to save.
|
255
|
+
# @return [String] Message indicating success or fail
|
256
|
+
def save_config(filename)
|
257
|
+
begin
|
258
|
+
File.open(filename, "w") do |file|
|
259
|
+
@ignored.each do |target, pkt_name, item_name|
|
260
|
+
if item_name
|
261
|
+
file.puts("IGNORE_ITEM #{target} #{pkt_name} #{item_name}")
|
262
|
+
else
|
263
|
+
file.puts("IGNORE_PACKET #{target} #{pkt_name}")
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
result = "#{filename} saved"
|
268
|
+
rescue => e
|
269
|
+
result = "Error saving configuration : #{e.message}"
|
270
|
+
end
|
271
|
+
result
|
272
|
+
end
|
137
273
|
|
138
|
-
|
139
|
-
value_thread()
|
140
|
-
limits_thread()
|
141
|
-
end # initialize
|
274
|
+
private
|
142
275
|
|
143
|
-
#
|
276
|
+
# Clear all tracked out of limits items and resubscribe to the server
|
277
|
+
# limits events. Clear the GUI and re-create all out of limits items
|
278
|
+
# and stale packets.
|
144
279
|
#
|
145
|
-
#
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
280
|
+
# Note this method can raise a DRb::DRbConnError error!
|
281
|
+
def reset
|
282
|
+
@items = {}
|
283
|
+
@out_of_limits = []
|
284
|
+
@limits_set = get_limits_set()
|
285
|
+
unsubscribe_limits_events(@queue_id) if @queue_id
|
286
|
+
@queue_id = subscribe_limits_events(100000)
|
287
|
+
@clear_items_callback.call
|
288
|
+
get_out_of_limits().each do |target, packet, item, state|
|
289
|
+
limits_change(target, packet, item, state)
|
290
|
+
end
|
291
|
+
get_stale(true).each do |target, packet|
|
292
|
+
stale_packet(target, packet)
|
293
|
+
end
|
154
294
|
|
155
|
-
|
156
|
-
|
157
|
-
new_widget.set_setting('COLORBLIND', [@colorblind])
|
158
|
-
new_widget.process_settings
|
295
|
+
@initialized = true
|
296
|
+
end
|
159
297
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
298
|
+
# Process a limits_change event by recoloring out of limits events
|
299
|
+
# and creating a log message.
|
300
|
+
def limits_change(target_name, packet_name, item_name, state)
|
301
|
+
message = ''
|
302
|
+
color = :BLACK
|
303
|
+
item = [target_name, packet_name, item_name]
|
304
|
+
|
305
|
+
case state
|
306
|
+
when :YELLOW, :YELLOW_HIGH, :YELLOW_LOW
|
307
|
+
message << "WARN: "
|
308
|
+
color = :YELLOW
|
309
|
+
out_of_limit(item)
|
310
|
+
when :RED, :RED_HIGH, :RED_LOW
|
311
|
+
message << "ERROR: "
|
312
|
+
color = :RED
|
313
|
+
out_of_limit(item)
|
314
|
+
when :GREEN, :GREEN_HIGH, :GREEN_LOW
|
315
|
+
message << "INFO: "
|
316
|
+
color = :GREEN
|
317
|
+
when :BLUE
|
318
|
+
message << "INFO: "
|
319
|
+
color = :BLUE
|
320
|
+
end
|
321
|
+
value = tlm(target_name, packet_name, item_name)
|
322
|
+
message << "#{target_name} #{packet_name} #{item_name} = #{value} is #{state}\n"
|
323
|
+
[message, color]
|
324
|
+
end
|
164
325
|
|
165
|
-
|
326
|
+
# Record the stale packet and generate a log message
|
327
|
+
def stale_packet(target_name, packet_name)
|
328
|
+
out_of_limit([target_name, packet_name, nil])
|
329
|
+
return ["INFO: Packet #{target_name} #{packet_name} is STALE\n", :BLACK]
|
330
|
+
end
|
166
331
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
332
|
+
# Record an out of limits item and call the new item callback.
|
333
|
+
# Existing out of limits and ignored items are not recorded.
|
334
|
+
def out_of_limit(item)
|
335
|
+
unless (@out_of_limits.includes_item?(item) || @ignored.includes_item?(item) || UNKNOWN_ARRAY.includes_item?(item))
|
336
|
+
@out_of_limits << item
|
337
|
+
@items["#{item[0]} #{item[1]} #{item[2]}"] = @new_item_callback.call(*item)
|
171
338
|
end
|
172
339
|
end
|
340
|
+
end
|
173
341
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
#
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
342
|
+
# The LimitsMonitor application displays all the out of limits items
|
343
|
+
# encountered by the COSMOS server. It provides the ability to ignore and
|
344
|
+
# restore limits as well as logs all limits events.
|
345
|
+
class LimitsMonitor < QtTool
|
346
|
+
# LimitsWidget displays either a stale packet using the Label widget
|
347
|
+
# or more commonly an out of limits item using the Labelvaluelimitsbar
|
348
|
+
# Widget.
|
349
|
+
class LimitsWidget < Qt::Widget
|
350
|
+
# @return [Widget] The widget which displays the value
|
351
|
+
attr_accessor :value
|
352
|
+
|
353
|
+
# @parent [Qt::Widget] Parent widget (the LimitsMonitor tool)
|
354
|
+
# @target_name [String] Target name
|
355
|
+
# @packet_name [String] Packet name
|
356
|
+
# @item_name [String] Telemetry item name (nil for stale packets)
|
357
|
+
def initialize(parent, target_name, packet_name, item_name)
|
358
|
+
super(parent)
|
359
|
+
@layout = Qt::HBoxLayout.new
|
360
|
+
@layout.setSpacing(0)
|
361
|
+
@layout.setContentsMargins(0,0,0,0)
|
362
|
+
setLayout(@layout)
|
363
|
+
|
364
|
+
item = [target_name, packet_name, item_name]
|
365
|
+
if item_name
|
366
|
+
@value = LabelvaluelimitsbarWidget.new(@layout, target_name, packet_name, item_name)
|
367
|
+
@value.set_setting('COLORBLIND', [@colorblind])
|
368
|
+
@value.process_settings
|
192
369
|
else
|
193
|
-
|
370
|
+
@value = LabelWidget.new(layout, "#{target_name} #{packet_name} is STALE")
|
371
|
+
end
|
372
|
+
|
373
|
+
@ignore_button = Qt::PushButton.new('Ignore')
|
374
|
+
@ignore_button.connect(SIGNAL('clicked()')) { parent.ignore(self, item) }
|
375
|
+
@layout.addWidget(@ignore_button)
|
376
|
+
end
|
377
|
+
|
378
|
+
# Update the widget's value, limits_state, and limits_set
|
379
|
+
def set_values(value, limits_state, limits_set)
|
380
|
+
if LabelvaluelimitsbarWidget === @value
|
381
|
+
@value.value = value
|
382
|
+
@value.limits_state = limits_state
|
383
|
+
@value.limits_set = limits_set
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
# Enable or disable Colorblind mode
|
388
|
+
def set_colorblind(enabled)
|
389
|
+
if LabelvaluelimitsbarWidget === @value
|
390
|
+
@value.set_setting('COLORBLIND', [enabled])
|
391
|
+
@value.process_settings
|
194
392
|
end
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
393
|
+
end
|
394
|
+
|
395
|
+
# Dispose of the widget
|
396
|
+
def dispose
|
397
|
+
@ignore_button.dispose
|
398
|
+
@value.dispose
|
399
|
+
@layout.dispose
|
400
|
+
super()
|
199
401
|
end
|
200
402
|
end
|
201
403
|
|
202
|
-
#
|
203
|
-
#
|
204
|
-
|
205
|
-
|
206
|
-
|
404
|
+
# Create the main application GUI. Start the limits thread which responds to
|
405
|
+
# asynchronous limits events from the server and the value thread which
|
406
|
+
# polls the server at 1Hz for the out of limits items values.
|
407
|
+
#
|
408
|
+
# @param options [Options] Contains the options for the window.
|
409
|
+
def initialize(options)
|
410
|
+
super(options)
|
411
|
+
Cosmos.load_cosmos_icon("limits_monitor.png")
|
412
|
+
|
413
|
+
@cancel_thread = false
|
414
|
+
@limits_sleeper = Sleeper.new
|
415
|
+
@value_sleeper = Sleeper.new
|
416
|
+
|
417
|
+
initialize_actions()
|
418
|
+
initialize_menus()
|
419
|
+
initialize_central_widget()
|
420
|
+
complete_initialize()
|
421
|
+
|
422
|
+
@limits_items = LimitsItems.new(
|
423
|
+
method(:new_gui_item), method(:update_gui_item), method(:clear_gui_items))
|
424
|
+
result = @limits_items.open_config(options.config_file)
|
425
|
+
statusBar.showMessage(tr(result))
|
426
|
+
|
427
|
+
limits_thread()
|
428
|
+
value_thread()
|
429
|
+
end
|
430
|
+
|
431
|
+
# Initialize all the actions in the application Menu
|
432
|
+
def initialize_actions
|
433
|
+
super
|
207
434
|
|
208
435
|
@options_action = Qt::Action.new(tr('O&ptions'), self)
|
209
436
|
@options_action.statusTip = tr('Open the options dialog')
|
210
|
-
connect(
|
437
|
+
@options_action.connect(SIGNAL('triggered()')) { show_options_dialog() }
|
211
438
|
|
212
439
|
@reset_action = Qt::Action.new(tr('&Reset'), self)
|
213
440
|
@reset_action_keyseq = Qt::KeySequence.new(tr('Ctrl+R'))
|
214
441
|
@reset_action.shortcut = @reset_action_keyseq
|
215
442
|
@reset_action.statusTip = tr('Reset connection and clear all items. This does not modify the ignored items.')
|
216
|
-
connect(
|
443
|
+
@reset_action.connect(SIGNAL('triggered()')) { @limits_items.request_reset() }
|
217
444
|
|
218
445
|
@open_ignored_action = Qt::Action.new(Cosmos.get_icon('open.png'),
|
219
446
|
tr('&Open Config'), self)
|
220
447
|
@open_ignored_action_keyseq = Qt::KeySequence.new(tr('Ctrl+O'))
|
221
448
|
@open_ignored_action.shortcut = @open_ignored_action_keyseq
|
222
449
|
@open_ignored_action.statusTip = tr('Open ignored telemetry items configuration file')
|
223
|
-
connect(
|
450
|
+
@open_ignored_action.connect(SIGNAL('triggered()')) { open_config_file() }
|
224
451
|
|
225
452
|
@save_ignored_action = Qt::Action.new(Cosmos.get_icon('save.png'),
|
226
453
|
tr('&Save Config'), self)
|
227
454
|
@save_ignored_action_keyseq = Qt::KeySequence.new(tr('Ctrl+S'))
|
228
455
|
@save_ignored_action.shortcut = @save_ignored_action_keyseq
|
229
456
|
@save_ignored_action.statusTip = tr('Save all ignored telemetry items in a configuration file')
|
230
|
-
connect(
|
457
|
+
@save_ignored_action.connect(SIGNAL('triggered()')) { save_config_file() }
|
231
458
|
|
232
|
-
@
|
233
|
-
@
|
234
|
-
|
235
|
-
|
236
|
-
@
|
237
|
-
|
238
|
-
connect(@view_ignored_action, SIGNAL('triggered()'), self, SLOT('handle_view_ignored_items()'))
|
459
|
+
@edit_ignored_action = Qt::Action.new(tr('&Edit Ignored'), self)
|
460
|
+
@edit_ignored_action_keyseq = Qt::KeySequence.new(tr('Ctrl+E'))
|
461
|
+
@edit_ignored_action.shortcut = @edit_ignored_action_keyseq
|
462
|
+
@edit_ignored_action.statusTip = tr('Edit the ignored telemetry items list')
|
463
|
+
@edit_ignored_action.connect(SIGNAL('triggered()')) { edit_ignored_items() }
|
464
|
+
end
|
239
465
|
|
466
|
+
# Initialize the application menu bar options
|
467
|
+
def initialize_menus
|
468
|
+
@file_menu = menuBar.addMenu(tr('&File'))
|
240
469
|
@file_menu.addAction(@open_ignored_action)
|
241
470
|
@file_menu.addAction(@save_ignored_action)
|
242
|
-
@file_menu.addAction(@
|
243
|
-
@file_menu.addAction(@view_ignored_action)
|
471
|
+
@file_menu.addAction(@edit_ignored_action)
|
244
472
|
@file_menu.addSeparator()
|
245
473
|
@file_menu.addAction(@reset_action)
|
246
474
|
@file_menu.addAction(@options_action)
|
@@ -253,29 +481,67 @@ module Cosmos
|
|
253
481
|
initialize_help_menu()
|
254
482
|
end
|
255
483
|
|
256
|
-
#
|
257
|
-
|
484
|
+
# Layout the main GUI tab widget with a view of all the out of limits items
|
485
|
+
# in one tab and a log tab showing all limits events.
|
486
|
+
def initialize_central_widget
|
487
|
+
@tabbook = Qt::TabWidget.new(self)
|
488
|
+
setCentralWidget(@tabbook)
|
489
|
+
@widget = Qt::Widget.new
|
490
|
+
@layout = Qt::VBoxLayout.new(@widget)
|
491
|
+
|
492
|
+
@monitored_state_text_field = Qt::LineEdit.new(self)
|
493
|
+
@monitored_state_text_field.setText('Stale')
|
494
|
+
@monitored_state_text_field.setAlignment(Qt::AlignCenter)
|
495
|
+
@monitored_state_text_field.setReadOnly(true)
|
496
|
+
@palette = Qt::Palette.new()
|
497
|
+
@palette.setColor(Qt::Palette::Base, Qt::Color.new(255,0,255))
|
498
|
+
@monitored_state_text_field.setPalette(@palette)
|
499
|
+
@state_label = Qt::Label.new('Monitored Limits State: ')
|
500
|
+
|
501
|
+
@monitored_state_frame = Qt::HBoxLayout.new
|
502
|
+
@monitored_state_frame.addWidget(@state_label)
|
503
|
+
@monitored_state_frame.addWidget(@monitored_state_text_field)
|
504
|
+
label = Qt::Label.new
|
505
|
+
filename = File.join(::Cosmos::PATH, 'data', 'spinner.gif')
|
506
|
+
movie = Qt::Movie.new(filename)
|
507
|
+
label.setMovie(movie)
|
508
|
+
movie.start
|
509
|
+
@monitored_state_frame.addWidget(label)
|
510
|
+
@monitored_state_frame.setAlignment(Qt::AlignTop)
|
511
|
+
@layout.addLayout(@monitored_state_frame)
|
512
|
+
|
513
|
+
@scroll = Qt::ScrollArea.new
|
514
|
+
@scroll_widget = Qt::Widget.new
|
515
|
+
@scroll.setWidget(@scroll_widget)
|
516
|
+
@scroll_layout = Qt::VBoxLayout.new(@scroll_widget)
|
517
|
+
@scroll_layout.setSizeConstraint(Qt::Layout::SetMinAndMaxSize)
|
518
|
+
@layout.addWidget(@scroll)
|
519
|
+
|
520
|
+
@log_output = Qt::PlainTextEdit.new
|
521
|
+
@log_output.setReadOnly(true)
|
522
|
+
@log_output.setMaximumBlockCount(100)
|
523
|
+
|
524
|
+
@tabbook.addTab(@widget, "Limits")
|
525
|
+
@tabbook.addTab(@log_output, "Log")
|
526
|
+
end
|
527
|
+
|
528
|
+
def show_options_dialog
|
258
529
|
Qt::Dialog.new(self) do |dialog|
|
259
530
|
dialog.setWindowTitle('Options')
|
260
531
|
|
261
532
|
colorblind_box = Qt::CheckBox.new('Colorblind Mode Enabled', self)
|
262
|
-
if
|
263
|
-
colorblind_box.setCheckState(Qt::Checked)
|
264
|
-
end
|
533
|
+
colorblind_box.setCheckState(Qt::Checked) if @colorblind
|
265
534
|
|
266
535
|
ok = Qt::PushButton.new('Ok') do
|
267
536
|
connect(SIGNAL('clicked()')) { dialog.accept }
|
268
537
|
end
|
269
|
-
|
270
538
|
cancel = Qt::PushButton.new('Cancel') do
|
271
539
|
connect(SIGNAL('clicked()')) { dialog.reject }
|
272
540
|
end
|
273
|
-
|
274
541
|
buttons = Qt::HBoxLayout.new do
|
275
542
|
addWidget(ok)
|
276
543
|
addWidget(cancel)
|
277
544
|
end
|
278
|
-
|
279
545
|
dialog.layout = Qt::VBoxLayout.new do
|
280
546
|
addWidget(colorblind_box)
|
281
547
|
addLayout(buttons)
|
@@ -288,471 +554,247 @@ module Cosmos
|
|
288
554
|
else
|
289
555
|
@colorblind = false
|
290
556
|
end
|
291
|
-
|
292
|
-
widget.
|
293
|
-
widget.process_settings
|
557
|
+
(0...@scroll_layout.count).each do |index|
|
558
|
+
@scroll_layout.itemAt(index).widget.set_colorblind(@colorblind)
|
294
559
|
end
|
295
560
|
end
|
296
561
|
dialog.dispose
|
297
562
|
end
|
298
563
|
end
|
299
564
|
|
300
|
-
#
|
301
|
-
def
|
302
|
-
|
565
|
+
# @return [String] Fully qualified path to the configuration file
|
566
|
+
def config_path
|
567
|
+
# If the config file has been set then just return it
|
568
|
+
return @filename if @filename
|
569
|
+
# This is the default path to the configuration files
|
570
|
+
File.join(::Cosmos::USERPATH, 'config', 'tools', 'limits_monitor', 'limits_monitor.txt')
|
303
571
|
end
|
304
572
|
|
305
|
-
#
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
begin
|
313
|
-
initialized = nil
|
314
|
-
break if @cancel_thread
|
315
|
-
Qt.execute_in_main_thread(true) do
|
316
|
-
initialized = @initialized
|
317
|
-
end
|
318
|
-
unless initialized
|
319
|
-
@limits_set = nil
|
320
|
-
break if @cancel_thread
|
321
|
-
Qt.execute_in_main_thread(true) do
|
322
|
-
@out_of_limits_items = []
|
323
|
-
end
|
324
|
-
unsubscribe_limits_events(@queue_id) if @queue_id
|
325
|
-
@queue_id = nil
|
326
|
-
|
327
|
-
handle_reset()
|
328
|
-
|
329
|
-
# Get the current limits set
|
330
|
-
@limits_set = get_limits_set()
|
331
|
-
|
332
|
-
# Subscribe to limits notifications
|
333
|
-
@queue_id = subscribe_limits_events(100000)
|
334
|
-
|
335
|
-
# Get initial list of out of limits items
|
336
|
-
items = get_out_of_limits()
|
337
|
-
unless items.empty?
|
338
|
-
break if @cancel_thread
|
339
|
-
Qt.execute_in_main_thread(true) do
|
340
|
-
items.each do |item|
|
341
|
-
unless @ignored_items.includes_item?(item) and !@items.includes_item?(item)
|
342
|
-
@new_items << [item[0], item[1], item[2]]
|
343
|
-
@out_of_limits_items << [item[0], item[1], item[2]]
|
344
|
-
end
|
345
|
-
end
|
346
|
-
handle_new_items()
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
break if @cancel_thread
|
351
|
-
Qt.execute_in_main_thread(true) do
|
352
|
-
@initialized = true
|
353
|
-
if @ignored_items.empty?
|
354
|
-
statusBar.showMessage(tr(""))
|
355
|
-
else
|
356
|
-
statusBar.showMessage('Warning: Some Telemetry Items are Ignored')
|
357
|
-
end
|
358
|
-
end
|
359
|
-
|
360
|
-
end
|
361
|
-
|
362
|
-
begin
|
363
|
-
break if @cancel_thread
|
364
|
-
type, data = get_limits_event(@queue_id, true)
|
365
|
-
break if @cancel_thread
|
366
|
-
rescue ThreadError
|
367
|
-
break if @cancel_thread
|
368
|
-
break if @limits_sleeper.sleep(1)
|
369
|
-
next
|
370
|
-
end
|
371
|
-
|
372
|
-
break if @cancel_thread
|
373
|
-
|
374
|
-
case type
|
375
|
-
when :LIMITS_CHANGE
|
376
|
-
item = [data[0], data[1], data[2]]
|
377
|
-
if data[4] != :GREEN and data[4] != :GREEN_HIGH and data[4] != :GREEN_LOW and data[4] != :BLUE and data[4] != nil
|
378
|
-
case data[4]
|
379
|
-
when :YELLOW, :YELLOW_HIGH, :YELLOW_LOW
|
380
|
-
to_print = Time.now.formatted << ' ' << "WARN: #{data[0]} #{data[1]} #{data[2]} = #{tlm(data[0], data[1], data[2])} is #{data[4]}\n"
|
381
|
-
update_log(to_print, 1)
|
382
|
-
when :RED, :RED_HIGH, :RED_LOW
|
383
|
-
to_print = Time.now.formatted << ' ' << "ERROR: #{data[0]} #{data[1]} #{data[2]} = #{tlm(data[0], data[1], data[2])} is #{data[4]}\n"
|
384
|
-
update_log(to_print, 2)
|
385
|
-
end
|
386
|
-
|
387
|
-
# Limits changed to non-green
|
388
|
-
unless @out_of_limits_items.includes_item?(item) or @ignored_items.includes_item?(item)
|
389
|
-
@out_of_limits_items << item
|
390
|
-
@new_items << item
|
391
|
-
handle_new_items()
|
392
|
-
end
|
393
|
-
elsif data[4] == :GREEN or data[4] == :GREEN_HIGH or data[4] == :GREEN_LOW or data[4] == :BLUE
|
394
|
-
to_print = Time.now.formatted << ' ' << "INFO: #{data[0]} #{data[1]} #{data[2]} = #{tlm(data[0], data[1], data[2])} returned to GREEN\n"
|
395
|
-
|
396
|
-
if data[4] == :BLUE
|
397
|
-
update_log(to_print, 3)
|
398
|
-
else
|
399
|
-
update_log(to_print, 0)
|
400
|
-
end
|
401
|
-
end
|
402
|
-
|
403
|
-
when :LIMITS_SET
|
404
|
-
if @limits_set != data
|
405
|
-
break if @cancel_thread
|
406
|
-
Qt.execute_in_main_thread(true) do
|
407
|
-
statusBar.showMessage('Limits Set Changed - Reseting')
|
408
|
-
@initialized = false
|
409
|
-
to_print = Time.now.formatted << ' ' << "INFO: Limits Set Changed to: #{data}\n"
|
410
|
-
update_log(to_print, 4)
|
411
|
-
end
|
412
|
-
break if @cancel_thread
|
413
|
-
end
|
414
|
-
|
415
|
-
when :LIMITS_SETTINGS
|
416
|
-
begin
|
417
|
-
System.limits.set(data[0], data[1], data[2], data[6], data[7], data[8], data[9], data[10], data[11], data[3], data[4], data[5])
|
418
|
-
break if @cancel_thread
|
419
|
-
Qt.execute_in_main_thread(true) do
|
420
|
-
statusBar.showMessage('Limits Settings Changed - Reseting')
|
421
|
-
@initialized = false
|
422
|
-
to_print = Time.now.formatted << ' ' << "INFO: Limits Settings Changed: #{data}\n"
|
423
|
-
update_log(to_print, 4)
|
424
|
-
end
|
425
|
-
break if @cancel_thread
|
426
|
-
rescue
|
427
|
-
# This can fail if we missed setting the DEFAULT limits set earlier - Oh well
|
428
|
-
end
|
429
|
-
end
|
430
|
-
|
431
|
-
rescue DRb::DRbConnError
|
432
|
-
break if @cancel_thread
|
433
|
-
@queue_id = nil
|
434
|
-
break if @cancel_thread
|
435
|
-
Qt.execute_in_main_thread(true) do
|
436
|
-
statusBar.showMessage('Error Connecting to Command and Telemetry Server - Reseting')
|
437
|
-
@initialized = false
|
438
|
-
end
|
439
|
-
break if @cancel_thread
|
440
|
-
break if @limits_sleeper.sleep(1)
|
441
|
-
end
|
442
|
-
end # loop
|
443
|
-
rescue Exception => error
|
444
|
-
Cosmos.handle_fatal_exception(error)
|
445
|
-
end
|
573
|
+
# Opens the configuration file and loads the ignored items
|
574
|
+
def open_config_file
|
575
|
+
filename = Qt::FileDialog::getOpenFileName(self,
|
576
|
+
"Open Configuration File", config_path())
|
577
|
+
unless filename.nil? || filename.empty?
|
578
|
+
result = @limits_items.open_config(filename)
|
579
|
+
statusBar.showMessage(tr(result))
|
446
580
|
end
|
447
581
|
end
|
448
582
|
|
449
|
-
#
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
Qt.execute_in_main_thread(true) do
|
458
|
-
begin
|
459
|
-
# Gather items for widgets
|
460
|
-
values, limits_states, limits_settings, limits_set = get_tlm_values(@items, :WITH_UNITS)
|
461
|
-
index = 0
|
462
|
-
@items.each do |target_name, packet_name, item_name|
|
463
|
-
begin
|
464
|
-
System.limits.set(target_name, packet_name, item_name, limits_settings[index][0], limits_settings[index][1], limits_settings[index][2], limits_settings[index][3], limits_settings[index][4], limits_settings[index][5], limits_set) if limits_settings[index]
|
465
|
-
rescue
|
466
|
-
# This can fail if we missed setting the DEFAULT limits set earlier - Oh well
|
467
|
-
end
|
468
|
-
index += 1
|
469
|
-
end
|
470
|
-
|
471
|
-
# Handle change in limits set
|
472
|
-
if limits_set != @value_limits_set
|
473
|
-
@value_limits_set = limits_set
|
474
|
-
@widgets.each do |widget|
|
475
|
-
widget.limits_set = @value_limits_set
|
476
|
-
end
|
477
|
-
end
|
478
|
-
|
479
|
-
# Update widgets with values and limits_states
|
480
|
-
@overall_limits_state = :STALE
|
481
|
-
(0..(values.length - 1)).each do |widget_index|
|
482
|
-
limits_state = limits_states[widget_index]
|
483
|
-
@widgets[widget_index].limits_state = limits_state
|
484
|
-
@widgets[widget_index].value = values[widget_index]
|
485
|
-
end
|
486
|
-
|
487
|
-
# Update overall limits state
|
488
|
-
modify_overall_limits_state(get_overall_limits_state(@ignored_items))
|
489
|
-
update_overall_limits_state()
|
490
|
-
rescue DRb::DRbConnError
|
491
|
-
# Do nothing
|
492
|
-
end
|
493
|
-
end
|
494
|
-
else
|
495
|
-
@overall_limits_state = :STALE
|
496
|
-
break if @cancel_thread
|
497
|
-
Qt.execute_in_main_thread(true) do
|
498
|
-
begin
|
499
|
-
modify_overall_limits_state(get_overall_limits_state(@ignored_items))
|
500
|
-
rescue DRb::DRbConnError
|
501
|
-
# Do nothing
|
502
|
-
end
|
503
|
-
update_overall_limits_state()
|
504
|
-
end
|
505
|
-
end
|
506
|
-
|
507
|
-
# Sleep until next polling period
|
508
|
-
break if @value_sleeper.sleep(1)
|
509
|
-
end
|
510
|
-
rescue Exception => error
|
511
|
-
Cosmos.handle_fatal_exception(error)
|
512
|
-
end
|
583
|
+
# Saves the ignored items to the configuration file
|
584
|
+
def save_config_file
|
585
|
+
filename = Qt::FileDialog.getSaveFileName(self,
|
586
|
+
'Save As...', config_path(), 'Configuration Files (*.txt)')
|
587
|
+
unless filename.nil? || filename.empty?
|
588
|
+
result = @limits_items.save_config(filename)
|
589
|
+
statusBar.showMessage(tr(result))
|
590
|
+
@filename = filename
|
513
591
|
end
|
514
592
|
end
|
515
593
|
|
516
|
-
#
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
@
|
523
|
-
|
524
|
-
|
525
|
-
@overall_limits_state = limits_state
|
526
|
-
end
|
527
|
-
when :GREEN, :GREEN_HIGH, :GREEN_LOW
|
528
|
-
if limits_state != nil and limits_state != :STALE and limits_state != :BLUE
|
529
|
-
@overall_limits_state = limits_state
|
530
|
-
end
|
531
|
-
when :YELLOW, :YELLOW_HIGH, :YELLOW_LOW
|
532
|
-
if limits_state == :RED or limits_state == :RED_HIGH or limits_state == :RED_LOW
|
533
|
-
@overall_limits_state = limits_state
|
534
|
-
end
|
535
|
-
end
|
536
|
-
end
|
537
|
-
|
538
|
-
# Changes the limits state on the status bar at the top of the screen.
|
539
|
-
def update_overall_limits_state
|
540
|
-
text = ''
|
541
|
-
case @overall_limits_state
|
542
|
-
when :STALE
|
543
|
-
palette = Cosmos.getPalette(Cosmos.getColor(0, 0, 0), Cosmos.getColor(255,0,255))
|
544
|
-
@monitored_state_text_field.setPalette(palette)
|
545
|
-
text = 'Stale'
|
546
|
-
when :GREEN, :GREEN_HIGH, :GREEN_LOW
|
547
|
-
palette = Cosmos.getPalette(Cosmos.getColor(0, 0, 0), Cosmos.getColor(0,255,0))
|
548
|
-
@monitored_state_text_field.setPalette(palette)
|
549
|
-
text = 'Green'
|
550
|
-
when :YELLOW, :YELLOW_HIGH, :YELLOW_LOW
|
551
|
-
palette = Cosmos.getPalette(Cosmos.getColor(0, 0, 0), Cosmos.getColor(255,255,0))
|
552
|
-
@monitored_state_text_field.setPalette(palette)
|
553
|
-
text = 'Yellow'
|
554
|
-
when :RED, :RED_HIGH, :RED_LOW
|
555
|
-
palette = Cosmos.getPalette(Cosmos.getColor(0, 0, 0), Cosmos.getColor(255,0,0))
|
556
|
-
@monitored_state_text_field.setPalette(palette)
|
557
|
-
text = 'Red'
|
558
|
-
when :BLUE
|
559
|
-
palette = Cosmos.getPalette(Cosmos.getColor(0, 0, 0), Cosmos.getColor(0,0,255))
|
560
|
-
@monitored_state_text_field.setPalette(palette)
|
561
|
-
text = 'Blue'
|
562
|
-
end
|
563
|
-
text << ' - Some Items Ignored' unless @ignored_items.empty?
|
564
|
-
@monitored_state_text_field.text = text
|
565
|
-
end
|
566
|
-
|
567
|
-
# Slot to handle when the clear ignored items menu option is selected.
|
568
|
-
def handle_clear_ignored_items
|
569
|
-
@ignored_items.clear
|
570
|
-
@initialized = false
|
571
|
-
statusBar.showMessage('')
|
572
|
-
end
|
573
|
-
|
574
|
-
# Slot to handle when the view ignored items menu option is selected.
|
575
|
-
def handle_view_ignored_items
|
576
|
-
# Turn Command into scripting text string
|
577
|
-
string = ''
|
578
|
-
@ignored_items.each do |item|
|
579
|
-
string << "#{item[0]} #{item[1]} #{item[2]}\n"
|
594
|
+
# Opens a dialog to allow the user to remove ignored items
|
595
|
+
def edit_ignored_items
|
596
|
+
items = []
|
597
|
+
index = 0
|
598
|
+
@limits_items.ignored.each do |target_name, packet_name, item_name|
|
599
|
+
item = Qt::ListWidgetItem.new("#{target_name} #{packet_name} #{item_name}")
|
600
|
+
item.setData(Qt::UserRole, Qt::Variant.new(@limits_items.ignored[index]))
|
601
|
+
items << item
|
602
|
+
index += 1
|
580
603
|
end
|
581
604
|
|
582
|
-
# Show Dialog box with text displaying ignored items
|
583
605
|
Qt::Dialog.new(self) do |dialog|
|
584
606
|
dialog.setWindowTitle('Ignored Telemetry Items')
|
585
|
-
|
586
|
-
|
587
|
-
|
607
|
+
list = Qt::ListWidget.new
|
608
|
+
list.setFocus()
|
609
|
+
# Allow multiple sections
|
610
|
+
list.setSelectionMode(Qt::AbstractItemView::ExtendedSelection)
|
611
|
+
items.each {|item| list.addItem(item) }
|
612
|
+
|
613
|
+
shortcut = Qt::Shortcut.new(Qt::KeySequence.new(Qt::KeySequence::Delete), list)
|
614
|
+
list.connect(shortcut, SIGNAL('activated()')) do
|
615
|
+
items = list.selectedItems()
|
616
|
+
(0...items.length).each do |index|
|
617
|
+
@limits_items.remove_ignored(items[index].data(Qt::UserRole).value)
|
618
|
+
end
|
619
|
+
list.remove_selected_items
|
620
|
+
list.setCurrentRow(0)
|
621
|
+
end
|
622
|
+
# Preselect the first row (works if list is empty) so the keyboard
|
623
|
+
# works instantly without having to click the list
|
624
|
+
list.setCurrentRow(0)
|
588
625
|
|
589
626
|
ok = Qt::PushButton.new('Ok') do
|
590
627
|
connect(SIGNAL('clicked()')) { dialog.done(0) }
|
591
628
|
end
|
592
|
-
|
593
|
-
|
594
|
-
|
629
|
+
remove = Qt::PushButton.new('Remove Selected') do
|
630
|
+
connect(SIGNAL('clicked()')) { shortcut.activated() }
|
631
|
+
end
|
632
|
+
button_layout = Qt::HBoxLayout.new do
|
595
633
|
addWidget(ok)
|
634
|
+
addStretch(1)
|
635
|
+
addWidget(remove)
|
636
|
+
end
|
637
|
+
dialog.layout = Qt::VBoxLayout.new do
|
638
|
+
addWidget(list)
|
639
|
+
addLayout(button_layout)
|
596
640
|
end
|
597
|
-
|
598
641
|
dialog.resize(500, 200)
|
599
642
|
dialog.exec
|
600
643
|
dialog.dispose
|
601
644
|
end
|
602
645
|
end
|
603
646
|
|
604
|
-
#
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
647
|
+
# Thread to monitor for broken limits and add them to the log and
|
648
|
+
# front panel when found.
|
649
|
+
def limits_thread
|
650
|
+
result = nil
|
651
|
+
color = nil
|
652
|
+
@limits_thread = Thread.new do
|
653
|
+
while true
|
654
|
+
break if @cancel_thread
|
655
|
+
Qt.execute_in_main_thread(true) do
|
656
|
+
result, color = @limits_items.process_events()
|
657
|
+
end
|
658
|
+
if result
|
659
|
+
update_log(result, color)
|
660
|
+
else
|
661
|
+
break if @limits_sleeper.sleep(1)
|
617
662
|
end
|
618
|
-
statusBar.showMessage("#{filename} saved")
|
619
|
-
rescue => e
|
620
|
-
statusBar.showMessage("Error Saving Configuration : #{e.message}")
|
621
663
|
end
|
622
664
|
end
|
665
|
+
rescue Exception => error
|
666
|
+
Cosmos.handle_fatal_exception(error)
|
623
667
|
end
|
624
668
|
|
625
|
-
#
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
669
|
+
# Add new out of limit item or stale packet
|
670
|
+
#
|
671
|
+
# @param target_name [String] Target name of out of limits item.
|
672
|
+
# @param packet_name [String] Packet name of out of limits item.
|
673
|
+
# @param item_name [String] Item name of out of limits item or nil
|
674
|
+
# if its a stale packet
|
675
|
+
# @return [Qt::Widget] The new widget that was created
|
676
|
+
def new_gui_item(target_name, packet_name, item_name)
|
677
|
+
widget = nil
|
678
|
+
Qt.execute_in_main_thread(true) do
|
679
|
+
widget = LimitsWidget.new(self, target_name, packet_name, item_name)
|
680
|
+
@scroll_layout.addWidget(widget)
|
634
681
|
end
|
682
|
+
widget
|
635
683
|
end
|
636
684
|
|
637
|
-
#
|
685
|
+
# Update out of limit item with a values
|
638
686
|
#
|
639
|
-
# @param
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
parser.parse_file(filename) do |keyword, params|
|
647
|
-
case keyword
|
648
|
-
when 'IGNORE'
|
649
|
-
ignore_item([params[0], params[1], params[2]])
|
650
|
-
end
|
651
|
-
end
|
652
|
-
statusBar.showMessage("#{filename} loaded")
|
653
|
-
rescue => e
|
654
|
-
statusBar.showMessage("Error Loading Configuration : #{e.message}")
|
687
|
+
# @param target_name [String] Target name of out of limits item.
|
688
|
+
# @param packet_name [String] Packet name of out of limits item.
|
689
|
+
# @param item_name [String] Item name of out of limits item or nil
|
690
|
+
# if its a stale packet
|
691
|
+
def update_gui_item(widget, value, limits_state, limits_set)
|
692
|
+
Qt.execute_in_main_thread(true) do
|
693
|
+
widget.set_values(value, limits_state, limits_set) if widget
|
655
694
|
end
|
656
695
|
end
|
657
696
|
|
697
|
+
# Reset the GUI by clearing all items
|
698
|
+
def clear_gui_items
|
699
|
+
Qt.execute_in_main_thread(true) { @scroll_layout.removeAll }
|
700
|
+
end
|
701
|
+
|
658
702
|
# Update front panel to ignore an item when the corresponding button is pressed.
|
659
703
|
#
|
660
|
-
# @param item [Array] Array containing the target
|
661
|
-
# item to ignore.
|
662
|
-
def
|
704
|
+
# @param item [Array<String,String,String] Array containing the target name,
|
705
|
+
# packet name, and item name of the item to ignore.
|
706
|
+
def ignore(widget, item)
|
707
|
+
@limits_items.ignore(item)
|
663
708
|
Qt.execute_in_main_thread(true) do
|
664
|
-
@
|
665
|
-
|
666
|
-
|
667
|
-
hframe_to_dispose = @widget_hframes[delete_index]
|
668
|
-
widget_to_dispose = @widgets[delete_index]
|
669
|
-
button_to_dispose = @buttons[delete_index]
|
670
|
-
@widgets.delete_at(delete_index)
|
671
|
-
@buttons.delete_at(delete_index)
|
672
|
-
@widget_hframes.delete_at(delete_index)
|
673
|
-
@scroll_layout.removeItem(hframe_to_dispose)
|
674
|
-
widget_to_dispose.dispose
|
675
|
-
button_to_dispose.dispose
|
676
|
-
hframe_to_dispose.dispose
|
677
|
-
@scroll.repaint
|
678
|
-
end
|
709
|
+
@scroll_layout.removeWidget(widget)
|
710
|
+
widget.dispose
|
711
|
+
@scroll_widget.adjustSize
|
679
712
|
statusBar.showMessage('Warning: Some Telemetry Items are Ignored')
|
680
713
|
end
|
681
714
|
end
|
682
715
|
|
683
|
-
#
|
684
|
-
|
685
|
-
|
686
|
-
|
716
|
+
# Update the log panel with limits change information.
|
717
|
+
#
|
718
|
+
# @param message [String] Text string with information about which item is out of
|
719
|
+
# limits, what its value is, and what limit was broken (red_low, yellow_low, etc.)
|
720
|
+
# @param color [Symbol] Color of text to add.
|
721
|
+
def update_log(message, color)
|
722
|
+
return if @cancel_thread
|
687
723
|
Qt.execute_in_main_thread(true) do
|
688
|
-
@
|
689
|
-
|
690
|
-
|
724
|
+
@tf ||= Qt::TextCharFormat.new
|
725
|
+
case color
|
726
|
+
when :GREEN
|
727
|
+
brush = Cosmos.getBrush(Cosmos::GREEN)
|
728
|
+
when :YELLOW
|
729
|
+
brush = Cosmos.getBrush(Cosmos::YELLOW)
|
730
|
+
when :RED
|
731
|
+
brush = Cosmos.getBrush(Cosmos::RED)
|
732
|
+
when :BLUE
|
733
|
+
brush = Cosmos.getBrush(Cosmos::BLUE)
|
734
|
+
else # :BLACK
|
735
|
+
brush = Cosmos.getBrush(Cosmos::BLACK)
|
691
736
|
end
|
737
|
+
@tf.setForeground(brush)
|
738
|
+
@log_output.setCurrentCharFormat(@tf)
|
739
|
+
@log_output.appendPlainText(message.chomp)
|
692
740
|
end
|
741
|
+
end
|
693
742
|
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
743
|
+
# Thread to request the out of limits values and update them at 1Hz.
|
744
|
+
# Also updates the status bar at the top of the front panel indicating
|
745
|
+
# the overall limits value of the system.
|
746
|
+
def value_thread
|
747
|
+
@value_thread = Thread.new do
|
748
|
+
while true
|
749
|
+
break if @cancel_thread
|
750
|
+
Qt.execute_in_main_thread(true) do
|
751
|
+
if @limits_items.initialized
|
752
|
+
@limits_items.update_values()
|
753
|
+
update_overall_limits_state(@limits_items.overall_state())
|
754
|
+
else
|
755
|
+
# Set the status bar message to expire in 2s since this runs at 1Hz
|
756
|
+
statusBar.showMessage('Error Connecting to Command and Telemetry Server', 2000)
|
705
757
|
end
|
706
|
-
index += 1
|
707
|
-
end
|
708
|
-
index = 0
|
709
|
-
|
710
|
-
@new_widgets.each do |widget|
|
711
|
-
limits_state = limits_states[index]
|
712
|
-
widget.limits_set = limits_set
|
713
|
-
widget.limits_state = limits_state
|
714
|
-
widget.value = values[index]
|
715
|
-
index += 1
|
716
|
-
modify_overall_limits_state(limits_state)
|
717
|
-
end
|
718
|
-
|
719
|
-
update_overall_limits_state()
|
720
|
-
|
721
|
-
@new_items.each do |item|
|
722
|
-
@items << item
|
723
758
|
end
|
724
|
-
|
725
|
-
rescue DRb::DRbConnError
|
726
|
-
statusBar.showMessage('Error Connecting to Command and Telemetry Server - Reseting')
|
727
|
-
@initialized = false
|
759
|
+
break if @value_sleeper.sleep(1)
|
728
760
|
end
|
729
761
|
end
|
762
|
+
rescue Exception => error
|
763
|
+
Cosmos.handle_fatal_exception(error)
|
730
764
|
end
|
731
765
|
|
732
|
-
#
|
733
|
-
def
|
766
|
+
# Changes the limits state on the status bar at the top of the screen.
|
767
|
+
def update_overall_limits_state(state)
|
734
768
|
Qt.execute_in_main_thread(true) do
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
769
|
+
text = ''
|
770
|
+
case state
|
771
|
+
when :STALE
|
772
|
+
palette = Cosmos.getPalette(Cosmos.getColor(0, 0, 0), Cosmos.getColor(255,0,255))
|
773
|
+
@monitored_state_text_field.setPalette(palette)
|
774
|
+
text = 'Stale'
|
775
|
+
when :GREEN, :GREEN_HIGH, :GREEN_LOW
|
776
|
+
palette = Cosmos.getPalette(Cosmos.getColor(0, 0, 0), Cosmos.getColor(0,255,0))
|
777
|
+
@monitored_state_text_field.setPalette(palette)
|
778
|
+
text = 'Green'
|
779
|
+
when :YELLOW, :YELLOW_HIGH, :YELLOW_LOW
|
780
|
+
palette = Cosmos.getPalette(Cosmos.getColor(0, 0, 0), Cosmos.getColor(255,255,0))
|
781
|
+
@monitored_state_text_field.setPalette(palette)
|
782
|
+
text = 'Yellow'
|
783
|
+
when :RED, :RED_HIGH, :RED_LOW
|
784
|
+
palette = Cosmos.getPalette(Cosmos.getColor(0, 0, 0), Cosmos.getColor(255,0,0))
|
785
|
+
@monitored_state_text_field.setPalette(palette)
|
786
|
+
text = 'Red'
|
787
|
+
when :BLUE
|
788
|
+
palette = Cosmos.getPalette(Cosmos.getColor(0, 0, 0), Cosmos.getColor(0,0,255))
|
789
|
+
@monitored_state_text_field.setPalette(palette)
|
790
|
+
text = 'Blue'
|
740
791
|
end
|
741
|
-
@
|
742
|
-
|
743
|
-
@widgets = []
|
744
|
-
@new_widgets = []
|
745
|
-
@buttons = []
|
746
|
-
@widget_hframes = []
|
747
|
-
@items = []
|
748
|
-
@value_limits_set = :DEFAULT
|
749
|
-
|
750
|
-
@overall_limits_state = :STALE
|
751
|
-
update_overall_limits_state()
|
792
|
+
text << ' - Some Items Ignored' if @limits_items.ignored_items?
|
793
|
+
@monitored_state_text_field.text = text
|
752
794
|
end
|
753
795
|
end
|
754
796
|
|
755
|
-
# Handle the window closing
|
797
|
+
# Handle the window closing
|
756
798
|
def closeEvent(event)
|
757
799
|
@cancel_thread = true
|
758
800
|
@value_sleeper.cancel
|
@@ -792,5 +834,4 @@ module Cosmos
|
|
792
834
|
end
|
793
835
|
|
794
836
|
end # class LimitsMonitor
|
795
|
-
|
796
837
|
end # module Cosmos
|