cosmos 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (912) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +48 -48
  4. data/.travis.yml +7 -7
  5. data/CONTRIBUTING.txt +50 -50
  6. data/Gemfile +6 -6
  7. data/Guardfile +27 -27
  8. data/LICENSE.txt +879 -879
  9. data/Manifest.txt +1116 -1114
  10. data/README.md +109 -107
  11. data/Rakefile +214 -214
  12. data/autohotkey/config/data/diamond.STL +57 -57
  13. data/autohotkey/config/system/system.txt +34 -34
  14. data/autohotkey/config/targets/COSMOS/cmd_tlm/cosmos_server_cmds.txt +41 -41
  15. data/autohotkey/config/targets/COSMOS/cmd_tlm/cosmos_server_tlm.txt +15 -15
  16. data/autohotkey/config/targets/COSMOS/cmd_tlm_server.txt +6 -6
  17. data/autohotkey/config/targets/COSMOS/target.txt +5 -5
  18. data/autohotkey/config/targets/INST/cmd_tlm/inst_cmds.txt +121 -121
  19. data/autohotkey/config/targets/INST/cmd_tlm/inst_tlm.txt +247 -247
  20. data/autohotkey/config/targets/INST/cmd_tlm_server.txt +5 -5
  21. data/autohotkey/config/targets/INST/lib/example_limits_response.rb +30 -30
  22. data/autohotkey/config/targets/INST/lib/sim_inst.rb +305 -294
  23. data/autohotkey/config/targets/INST/screens/adcs.txt +46 -46
  24. data/autohotkey/config/targets/INST/screens/array.txt +7 -7
  25. data/autohotkey/config/targets/INST/screens/block.txt +8 -8
  26. data/autohotkey/config/targets/INST/screens/commanding.txt +30 -30
  27. data/autohotkey/config/targets/INST/screens/graphs.txt +14 -14
  28. data/autohotkey/config/targets/INST/screens/ground.txt +25 -25
  29. data/autohotkey/config/targets/INST/screens/health_status.txt +33 -33
  30. data/autohotkey/config/targets/INST/screens/hs.txt +49 -49
  31. data/autohotkey/config/targets/INST/screens/image.txt +21 -21
  32. data/autohotkey/config/targets/INST/screens/latest.txt +23 -23
  33. data/autohotkey/config/targets/INST/screens/mech.txt +25 -25
  34. data/autohotkey/config/targets/INST/screens/other.txt +25 -25
  35. data/autohotkey/config/targets/INST/screens/params.txt +25 -25
  36. data/autohotkey/config/targets/INST/screens/tabs.txt +68 -68
  37. data/autohotkey/config/targets/INST/target.txt +26 -26
  38. data/autohotkey/config/targets/META/cmd_tlm/meta_cmd.txt +10 -10
  39. data/autohotkey/config/targets/META/cmd_tlm/meta_tlm.txt +9 -9
  40. data/autohotkey/config/targets/SYSTEM/cmd_tlm/limits_groups.txt +7 -7
  41. data/autohotkey/config/targets/SYSTEM/screens/error.txt +11 -11
  42. data/autohotkey/config/tools/cmd_tlm_server/cmd_tlm_server.txt +22 -22
  43. data/autohotkey/config/tools/data_viewer/data_viewer.txt +11 -11
  44. data/autohotkey/config/tools/handbook_creator/handbook_creator.txt +49 -49
  45. data/autohotkey/config/tools/handbook_creator/templates/command_packets.html.erb +86 -86
  46. data/autohotkey/config/tools/handbook_creator/templates/command_toc.html.erb +38 -38
  47. data/autohotkey/config/tools/handbook_creator/templates/footer.html.erb +9 -9
  48. data/autohotkey/config/tools/handbook_creator/templates/header.html.erb +25 -25
  49. data/autohotkey/config/tools/handbook_creator/templates/limits_groups.html.erb +13 -13
  50. data/autohotkey/config/tools/handbook_creator/templates/nav.html.erb +27 -27
  51. data/autohotkey/config/tools/handbook_creator/templates/overview.html.erb +1 -1
  52. data/autohotkey/config/tools/handbook_creator/templates/pdf_cover.html.erb +23 -23
  53. data/autohotkey/config/tools/handbook_creator/templates/pdf_footer.html.erb +33 -33
  54. data/autohotkey/config/tools/handbook_creator/templates/pdf_header.html.erb +41 -41
  55. data/autohotkey/config/tools/handbook_creator/templates/telemetry_packets.html.erb +80 -80
  56. data/autohotkey/config/tools/handbook_creator/templates/telemetry_toc.html.erb +38 -38
  57. data/autohotkey/config/tools/handbook_creator/templates/title.html.erb +1 -1
  58. data/autohotkey/config/tools/launcher/launcher.txt +38 -38
  59. data/autohotkey/config/tools/script_runner/script_runner.txt +3 -3
  60. data/autohotkey/config/tools/table_manager/ConfigTables_def.txt +8 -8
  61. data/autohotkey/config/tools/table_manager/OneDimensionalTable_def.txt +19 -19
  62. data/autohotkey/config/tools/table_manager/TwoDimensionalTable_def.txt +248 -248
  63. data/autohotkey/config/tools/test_runner/test_runner.txt +8 -8
  64. data/autohotkey/config/tools/test_runner/test_runner2.txt +11 -11
  65. data/autohotkey/config/tools/test_runner/test_runner3.txt +6 -6
  66. data/autohotkey/config/tools/test_runner/test_runner4.txt +1 -1
  67. data/autohotkey/config/tools/tlm_extractor/tlm_extractor.txt +13 -13
  68. data/autohotkey/config/tools/tlm_extractor/tlm_extractor2.txt +9 -9
  69. data/autohotkey/config/tools/tlm_grapher/bad.txt +50 -50
  70. data/autohotkey/config/tools/tlm_grapher/temp1-4.txt +51 -51
  71. data/autohotkey/config/tools/tlm_grapher/test2.txt +111 -111
  72. data/autohotkey/config/tools/tlm_viewer/tlm_viewer.txt +24 -24
  73. data/autohotkey/config/tools/tlm_viewer/tlm_viewer2.txt +4 -4
  74. data/autohotkey/config/tools/tlm_viewer/tlm_viewer3.txt +3 -3
  75. data/autohotkey/lib/example_background_task.rb +42 -42
  76. data/autohotkey/lib/user_version.rb +3 -3
  77. data/autohotkey/procedures/clear_util.rb +7 -7
  78. data/autohotkey/procedures/collect.rb +18 -18
  79. data/autohotkey/procedures/collect_util.rb +14 -14
  80. data/autohotkey/procedures/example_test.rb +67 -67
  81. data/autohotkey/procedures/example_test2.rb +74 -74
  82. data/autohotkey/procedures/script_test.rb +17 -17
  83. data/autohotkey/procedures/syntax_error.rb +18 -18
  84. data/autohotkey/tools/CmdExtractorAHK +16 -16
  85. data/autohotkey/tools/CmdSender +14 -14
  86. data/autohotkey/tools/CmdSenderAHK +18 -18
  87. data/autohotkey/tools/CmdTlmServer +14 -14
  88. data/autohotkey/tools/CmdTlmServerAHK +28 -28
  89. data/autohotkey/tools/CmdTlmServerAHK2 +17 -17
  90. data/autohotkey/tools/DataViewer +14 -14
  91. data/autohotkey/tools/DataViewerAHK +17 -17
  92. data/autohotkey/tools/HandbookCreatorAHK +20 -20
  93. data/autohotkey/tools/LauncherAHK +17 -17
  94. data/autohotkey/tools/LimitsMonitorAHK +20 -20
  95. data/autohotkey/tools/OpenGLBuilderAHK +20 -20
  96. data/autohotkey/tools/PacketViewer +14 -14
  97. data/autohotkey/tools/PacketViewerAHK +18 -18
  98. data/autohotkey/tools/PacketViewerAHK2 +17 -17
  99. data/autohotkey/tools/Replay +14 -14
  100. data/autohotkey/tools/Replay.bat +59 -59
  101. data/autohotkey/tools/ReplayAHK +17 -17
  102. data/autohotkey/tools/ScriptRunner +14 -14
  103. data/autohotkey/tools/ScriptRunnerAHK +20 -20
  104. data/autohotkey/tools/ScriptRunnerAHK2 +17 -17
  105. data/autohotkey/tools/TableManager +14 -14
  106. data/autohotkey/tools/TableManagerAHK +30 -30
  107. data/autohotkey/tools/TestRunner +15 -15
  108. data/autohotkey/tools/TestRunnerAHK +17 -17
  109. data/autohotkey/tools/TestRunnerAHK2 +17 -17
  110. data/autohotkey/tools/TestRunnerAHK3 +17 -17
  111. data/autohotkey/tools/TestRunnerAHK4 +17 -17
  112. data/autohotkey/tools/TlmExtractor +15 -15
  113. data/autohotkey/tools/TlmExtractorAHK +19 -19
  114. data/autohotkey/tools/TlmExtractorAHK2 +16 -16
  115. data/autohotkey/tools/TlmExtractorAHK3 +16 -16
  116. data/autohotkey/tools/TlmGrapher +14 -14
  117. data/autohotkey/tools/TlmGrapherAHK +19 -19
  118. data/autohotkey/tools/TlmGrapherAHK2 +23 -23
  119. data/autohotkey/tools/TlmGrapherAHK3 +17 -17
  120. data/autohotkey/tools/TlmGrapherAHK4 +17 -17
  121. data/autohotkey/tools/TlmViewer +14 -14
  122. data/autohotkey/tools/TlmViewerAHK +28 -28
  123. data/autohotkey/tools/TlmViewerAHK2 +18 -18
  124. data/autohotkey/tools/TlmViewerAHK3 +18 -18
  125. data/autohotkey/tools/TlmViewerAHK4 +18 -18
  126. data/autohotkey/tools/TlmViewerAHK5 +18 -18
  127. data/autohotkey/tools/autohotkey.rb +37 -37
  128. data/autohotkey/tools/cmd_extractor.ahk +27 -27
  129. data/autohotkey/tools/cmd_sender.ahk +182 -162
  130. data/autohotkey/tools/cmd_tlm_server.ahk +89 -89
  131. data/autohotkey/tools/cmd_tlm_server2.ahk +45 -45
  132. data/autohotkey/tools/data_viewer.ahk +135 -135
  133. data/autohotkey/tools/handbook_creator.ahk +23 -23
  134. data/autohotkey/tools/launcher.ahk +41 -41
  135. data/autohotkey/tools/limits_monitor.ahk +70 -70
  136. data/autohotkey/tools/open_gl_builder.ahk +134 -134
  137. data/autohotkey/tools/packet_viewer.ahk +143 -143
  138. data/autohotkey/tools/packet_viewer2.ahk +9 -9
  139. data/autohotkey/tools/replay.ahk +98 -98
  140. data/autohotkey/tools/script_runner.ahk +589 -589
  141. data/autohotkey/tools/script_runner2.ahk +34 -31
  142. data/autohotkey/tools/table_manager.ahk +220 -220
  143. data/autohotkey/tools/test_runner.ahk +262 -259
  144. data/autohotkey/tools/test_runner2.ahk +52 -52
  145. data/autohotkey/tools/test_runner3.ahk +13 -13
  146. data/autohotkey/tools/tlm_extractor.ahk +272 -272
  147. data/autohotkey/tools/tlm_grapher.ahk +642 -642
  148. data/autohotkey/tools/tlm_grapher2.ahk +115 -115
  149. data/autohotkey/tools/tlm_grapher3.ahk +24 -24
  150. data/autohotkey/tools/tlm_viewer.ahk +133 -133
  151. data/autohotkey/tools/tlm_viewer2.ahk +50 -49
  152. data/autohotkey/tools/tlm_viewer4.ahk +4 -4
  153. data/autohotkey/tools/tlm_viewer5.ahk +20 -20
  154. data/bin/cosmos +96 -96
  155. data/bin/cstol_converter +1166 -1166
  156. data/bin/rubysloc +85 -85
  157. data/cosmos.gemspec +98 -97
  158. data/data/about.txt +4 -4
  159. data/data/crc.txt +306 -305
  160. data/data/diamond.STL +57 -57
  161. data/data/legal.txt +9 -9
  162. data/demo/Gemfile +6 -6
  163. data/demo/Launcher +15 -15
  164. data/demo/Launcher.bat +59 -59
  165. data/demo/Rakefile +61 -61
  166. data/demo/config/data/crc.txt +222 -206
  167. data/demo/config/data/diamond.STL +57 -57
  168. data/demo/config/data/meta_init.txt +4 -4
  169. data/demo/config/system/system.txt +34 -34
  170. data/demo/config/system/system2.txt +33 -33
  171. data/demo/config/targets/COSMOS/cmd_tlm/cosmos_server_cmds.txt +41 -41
  172. data/demo/config/targets/COSMOS/cmd_tlm/cosmos_server_tlm.txt +15 -15
  173. data/demo/config/targets/COSMOS/cmd_tlm_server.txt +6 -6
  174. data/demo/config/targets/COSMOS/screens/limits_change.txt +20 -20
  175. data/demo/config/targets/COSMOS/screens/version.txt +19 -19
  176. data/demo/config/targets/COSMOS/target.txt +11 -11
  177. data/demo/config/targets/EXAMPLE/cmd_tlm/example_cmds.txt +2 -2
  178. data/demo/config/targets/EXAMPLE/cmd_tlm/example_tlm.txt +3 -3
  179. data/demo/config/targets/EXAMPLE/cmd_tlm_server.txt +6 -6
  180. data/demo/config/targets/EXAMPLE/lib/example_interface.rb +22 -22
  181. data/demo/config/targets/EXAMPLE/target.txt +6 -6
  182. data/demo/config/targets/INST/cmd_tlm/inst_cmds.txt +121 -121
  183. data/demo/config/targets/INST/cmd_tlm/inst_tlm.txt +247 -247
  184. data/demo/config/targets/INST/cmd_tlm_server.txt +5 -5
  185. data/demo/config/targets/INST/lib/example_limits_response.rb +30 -30
  186. data/demo/config/targets/INST/lib/sim_inst.rb +305 -294
  187. data/demo/config/targets/INST/screens/adcs.txt +46 -46
  188. data/demo/config/targets/INST/screens/array.txt +15 -15
  189. data/demo/config/targets/INST/screens/block.txt +8 -8
  190. data/demo/config/targets/INST/screens/commanding.txt +30 -30
  191. data/demo/config/targets/INST/screens/graphs.txt +14 -14
  192. data/demo/config/targets/INST/screens/ground.txt +25 -25
  193. data/demo/config/targets/INST/screens/hs.txt +44 -44
  194. data/demo/config/targets/INST/screens/latest.txt +23 -23
  195. data/demo/config/targets/INST/screens/other.txt +29 -29
  196. data/demo/config/targets/INST/screens/tabs.txt +70 -70
  197. data/demo/config/targets/INST/target.txt +33 -33
  198. data/demo/config/targets/META/cmd_tlm/meta_cmd.txt +10 -10
  199. data/demo/config/targets/META/cmd_tlm/meta_tlm.txt +13 -13
  200. data/demo/config/targets/SYSTEM/cmd_tlm/limits_groups.txt +7 -7
  201. data/demo/config/targets/SYSTEM/cmd_tlm/override.txt +29 -29
  202. data/demo/config/targets/SYSTEM/screens/status.txt +12 -12
  203. data/demo/config/targets/TEMPLATED/cmd_tlm/templated_cmds.txt +13 -12
  204. data/demo/config/targets/TEMPLATED/cmd_tlm/templated_tlm.txt +3 -3
  205. data/demo/config/targets/TEMPLATED/cmd_tlm_server.txt +6 -6
  206. data/demo/config/targets/TEMPLATED/lib/templated_interface.rb +54 -48
  207. data/demo/config/targets/TEMPLATED/target.txt +6 -6
  208. data/demo/config/tools/cmd_tlm_server/cmd_tlm_server.txt +33 -33
  209. data/demo/config/tools/cmd_tlm_server/cmd_tlm_server2.txt +29 -29
  210. data/demo/config/tools/data_viewer/data_viewer.txt +11 -11
  211. data/demo/config/tools/handbook_creator/handbook_creator.txt +66 -66
  212. data/demo/config/tools/handbook_creator/templates/command_packets.html.erb +86 -86
  213. data/demo/config/tools/handbook_creator/templates/command_toc.html.erb +38 -38
  214. data/demo/config/tools/handbook_creator/templates/footer.html.erb +9 -9
  215. data/demo/config/tools/handbook_creator/templates/header.html.erb +25 -25
  216. data/demo/config/tools/handbook_creator/templates/limits_groups.html.erb +13 -13
  217. data/demo/config/tools/handbook_creator/templates/nav.html.erb +27 -27
  218. data/demo/config/tools/handbook_creator/templates/overview.html.erb +1 -1
  219. data/demo/config/tools/handbook_creator/templates/pdf_cover.html.erb +23 -23
  220. data/demo/config/tools/handbook_creator/templates/pdf_footer.html.erb +33 -33
  221. data/demo/config/tools/handbook_creator/templates/pdf_header.html.erb +41 -41
  222. data/demo/config/tools/handbook_creator/templates/telemetry_packets.html.erb +80 -80
  223. data/demo/config/tools/handbook_creator/templates/telemetry_toc.html.erb +38 -38
  224. data/demo/config/tools/handbook_creator/templates/title.html.erb +1 -1
  225. data/demo/config/tools/launcher/launcher.txt +45 -45
  226. data/demo/config/tools/launcher/launcher2.txt +45 -45
  227. data/demo/config/tools/script_runner/script_runner.txt +3 -3
  228. data/demo/config/tools/table_manager/ConfigTables_def.txt +8 -8
  229. data/demo/config/tools/table_manager/ExampleTableDefinition.txt +24 -24
  230. data/demo/config/tools/table_manager/MCConfigurationTable_fsw1_def.txt +25 -25
  231. data/demo/config/tools/table_manager/MCConfigurationTable_fsw2_def.txt +25 -25
  232. data/demo/config/tools/table_manager/PPSSelectionTable_def.txt +8 -8
  233. data/demo/config/tools/table_manager/TLMMonitoringTable_def.txt +248 -248
  234. data/demo/config/tools/test_runner/test_runner.txt +17 -17
  235. data/demo/config/tools/tlm_extractor/tlm_extractor.txt +13 -13
  236. data/demo/config/tools/tlm_extractor/tlm_extractor2.txt +2 -2
  237. data/demo/config/tools/tlm_extractor/tlm_extractor3.txt +2 -2
  238. data/demo/config/tools/tlm_extractor/tlm_extractor4.txt +2 -2
  239. data/demo/config/tools/tlm_viewer/tlm_viewer.txt +41 -41
  240. data/demo/lib/example_background_task.rb +57 -52
  241. data/demo/lib/example_target.rb +113 -108
  242. data/demo/lib/scpi_target.rb +74 -74
  243. data/demo/lib/user_version.rb +3 -3
  244. data/demo/procedures/checks.rb +11 -11
  245. data/demo/procedures/clear_util.rb +7 -7
  246. data/demo/procedures/collect.rb +18 -18
  247. data/demo/procedures/collect_util.rb +14 -14
  248. data/demo/procedures/cosmos_api_test.rb +293 -293
  249. data/demo/procedures/disconnect.rb +29 -29
  250. data/demo/procedures/example_test.rb +182 -182
  251. data/demo/procedures/plot_test.rb +8 -8
  252. data/demo/procedures/run_example_test.rb +3 -3
  253. data/demo/procedures/test.rb +51 -51
  254. data/demo/tools/CmdExtractor +15 -15
  255. data/demo/tools/CmdExtractor.bat +59 -59
  256. data/demo/tools/CmdSender +15 -15
  257. data/demo/tools/CmdSender.bat +59 -59
  258. data/demo/tools/CmdTlmServer +15 -15
  259. data/demo/tools/CmdTlmServer.bat +59 -59
  260. data/demo/tools/DataViewer +15 -15
  261. data/demo/tools/DataViewer.bat +59 -59
  262. data/demo/tools/ExampleTarget +15 -15
  263. data/demo/tools/ExampleTarget.bat +59 -59
  264. data/demo/tools/HandbookCreator +15 -15
  265. data/demo/tools/HandbookCreator.bat +61 -61
  266. data/demo/tools/Launcher +15 -15
  267. data/demo/tools/Launcher.bat +59 -59
  268. data/demo/tools/LimitsMonitor +15 -15
  269. data/demo/tools/LimitsMonitor.bat +59 -59
  270. data/demo/tools/OpenGLBuilder +15 -15
  271. data/demo/tools/OpenGLBuilder.bat +59 -59
  272. data/demo/tools/PacketViewer +15 -15
  273. data/demo/tools/PacketViewer.bat +59 -59
  274. data/demo/tools/Replay +15 -15
  275. data/demo/tools/Replay.bat +59 -59
  276. data/demo/tools/ScpiTarget +15 -15
  277. data/demo/tools/ScpiTarget.bat +59 -59
  278. data/demo/tools/ScriptRunner +15 -15
  279. data/demo/tools/ScriptRunner.bat +59 -59
  280. data/demo/tools/TableManager +15 -15
  281. data/demo/tools/TableManager.bat +59 -59
  282. data/demo/tools/TestRunner +15 -15
  283. data/demo/tools/TestRunner.bat +59 -59
  284. data/demo/tools/TlmExtractor +15 -15
  285. data/demo/tools/TlmExtractor.bat +59 -59
  286. data/demo/tools/TlmGrapher +15 -15
  287. data/demo/tools/TlmGrapher.bat +59 -59
  288. data/demo/tools/TlmViewer +15 -15
  289. data/demo/tools/TlmViewer.bat +59 -59
  290. data/demo/tools/mac/CmdExtractor.app/Contents/Info.plist +38 -38
  291. data/demo/tools/mac/CmdExtractor.app/Contents/MacOS/CmdExtractor.rb +15 -15
  292. data/demo/tools/mac/CmdExtractor.app/Contents/MacOS/main.sh +6 -6
  293. data/demo/tools/mac/CmdSender.app/Contents/Info.plist +38 -38
  294. data/demo/tools/mac/CmdSender.app/Contents/MacOS/CmdSender.rb +15 -15
  295. data/demo/tools/mac/CmdSender.app/Contents/MacOS/main.sh +6 -6
  296. data/demo/tools/mac/CmdTlmServer.app/Contents/Info.plist +38 -38
  297. data/demo/tools/mac/CmdTlmServer.app/Contents/MacOS/CmdTlmServer.rb +15 -15
  298. data/demo/tools/mac/CmdTlmServer.app/Contents/MacOS/main.sh +6 -6
  299. data/demo/tools/mac/DataViewer.app/Contents/Info.plist +38 -38
  300. data/demo/tools/mac/DataViewer.app/Contents/MacOS/DataViewer.rb +15 -15
  301. data/demo/tools/mac/DataViewer.app/Contents/MacOS/main.sh +6 -6
  302. data/demo/tools/mac/HandbookCreator.app/Contents/Info.plist +38 -38
  303. data/demo/tools/mac/HandbookCreator.app/Contents/MacOS/HandbookCreator.rb +15 -15
  304. data/demo/tools/mac/HandbookCreator.app/Contents/MacOS/main.sh +6 -6
  305. data/demo/tools/mac/Launcher.app/Contents/Info.plist +38 -38
  306. data/demo/tools/mac/Launcher.app/Contents/MacOS/Launcher.rb +15 -15
  307. data/demo/tools/mac/Launcher.app/Contents/MacOS/main.sh +6 -6
  308. data/demo/tools/mac/LimitsMonitor.app/Contents/Info.plist +38 -38
  309. data/demo/tools/mac/LimitsMonitor.app/Contents/MacOS/LimitsMonitor.rb +15 -15
  310. data/demo/tools/mac/LimitsMonitor.app/Contents/MacOS/main.sh +6 -6
  311. data/demo/tools/mac/OpenGLBuilder.app/Contents/Info.plist +38 -38
  312. data/demo/tools/mac/OpenGLBuilder.app/Contents/MacOS/OpenGLBuilder.rb +15 -15
  313. data/demo/tools/mac/OpenGLBuilder.app/Contents/MacOS/main.sh +6 -6
  314. data/demo/tools/mac/PacketViewer.app/Contents/Info.plist +38 -38
  315. data/demo/tools/mac/PacketViewer.app/Contents/MacOS/PacketViewer.rb +15 -15
  316. data/demo/tools/mac/PacketViewer.app/Contents/MacOS/main.sh +6 -6
  317. data/demo/tools/mac/Replay.app/Contents/Info.plist +38 -38
  318. data/demo/tools/mac/Replay.app/Contents/MacOS/Replay.rb +15 -15
  319. data/demo/tools/mac/Replay.app/Contents/MacOS/main.sh +6 -6
  320. data/demo/tools/mac/ScriptRunner.app/Contents/Info.plist +38 -38
  321. data/demo/tools/mac/ScriptRunner.app/Contents/MacOS/ScriptRunner.rb +15 -15
  322. data/demo/tools/mac/ScriptRunner.app/Contents/MacOS/main.sh +6 -6
  323. data/demo/tools/mac/TableManager.app/Contents/Info.plist +38 -38
  324. data/demo/tools/mac/TableManager.app/Contents/MacOS/TableManager.rb +15 -15
  325. data/demo/tools/mac/TableManager.app/Contents/MacOS/main.sh +6 -6
  326. data/demo/tools/mac/TestRunner.app/Contents/Info.plist +38 -38
  327. data/demo/tools/mac/TestRunner.app/Contents/MacOS/TestRunner.rb +15 -15
  328. data/demo/tools/mac/TestRunner.app/Contents/MacOS/main.sh +6 -6
  329. data/demo/tools/mac/TlmExtractor.app/Contents/Info.plist +38 -38
  330. data/demo/tools/mac/TlmExtractor.app/Contents/MacOS/TlmExtractor.rb +15 -15
  331. data/demo/tools/mac/TlmExtractor.app/Contents/MacOS/main.sh +6 -6
  332. data/demo/tools/mac/TlmGrapher.app/Contents/Info.plist +38 -38
  333. data/demo/tools/mac/TlmGrapher.app/Contents/MacOS/TlmGrapher.rb +15 -15
  334. data/demo/tools/mac/TlmGrapher.app/Contents/MacOS/main.sh +6 -6
  335. data/demo/tools/mac/TlmViewer.app/Contents/Info.plist +38 -38
  336. data/demo/tools/mac/TlmViewer.app/Contents/MacOS/TlmViewer.rb +15 -15
  337. data/demo/tools/mac/TlmViewer.app/Contents/MacOS/main.sh +6 -6
  338. data/ext/cosmos/ext/array/array.c +111 -111
  339. data/ext/cosmos/ext/array/extconf.rb +13 -13
  340. data/ext/cosmos/ext/buffered_file/buffered_file.c +167 -167
  341. data/ext/cosmos/ext/buffered_file/extconf.rb +13 -13
  342. data/ext/cosmos/ext/config_parser/config_parser.c +237 -237
  343. data/ext/cosmos/ext/config_parser/extconf.rb +13 -13
  344. data/ext/cosmos/ext/cosmos_io/cosmos_io.c +117 -117
  345. data/ext/cosmos/ext/cosmos_io/extconf.rb +13 -13
  346. data/ext/cosmos/ext/crc/crc.c +341 -341
  347. data/ext/cosmos/ext/crc/extconf.rb +12 -12
  348. data/ext/cosmos/ext/line_graph/extconf.rb +13 -13
  349. data/ext/cosmos/ext/line_graph/line_graph.c +501 -501
  350. data/ext/cosmos/ext/low_fragmentation_array/extconf.rb +12 -12
  351. data/ext/cosmos/ext/low_fragmentation_array/low_fragmentation_array.c +261 -261
  352. data/ext/cosmos/ext/packet/extconf.rb +13 -13
  353. data/ext/cosmos/ext/packet/packet.c +339 -339
  354. data/ext/cosmos/ext/platform/extconf.rb +13 -13
  355. data/ext/cosmos/ext/platform/platform.c +81 -81
  356. data/ext/cosmos/ext/polynomial_conversion/extconf.rb +13 -13
  357. data/ext/cosmos/ext/polynomial_conversion/polynomial_conversion.c +73 -73
  358. data/ext/cosmos/ext/string/extconf.rb +13 -13
  359. data/ext/cosmos/ext/string/string.c +49 -49
  360. data/ext/cosmos/ext/structure/structure.c +894 -894
  361. data/ext/cosmos/ext/tabbed_plots_config/extconf.rb +13 -13
  362. data/ext/cosmos/ext/tabbed_plots_config/tabbed_plots_config.c +51 -51
  363. data/ext/cosmos/ext/telemetry/extconf.rb +13 -13
  364. data/ext/cosmos/ext/telemetry/telemetry.c +306 -306
  365. data/ext/mkrf_conf.rb +40 -40
  366. data/install/Gemfile +6 -6
  367. data/install/Launcher +14 -14
  368. data/install/Launcher.bat +59 -59
  369. data/install/Rakefile +61 -61
  370. data/install/config/data/crc.txt +134 -133
  371. data/install/config/system/system.txt +29 -29
  372. data/install/config/targets/COSMOS/cmd_tlm/cosmos_server_cmds.txt +41 -41
  373. data/install/config/targets/COSMOS/cmd_tlm/cosmos_server_tlm.txt +15 -15
  374. data/install/config/targets/COSMOS/cmd_tlm_server.txt +6 -6
  375. data/install/config/targets/COSMOS/screens/limits_change.txt +20 -20
  376. data/install/config/targets/COSMOS/screens/version.txt +19 -19
  377. data/install/config/targets/COSMOS/target.txt +8 -8
  378. data/install/config/tools/cmd_tlm_server/cmd_tlm_server.txt +2 -2
  379. data/install/config/tools/data_viewer/data_viewer.txt +3 -3
  380. data/install/config/tools/handbook_creator/handbook_creator.txt +49 -49
  381. data/install/config/tools/handbook_creator/templates/command_packets.html.erb +86 -86
  382. data/install/config/tools/handbook_creator/templates/command_toc.html.erb +38 -38
  383. data/install/config/tools/handbook_creator/templates/footer.html.erb +9 -9
  384. data/install/config/tools/handbook_creator/templates/header.html.erb +25 -25
  385. data/install/config/tools/handbook_creator/templates/limits_groups.html.erb +13 -13
  386. data/install/config/tools/handbook_creator/templates/nav.html.erb +27 -27
  387. data/install/config/tools/handbook_creator/templates/overview.html.erb +1 -1
  388. data/install/config/tools/handbook_creator/templates/pdf_cover.html.erb +23 -23
  389. data/install/config/tools/handbook_creator/templates/pdf_footer.html.erb +33 -33
  390. data/install/config/tools/handbook_creator/templates/pdf_header.html.erb +41 -41
  391. data/install/config/tools/handbook_creator/templates/telemetry_packets.html.erb +80 -80
  392. data/install/config/tools/handbook_creator/templates/telemetry_toc.html.erb +38 -38
  393. data/install/config/tools/handbook_creator/templates/title.html.erb +1 -1
  394. data/install/config/tools/launcher/launcher.txt +39 -39
  395. data/install/config/tools/script_runner/script_runner.txt +3 -3
  396. data/install/config/tools/test_runner/test_runner.txt +8 -8
  397. data/install/config/tools/tlm_viewer/tlm_viewer.txt +5 -5
  398. data/install/lib/user_version.rb +3 -3
  399. data/install/tools/CmdExtractor +15 -15
  400. data/install/tools/CmdExtractor.bat +59 -59
  401. data/install/tools/CmdSender +15 -15
  402. data/install/tools/CmdSender.bat +59 -59
  403. data/install/tools/CmdTlmServer +15 -15
  404. data/install/tools/CmdTlmServer.bat +59 -59
  405. data/install/tools/DataViewer +15 -15
  406. data/install/tools/DataViewer.bat +59 -59
  407. data/install/tools/HandbookCreator +15 -15
  408. data/install/tools/HandbookCreator.bat +61 -61
  409. data/install/tools/Launcher +15 -15
  410. data/install/tools/Launcher.bat +59 -59
  411. data/install/tools/LimitsMonitor +15 -15
  412. data/install/tools/LimitsMonitor.bat +59 -59
  413. data/install/tools/OpenGLBuilder +15 -15
  414. data/install/tools/OpenGLBuilder.bat +59 -59
  415. data/install/tools/PacketViewer +15 -15
  416. data/install/tools/PacketViewer.bat +59 -59
  417. data/install/tools/Replay +15 -15
  418. data/install/tools/Replay.bat +59 -59
  419. data/install/tools/ScriptRunner +15 -15
  420. data/install/tools/ScriptRunner.bat +59 -59
  421. data/install/tools/TableManager +15 -15
  422. data/install/tools/TableManager.bat +59 -59
  423. data/install/tools/TestRunner +15 -15
  424. data/install/tools/TestRunner.bat +59 -59
  425. data/install/tools/TlmExtractor +15 -15
  426. data/install/tools/TlmExtractor.bat +59 -59
  427. data/install/tools/TlmGrapher +15 -15
  428. data/install/tools/TlmGrapher.bat +59 -59
  429. data/install/tools/TlmViewer +15 -15
  430. data/install/tools/TlmViewer.bat +59 -59
  431. data/install/tools/mac/CmdExtractor.app/Contents/Info.plist +38 -38
  432. data/install/tools/mac/CmdExtractor.app/Contents/MacOS/CmdExtractor.rb +15 -15
  433. data/install/tools/mac/CmdExtractor.app/Contents/MacOS/main.sh +6 -6
  434. data/install/tools/mac/CmdSender.app/Contents/Info.plist +38 -38
  435. data/install/tools/mac/CmdSender.app/Contents/MacOS/CmdSender.rb +15 -15
  436. data/install/tools/mac/CmdSender.app/Contents/MacOS/main.sh +6 -6
  437. data/install/tools/mac/CmdTlmServer.app/Contents/Info.plist +38 -38
  438. data/install/tools/mac/CmdTlmServer.app/Contents/MacOS/CmdTlmServer.rb +15 -15
  439. data/install/tools/mac/CmdTlmServer.app/Contents/MacOS/main.sh +6 -6
  440. data/install/tools/mac/DataViewer.app/Contents/Info.plist +38 -38
  441. data/install/tools/mac/DataViewer.app/Contents/MacOS/DataViewer.rb +15 -15
  442. data/install/tools/mac/DataViewer.app/Contents/MacOS/main.sh +6 -6
  443. data/install/tools/mac/HandbookCreator.app/Contents/Info.plist +38 -38
  444. data/install/tools/mac/HandbookCreator.app/Contents/MacOS/HandbookCreator.rb +15 -15
  445. data/install/tools/mac/HandbookCreator.app/Contents/MacOS/main.sh +6 -6
  446. data/install/tools/mac/Launcher.app/Contents/Info.plist +38 -38
  447. data/install/tools/mac/Launcher.app/Contents/MacOS/Launcher.rb +15 -15
  448. data/install/tools/mac/Launcher.app/Contents/MacOS/main.sh +6 -6
  449. data/install/tools/mac/LimitsMonitor.app/Contents/Info.plist +38 -38
  450. data/install/tools/mac/LimitsMonitor.app/Contents/MacOS/LimitsMonitor.rb +15 -15
  451. data/install/tools/mac/LimitsMonitor.app/Contents/MacOS/main.sh +6 -6
  452. data/install/tools/mac/OpenGLBuilder.app/Contents/Info.plist +38 -38
  453. data/install/tools/mac/OpenGLBuilder.app/Contents/MacOS/OpenGLBuilder.rb +15 -15
  454. data/install/tools/mac/OpenGLBuilder.app/Contents/MacOS/main.sh +6 -6
  455. data/install/tools/mac/PacketViewer.app/Contents/Info.plist +38 -38
  456. data/install/tools/mac/PacketViewer.app/Contents/MacOS/PacketViewer.rb +15 -15
  457. data/install/tools/mac/PacketViewer.app/Contents/MacOS/main.sh +6 -6
  458. data/install/tools/mac/Replay.app/Contents/Info.plist +38 -38
  459. data/install/tools/mac/Replay.app/Contents/MacOS/Replay.rb +15 -15
  460. data/install/tools/mac/Replay.app/Contents/MacOS/main.sh +6 -6
  461. data/install/tools/mac/ScriptRunner.app/Contents/Info.plist +38 -38
  462. data/install/tools/mac/ScriptRunner.app/Contents/MacOS/ScriptRunner.rb +15 -15
  463. data/install/tools/mac/ScriptRunner.app/Contents/MacOS/main.sh +6 -6
  464. data/install/tools/mac/TableManager.app/Contents/Info.plist +38 -38
  465. data/install/tools/mac/TableManager.app/Contents/MacOS/TableManager.rb +15 -15
  466. data/install/tools/mac/TableManager.app/Contents/MacOS/main.sh +6 -6
  467. data/install/tools/mac/TestRunner.app/Contents/Info.plist +38 -38
  468. data/install/tools/mac/TestRunner.app/Contents/MacOS/TestRunner.rb +15 -15
  469. data/install/tools/mac/TestRunner.app/Contents/MacOS/main.sh +6 -6
  470. data/install/tools/mac/TlmExtractor.app/Contents/Info.plist +38 -38
  471. data/install/tools/mac/TlmExtractor.app/Contents/MacOS/TlmExtractor.rb +15 -15
  472. data/install/tools/mac/TlmExtractor.app/Contents/MacOS/main.sh +6 -6
  473. data/install/tools/mac/TlmGrapher.app/Contents/Info.plist +38 -38
  474. data/install/tools/mac/TlmGrapher.app/Contents/MacOS/TlmGrapher.rb +15 -15
  475. data/install/tools/mac/TlmGrapher.app/Contents/MacOS/main.sh +6 -6
  476. data/install/tools/mac/TlmViewer.app/Contents/Info.plist +38 -38
  477. data/install/tools/mac/TlmViewer.app/Contents/MacOS/TlmViewer.rb +15 -15
  478. data/install/tools/mac/TlmViewer.app/Contents/MacOS/main.sh +6 -6
  479. data/lib/cosmos.rb +63 -63
  480. data/lib/cosmos/ccsds/ccsds_packet.rb +63 -63
  481. data/lib/cosmos/ccsds/ccsds_parser.rb +143 -143
  482. data/lib/cosmos/config/config_parser.rb +324 -324
  483. data/lib/cosmos/conversions.rb +13 -13
  484. data/lib/cosmos/conversions/conversion.rb +47 -47
  485. data/lib/cosmos/conversions/generic_conversion.rb +55 -55
  486. data/lib/cosmos/conversions/new_packet_log_conversion.rb +45 -45
  487. data/lib/cosmos/conversions/polynomial_conversion.rb +57 -57
  488. data/lib/cosmos/conversions/processor_conversion.rb +46 -46
  489. data/lib/cosmos/conversions/received_count_conversion.rb +33 -33
  490. data/lib/cosmos/conversions/received_time_formatted_conversion.rb +37 -37
  491. data/lib/cosmos/conversions/received_time_seconds_conversion.rb +37 -37
  492. data/lib/cosmos/conversions/segmented_polynomial_conversion.rb +128 -128
  493. data/lib/cosmos/conversions/unix_time_conversion.rb +50 -50
  494. data/lib/cosmos/conversions/unix_time_formatted_conversion.rb +44 -44
  495. data/lib/cosmos/conversions/unix_time_seconds_conversion.rb +44 -44
  496. data/lib/cosmos/core_ext.rb +18 -18
  497. data/lib/cosmos/core_ext/array.rb +354 -354
  498. data/lib/cosmos/core_ext/class.rb +51 -51
  499. data/lib/cosmos/core_ext/cosmos_io.rb +29 -29
  500. data/lib/cosmos/core_ext/exception.rb +52 -52
  501. data/lib/cosmos/core_ext/file.rb +75 -75
  502. data/lib/cosmos/core_ext/hash.rb +28 -28
  503. data/lib/cosmos/core_ext/io.rb +75 -75
  504. data/lib/cosmos/core_ext/kernel.rb +38 -38
  505. data/lib/cosmos/core_ext/math.rb +119 -119
  506. data/lib/cosmos/core_ext/matrix.rb +146 -146
  507. data/lib/cosmos/core_ext/objectspace.rb +29 -29
  508. data/lib/cosmos/core_ext/range.rb +22 -22
  509. data/lib/cosmos/core_ext/socket.rb +32 -32
  510. data/lib/cosmos/core_ext/string.rb +310 -310
  511. data/lib/cosmos/core_ext/stringio.rb +24 -24
  512. data/lib/cosmos/core_ext/time.rb +446 -446
  513. data/lib/cosmos/gui/choosers/combobox_chooser.rb +130 -130
  514. data/lib/cosmos/gui/choosers/file_chooser.rb +68 -68
  515. data/lib/cosmos/gui/choosers/float_chooser.rb +82 -82
  516. data/lib/cosmos/gui/choosers/integer_chooser.rb +80 -80
  517. data/lib/cosmos/gui/choosers/string_chooser.rb +53 -53
  518. data/lib/cosmos/gui/choosers/telemetry_chooser.rb +317 -317
  519. data/lib/cosmos/gui/dialogs/about_dialog.rb +128 -128
  520. data/lib/cosmos/gui/dialogs/calendar_dialog.rb +136 -136
  521. data/lib/cosmos/gui/dialogs/cmd_details_dialog.rb +52 -52
  522. data/lib/cosmos/gui/dialogs/cmd_tlm_raw_dialog.rb +149 -149
  523. data/lib/cosmos/gui/dialogs/details_dialog.rb +174 -174
  524. data/lib/cosmos/gui/dialogs/exception_dialog.rb +97 -97
  525. data/lib/cosmos/gui/dialogs/exception_list_dialog.rb +59 -59
  526. data/lib/cosmos/gui/dialogs/find_replace_dialog.rb +196 -196
  527. data/lib/cosmos/gui/dialogs/legal_dialog.rb +169 -168
  528. data/lib/cosmos/gui/dialogs/packet_log_dialog.rb +118 -118
  529. data/lib/cosmos/gui/dialogs/progress_dialog.rb +270 -262
  530. data/lib/cosmos/gui/dialogs/pry_dialog.rb +165 -161
  531. data/lib/cosmos/gui/dialogs/scroll_text_dialog.rb +37 -37
  532. data/lib/cosmos/gui/dialogs/select_dialog.rb +54 -54
  533. data/lib/cosmos/gui/dialogs/set_tlm_dialog.rb +131 -131
  534. data/lib/cosmos/gui/dialogs/splash.rb +113 -113
  535. data/lib/cosmos/gui/dialogs/tlm_details_dialog.rb +206 -206
  536. data/lib/cosmos/gui/dialogs/tlm_edit_dialog.rb +81 -81
  537. data/lib/cosmos/gui/line_graph/line_graph.rb +456 -456
  538. data/lib/cosmos/gui/line_graph/line_graph_dialog.rb +34 -34
  539. data/lib/cosmos/gui/line_graph/line_graph_drawing.rb +494 -494
  540. data/lib/cosmos/gui/line_graph/line_graph_popups.rb +116 -116
  541. data/lib/cosmos/gui/line_graph/line_graph_scaling.rb +460 -460
  542. data/lib/cosmos/gui/line_graph/line_graph_script.rb +26 -26
  543. data/lib/cosmos/gui/line_graph/lines.rb +290 -290
  544. data/lib/cosmos/gui/line_graph/overview_graph.rb +459 -459
  545. data/lib/cosmos/gui/opengl/earth_model.rb +22 -22
  546. data/lib/cosmos/gui/opengl/gl_bounds.rb +67 -67
  547. data/lib/cosmos/gui/opengl/gl_light.rb +39 -39
  548. data/lib/cosmos/gui/opengl/gl_material.rb +29 -29
  549. data/lib/cosmos/gui/opengl/gl_scene.rb +72 -72
  550. data/lib/cosmos/gui/opengl/gl_shape.rb +146 -146
  551. data/lib/cosmos/gui/opengl/gl_viewer.rb +724 -712
  552. data/lib/cosmos/gui/opengl/gl_viewport.rb +35 -35
  553. data/lib/cosmos/gui/opengl/moon_model.rb +22 -22
  554. data/lib/cosmos/gui/opengl/opengl.rb +8 -8
  555. data/lib/cosmos/gui/opengl/stl_reader.rb +211 -211
  556. data/lib/cosmos/gui/opengl/stl_shape.rb +124 -124
  557. data/lib/cosmos/gui/opengl/texture_mapped_sphere.rb +202 -202
  558. data/lib/cosmos/gui/qt.rb +813 -786
  559. data/lib/cosmos/gui/qt_tool.rb +378 -373
  560. data/lib/cosmos/gui/text/completion.rb +381 -381
  561. data/lib/cosmos/gui/text/completion_line_edit.rb +30 -30
  562. data/lib/cosmos/gui/text/completion_text_edit.rb +179 -179
  563. data/lib/cosmos/gui/text/ruby_editor.rb +395 -395
  564. data/lib/cosmos/gui/utilities/screenshot.rb +25 -25
  565. data/lib/cosmos/gui/utilities/script_module_gui.rb +203 -203
  566. data/lib/cosmos/gui/widgets/full_text_search_line_edit.rb +161 -161
  567. data/lib/cosmos/gui/widgets/packet_log_frame.rb +305 -305
  568. data/lib/cosmos/gui/widgets/realtime_button_bar.rb +98 -98
  569. data/lib/cosmos/interfaces.rb +11 -11
  570. data/lib/cosmos/interfaces/cmd_tlm_server_interface.rb +153 -149
  571. data/lib/cosmos/interfaces/interface.rb +213 -213
  572. data/lib/cosmos/interfaces/linc_interface.rb +360 -360
  573. data/lib/cosmos/interfaces/serial_interface.rb +76 -76
  574. data/lib/cosmos/interfaces/simulated_target_interface.rb +129 -128
  575. data/lib/cosmos/interfaces/stream_interface.rb +156 -156
  576. data/lib/cosmos/interfaces/tcpip_client_interface.rb +60 -60
  577. data/lib/cosmos/interfaces/tcpip_server_interface.rb +154 -154
  578. data/lib/cosmos/interfaces/udp_interface.rb +173 -173
  579. data/lib/cosmos/io/buffered_file.rb +11 -11
  580. data/lib/cosmos/io/cosmos_snmp.rb +50 -50
  581. data/lib/cosmos/io/io_multiplexer.rb +89 -89
  582. data/lib/cosmos/io/json_drb.rb +344 -320
  583. data/lib/cosmos/io/json_drb_object.rb +137 -137
  584. data/lib/cosmos/io/json_rpc.rb +365 -365
  585. data/lib/cosmos/io/posix_serial_driver.rb +145 -145
  586. data/lib/cosmos/io/raw_logger.rb +174 -174
  587. data/lib/cosmos/io/raw_logger_pair.rb +71 -71
  588. data/lib/cosmos/io/serial_driver.rb +85 -85
  589. data/lib/cosmos/io/stderr.rb +36 -36
  590. data/lib/cosmos/io/stdout.rb +36 -36
  591. data/lib/cosmos/io/tcpip_server.rb +583 -532
  592. data/lib/cosmos/io/udp_sockets.rb +152 -152
  593. data/lib/cosmos/io/win32_serial_driver.rb +147 -147
  594. data/lib/cosmos/packet_logs.rb +6 -6
  595. data/lib/cosmos/packet_logs/meta_packet_log_writer.rb +107 -107
  596. data/lib/cosmos/packet_logs/packet_log_reader.rb +441 -439
  597. data/lib/cosmos/packet_logs/packet_log_writer.rb +321 -309
  598. data/lib/cosmos/packet_logs/packet_log_writer_pair.rb +30 -30
  599. data/lib/cosmos/packets/binary_accessor.rb +921 -921
  600. data/lib/cosmos/packets/commands.rb +291 -291
  601. data/lib/cosmos/packets/limits.rb +263 -263
  602. data/lib/cosmos/packets/limits_response.rb +38 -38
  603. data/lib/cosmos/packets/packet.rb +714 -699
  604. data/lib/cosmos/packets/packet_config.rb +1034 -1034
  605. data/lib/cosmos/packets/packet_item.rb +317 -317
  606. data/lib/cosmos/packets/packet_item_limits.rb +128 -128
  607. data/lib/cosmos/packets/structure.rb +421 -386
  608. data/lib/cosmos/packets/structure_item.rb +233 -233
  609. data/lib/cosmos/packets/telemetry.rb +317 -317
  610. data/lib/cosmos/processors.rb +6 -6
  611. data/lib/cosmos/processors/new_packet_log_processor.rb +34 -34
  612. data/lib/cosmos/processors/processor.rb +71 -71
  613. data/lib/cosmos/processors/statistics_processor.rb +65 -65
  614. data/lib/cosmos/processors/watermark_processor.rb +44 -44
  615. data/lib/cosmos/script.rb +9 -9
  616. data/lib/cosmos/script/extract.rb +115 -115
  617. data/lib/cosmos/script/script.rb +1513 -1493
  618. data/lib/cosmos/streams/burst_stream_protocol.rb +25 -25
  619. data/lib/cosmos/streams/fixed_stream_protocol.rb +111 -111
  620. data/lib/cosmos/streams/length_stream_protocol.rb +140 -140
  621. data/lib/cosmos/streams/preidentified_stream_protocol.rb +118 -118
  622. data/lib/cosmos/streams/serial_stream.rb +152 -143
  623. data/lib/cosmos/streams/stream.rb +57 -57
  624. data/lib/cosmos/streams/stream_protocol.rb +369 -369
  625. data/lib/cosmos/streams/tcpip_client_stream.rb +77 -77
  626. data/lib/cosmos/streams/tcpip_socket_stream.rb +139 -139
  627. data/lib/cosmos/streams/template_stream_protocol.rb +140 -140
  628. data/lib/cosmos/streams/terminated_stream_protocol.rb +81 -81
  629. data/lib/cosmos/system.rb +4 -4
  630. data/lib/cosmos/system/system.rb +558 -558
  631. data/lib/cosmos/system/target.rb +178 -178
  632. data/lib/cosmos/tools/cmd_extractor/cmd_extractor.rb +254 -253
  633. data/lib/cosmos/tools/cmd_sender/cmd_sender.rb +716 -716
  634. data/lib/cosmos/tools/cmd_sender/cmd_sender_item_delegate.rb +77 -77
  635. data/lib/cosmos/tools/cmd_sender/cmd_sender_text_edit.rb +70 -70
  636. data/lib/cosmos/tools/cmd_tlm_server/api.rb +936 -940
  637. data/lib/cosmos/tools/cmd_tlm_server/background_task.rb +46 -46
  638. data/lib/cosmos/tools/cmd_tlm_server/background_tasks.rb +67 -63
  639. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server.rb +511 -497
  640. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_config.rb +241 -241
  641. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_gui.rb +1033 -1008
  642. data/lib/cosmos/tools/cmd_tlm_server/commanding.rb +112 -112
  643. data/lib/cosmos/tools/cmd_tlm_server/connections.rb +176 -176
  644. data/lib/cosmos/tools/cmd_tlm_server/interface_thread.rb +241 -221
  645. data/lib/cosmos/tools/cmd_tlm_server/interfaces.rb +127 -127
  646. data/lib/cosmos/tools/cmd_tlm_server/packet_logging.rb +132 -132
  647. data/lib/cosmos/tools/cmd_tlm_server/router_thread.rb +66 -66
  648. data/lib/cosmos/tools/cmd_tlm_server/routers.rb +97 -97
  649. data/lib/cosmos/tools/data_viewer/data_viewer.rb +628 -600
  650. data/lib/cosmos/tools/data_viewer/data_viewer_component.rb +167 -167
  651. data/lib/cosmos/tools/data_viewer/dump_component.rb +40 -40
  652. data/lib/cosmos/tools/handbook_creator/handbook_creator.rb +149 -149
  653. data/lib/cosmos/tools/handbook_creator/handbook_creator_config.rb +360 -360
  654. data/lib/cosmos/tools/launcher/launcher.rb +184 -184
  655. data/lib/cosmos/tools/launcher/launcher_config.rb +175 -167
  656. data/lib/cosmos/tools/launcher/launcher_multitool.rb +40 -41
  657. data/lib/cosmos/tools/launcher/launcher_tool.rb +104 -104
  658. data/lib/cosmos/tools/limits_monitor/limits_monitor.rb +796 -768
  659. data/lib/cosmos/tools/opengl_builder/opengl_builder.rb +416 -416
  660. data/lib/cosmos/tools/opengl_builder/scene_config.rb +118 -118
  661. data/lib/cosmos/tools/packet_viewer/packet_viewer.rb +525 -525
  662. data/lib/cosmos/tools/replay/replay.rb +495 -488
  663. data/lib/cosmos/tools/replay/replay_server.rb +91 -91
  664. data/lib/cosmos/tools/script_runner/script_audit.rb +147 -139
  665. data/lib/cosmos/tools/script_runner/script_runner.rb +914 -914
  666. data/lib/cosmos/tools/script_runner/script_runner_config.rb +40 -40
  667. data/lib/cosmos/tools/script_runner/script_runner_frame.rb +1892 -1859
  668. data/lib/cosmos/tools/table_manager/table.rb +70 -70
  669. data/lib/cosmos/tools/table_manager/table_config.rb +764 -764
  670. data/lib/cosmos/tools/table_manager/table_item.rb +74 -74
  671. data/lib/cosmos/tools/table_manager/table_manager.rb +1065 -1065
  672. data/lib/cosmos/tools/table_manager/table_manager_core.rb +539 -539
  673. data/lib/cosmos/tools/test_runner/results_writer.rb +283 -283
  674. data/lib/cosmos/tools/test_runner/test.rb +480 -480
  675. data/lib/cosmos/tools/test_runner/test_runner.rb +1157 -1157
  676. data/lib/cosmos/tools/test_runner/test_runner_chooser.rb +338 -338
  677. data/lib/cosmos/tools/tlm_extractor/text_item_chooser.rb +60 -60
  678. data/lib/cosmos/tools/tlm_extractor/tlm_extractor.rb +1008 -1008
  679. data/lib/cosmos/tools/tlm_extractor/tlm_extractor_config.rb +371 -371
  680. data/lib/cosmos/tools/tlm_extractor/tlm_extractor_processor.rb +60 -60
  681. data/lib/cosmos/tools/tlm_grapher/data_object_adders/housekeeping_data_object_adder.rb +75 -75
  682. data/lib/cosmos/tools/tlm_grapher/data_object_adders/singlexy_data_object_adder.rb +44 -44
  683. data/lib/cosmos/tools/tlm_grapher/data_object_adders/xy_data_object_adder.rb +94 -94
  684. data/lib/cosmos/tools/tlm_grapher/data_object_editors/data_object_editor.rb +61 -61
  685. data/lib/cosmos/tools/tlm_grapher/data_object_editors/housekeeping_data_object_editor.rb +180 -180
  686. data/lib/cosmos/tools/tlm_grapher/data_object_editors/linegraph_data_object_editor.rb +141 -141
  687. data/lib/cosmos/tools/tlm_grapher/data_object_editors/singlexy_data_object_editor.rb +30 -30
  688. data/lib/cosmos/tools/tlm_grapher/data_object_editors/xy_data_object_editor.rb +173 -173
  689. data/lib/cosmos/tools/tlm_grapher/data_objects/data_object.rb +177 -177
  690. data/lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb +412 -409
  691. data/lib/cosmos/tools/tlm_grapher/data_objects/linegraph_data_object.rb +176 -176
  692. data/lib/cosmos/tools/tlm_grapher/data_objects/singlexy_data_object.rb +25 -25
  693. data/lib/cosmos/tools/tlm_grapher/data_objects/xy_data_object.rb +323 -320
  694. data/lib/cosmos/tools/tlm_grapher/plot_editors/linegraph_plot_editor.rb +181 -181
  695. data/lib/cosmos/tools/tlm_grapher/plot_editors/plot_editor.rb +28 -28
  696. data/lib/cosmos/tools/tlm_grapher/plot_editors/singlexy_plot_editor.rb +30 -30
  697. data/lib/cosmos/tools/tlm_grapher/plot_editors/xy_plot_editor.rb +59 -59
  698. data/lib/cosmos/tools/tlm_grapher/plot_gui_objects/linegraph_plot_gui_object.rb +172 -172
  699. data/lib/cosmos/tools/tlm_grapher/plot_gui_objects/singlexy_plot_gui_object.rb +27 -27
  700. data/lib/cosmos/tools/tlm_grapher/plot_gui_objects/xy_plot_gui_object.rb +74 -74
  701. data/lib/cosmos/tools/tlm_grapher/plots/linegraph_plot.rb +201 -201
  702. data/lib/cosmos/tools/tlm_grapher/plots/plot.rb +69 -69
  703. data/lib/cosmos/tools/tlm_grapher/plots/singlexy_plot.rb +20 -20
  704. data/lib/cosmos/tools/tlm_grapher/plots/xy_plot.rb +61 -61
  705. data/lib/cosmos/tools/tlm_grapher/tabbed_plots/overview_tabbed_plots.rb +1278 -1278
  706. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_config.rb +430 -430
  707. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_data_object_editor.rb +107 -107
  708. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_logfile_thread.rb +111 -95
  709. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_plot_editor.rb +101 -101
  710. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_realtime_thread.rb +72 -66
  711. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_tab.rb +57 -57
  712. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_tool.rb +1004 -1004
  713. data/lib/cosmos/tools/tlm_grapher/tlm_grapher.rb +87 -87
  714. data/lib/cosmos/tools/tlm_viewer/screen.rb +486 -458
  715. data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +563 -544
  716. data/lib/cosmos/tools/tlm_viewer/tlm_viewer_config.rb +287 -287
  717. data/lib/cosmos/tools/tlm_viewer/widgets.rb +53 -53
  718. data/lib/cosmos/tools/tlm_viewer/widgets/aging_widget.rb +110 -110
  719. data/lib/cosmos/tools/tlm_viewer/widgets/array_widget.rb +70 -70
  720. data/lib/cosmos/tools/tlm_viewer/widgets/block_widget.rb +61 -61
  721. data/lib/cosmos/tools/tlm_viewer/widgets/button_widget.rb +39 -39
  722. data/lib/cosmos/tools/tlm_viewer/widgets/canvas_widget.rb +62 -62
  723. data/lib/cosmos/tools/tlm_viewer/widgets/canvasimage_widget.rb +41 -41
  724. data/lib/cosmos/tools/tlm_viewer/widgets/canvasimagevalue_widget.rb +57 -57
  725. data/lib/cosmos/tools/tlm_viewer/widgets/canvaslabel_widget.rb +37 -37
  726. data/lib/cosmos/tools/tlm_viewer/widgets/canvaslabelvalue_widget.rb +56 -56
  727. data/lib/cosmos/tools/tlm_viewer/widgets/canvasline_widget.rb +55 -55
  728. data/lib/cosmos/tools/tlm_viewer/widgets/canvaslinevalue_widget.rb +66 -66
  729. data/lib/cosmos/tools/tlm_viewer/widgets/canvasvalue_widget.rb +124 -124
  730. data/lib/cosmos/tools/tlm_viewer/widgets/checkbutton_widget.rb +31 -31
  731. data/lib/cosmos/tools/tlm_viewer/widgets/combobox_widget.rb +30 -30
  732. data/lib/cosmos/tools/tlm_viewer/widgets/formatfontvalue_widget.rb +36 -36
  733. data/lib/cosmos/tools/tlm_viewer/widgets/formatvalue_widget.rb +35 -35
  734. data/lib/cosmos/tools/tlm_viewer/widgets/horizontal_widget.rb +27 -27
  735. data/lib/cosmos/tools/tlm_viewer/widgets/horizontalbox_widget.rb +31 -31
  736. data/lib/cosmos/tools/tlm_viewer/widgets/horizontalline_widget.rb +26 -26
  737. data/lib/cosmos/tools/tlm_viewer/widgets/label_widget.rb +29 -29
  738. data/lib/cosmos/tools/tlm_viewer/widgets/labelformatvalue_widget.rb +39 -39
  739. data/lib/cosmos/tools/tlm_viewer/widgets/labelprogressbar_widget.rb +38 -38
  740. data/lib/cosmos/tools/tlm_viewer/widgets/labeltrendlimitsbar_widget.rb +38 -38
  741. data/lib/cosmos/tools/tlm_viewer/widgets/labelvalue_widget.rb +39 -39
  742. data/lib/cosmos/tools/tlm_viewer/widgets/labelvaluedesc_widget.rb +42 -42
  743. data/lib/cosmos/tools/tlm_viewer/widgets/labelvaluelimitsbar_widget.rb +37 -37
  744. data/lib/cosmos/tools/tlm_viewer/widgets/labelvaluerangebar_widget.rb +37 -37
  745. data/lib/cosmos/tools/tlm_viewer/widgets/layout_widget.rb +34 -34
  746. data/lib/cosmos/tools/tlm_viewer/widgets/limitsbar_widget.rb +178 -178
  747. data/lib/cosmos/tools/tlm_viewer/widgets/linegraph_widget.rb +54 -54
  748. data/lib/cosmos/tools/tlm_viewer/widgets/matrixbycolumns_widget.rb +47 -47
  749. data/lib/cosmos/tools/tlm_viewer/widgets/multi_widget.rb +116 -116
  750. data/lib/cosmos/tools/tlm_viewer/widgets/progressbar_widget.rb +34 -34
  751. data/lib/cosmos/tools/tlm_viewer/widgets/radiobutton_widget.rb +30 -30
  752. data/lib/cosmos/tools/tlm_viewer/widgets/rangebar_widget.rb +57 -57
  753. data/lib/cosmos/tools/tlm_viewer/widgets/screenshotbutton_widget.rb +34 -34
  754. data/lib/cosmos/tools/tlm_viewer/widgets/scrollwindow_widget.rb +35 -35
  755. data/lib/cosmos/tools/tlm_viewer/widgets/sectionheader_widget.rb +33 -33
  756. data/lib/cosmos/tools/tlm_viewer/widgets/tabbook_widget.rb +26 -26
  757. data/lib/cosmos/tools/tlm_viewer/widgets/tabitem_widget.rb +28 -28
  758. data/lib/cosmos/tools/tlm_viewer/widgets/textbox_widget.rb +47 -47
  759. data/lib/cosmos/tools/tlm_viewer/widgets/textfield_widget.rb +26 -26
  760. data/lib/cosmos/tools/tlm_viewer/widgets/timegraph_widget.rb +88 -88
  761. data/lib/cosmos/tools/tlm_viewer/widgets/title_widget.rb +27 -27
  762. data/lib/cosmos/tools/tlm_viewer/widgets/trendbar_widget.rb +130 -130
  763. data/lib/cosmos/tools/tlm_viewer/widgets/trendlimitsbar_widget.rb +46 -46
  764. data/lib/cosmos/tools/tlm_viewer/widgets/value_widget.rb +43 -43
  765. data/lib/cosmos/tools/tlm_viewer/widgets/valuelimitsbar_widget.rb +37 -37
  766. data/lib/cosmos/tools/tlm_viewer/widgets/valuerangebar_widget.rb +37 -37
  767. data/lib/cosmos/tools/tlm_viewer/widgets/vertical_widget.rb +35 -35
  768. data/lib/cosmos/tools/tlm_viewer/widgets/verticalbox_widget.rb +37 -37
  769. data/lib/cosmos/tools/tlm_viewer/widgets/widget.rb +257 -257
  770. data/lib/cosmos/top_level.rb +647 -596
  771. data/lib/cosmos/utilities.rb +11 -10
  772. data/lib/cosmos/utilities/crc.rb +166 -166
  773. data/lib/cosmos/utilities/csv.rb +83 -83
  774. data/lib/cosmos/utilities/logger.rb +137 -137
  775. data/lib/cosmos/utilities/low_fragmentation_array.rb +11 -11
  776. data/lib/cosmos/utilities/message_log.rb +74 -74
  777. data/lib/cosmos/utilities/quaternion.rb +258 -258
  778. data/lib/cosmos/utilities/ruby_lex_utils.rb +313 -313
  779. data/lib/cosmos/utilities/simulated_target.rb +99 -99
  780. data/lib/cosmos/utilities/sleeper.rb +44 -0
  781. data/lib/cosmos/version.rb +12 -12
  782. data/lib/cosmos/win32/excel.rb +66 -66
  783. data/lib/cosmos/win32/win32.rb +387 -387
  784. data/lib/cosmos/win32/win32_main.rb +311 -311
  785. data/roodi.yml +24 -24
  786. data/run_gui_tests.bat +32 -32
  787. data/spec/ccsds/ccsds_packet_spec.rb +67 -67
  788. data/spec/ccsds/ccsds_parser_spec.rb +148 -148
  789. data/spec/config/config_parser_spec.rb +322 -322
  790. data/spec/conversions/conversion_spec.rb +31 -31
  791. data/spec/conversions/generic_conversion_spec.rb +45 -45
  792. data/spec/conversions/new_packet_log_conversion_spec.rb +39 -39
  793. data/spec/conversions/polynomial_conversion_spec.rb +40 -40
  794. data/spec/conversions/processor_conversion_spec.rb +45 -45
  795. data/spec/conversions/received_count_conversion_spec.rb +43 -43
  796. data/spec/conversions/received_time_formatted_conversion_spec.rb +49 -49
  797. data/spec/conversions/received_time_seconds_conversion_spec.rb +50 -50
  798. data/spec/conversions/segmented_polynomial_conversion_spec.rb +51 -51
  799. data/spec/conversions/unix_time_formatted_conversion_spec.rb +74 -74
  800. data/spec/conversions/unix_time_seconds_conversion_spec.rb +76 -76
  801. data/spec/core_ext/array_spec.rb +186 -186
  802. data/spec/core_ext/class_spec.rb +36 -36
  803. data/spec/core_ext/cosmos_io_spec.rb +77 -77
  804. data/spec/core_ext/exception_spec.rb +91 -91
  805. data/spec/core_ext/file_spec.rb +72 -72
  806. data/spec/core_ext/hash_spec.rb +24 -24
  807. data/spec/core_ext/io_spec.rb +46 -46
  808. data/spec/core_ext/kernel_spec.rb +54 -54
  809. data/spec/core_ext/math_spec.rb +116 -116
  810. data/spec/core_ext/matrix_spec.rb +66 -66
  811. data/spec/core_ext/objectspace_spec.rb +29 -29
  812. data/spec/core_ext/range_spec.rb +21 -21
  813. data/spec/core_ext/socket_spec.rb +32 -32
  814. data/spec/core_ext/string_spec.rb +223 -223
  815. data/spec/core_ext/stringio_spec.rb +21 -21
  816. data/spec/core_ext/time_spec.rb +151 -151
  817. data/spec/gui/line_graph/line_clip_spec.rb +322 -322
  818. data/spec/install/config/system/system.txt +33 -33
  819. data/spec/install/config/targets/COSMOS/cmd_tlm/cosmos_server_cmds.txt +41 -41
  820. data/spec/install/config/targets/COSMOS/cmd_tlm/cosmos_server_tlm.txt +15 -15
  821. data/spec/install/config/targets/COSMOS/cmd_tlm_server.txt +6 -6
  822. data/spec/install/config/targets/COSMOS/screens/limits_change.txt +20 -20
  823. data/spec/install/config/targets/COSMOS/screens/version.txt +19 -19
  824. data/spec/install/config/targets/COSMOS/target.txt +5 -5
  825. data/spec/install/config/targets/INST/cmd_tlm/inst_cmd_linc.txt +30 -30
  826. data/spec/install/config/targets/INST/cmd_tlm/inst_cmds.txt +111 -111
  827. data/spec/install/config/targets/INST/cmd_tlm/inst_tlm.txt +236 -236
  828. data/spec/install/config/targets/INST/cmd_tlm/inst_tlm_linc.txt +25 -25
  829. data/spec/install/config/targets/INST/cmd_tlm_server.txt +5 -5
  830. data/spec/install/config/targets/INST/lib/sim_inst.rb +305 -294
  831. data/spec/install/config/targets/INST/target.txt +10 -10
  832. data/spec/install/config/targets/META/cmd_tlm/meta_cmd.txt +4 -4
  833. data/spec/install/config/targets/META/cmd_tlm/meta_tlm.txt +4 -4
  834. data/spec/install/config/targets/SYSTEM/cmd_tlm/limits_groups.txt +7 -7
  835. data/spec/interfaces/cmd_tlm_server_interface_spec.rb +150 -150
  836. data/spec/interfaces/interface_spec.rb +130 -131
  837. data/spec/interfaces/linc_interface_spec.rb +199 -199
  838. data/spec/interfaces/serial_interface_spec.rb +56 -56
  839. data/spec/interfaces/simulated_target_interface_spec.rb +128 -128
  840. data/spec/interfaces/stream_interface_spec.rb +157 -157
  841. data/spec/interfaces/tcpip_client_interface_spec.rb +54 -54
  842. data/spec/interfaces/tcpip_server_interface_spec.rb +151 -151
  843. data/spec/interfaces/udp_interface_spec.rb +175 -177
  844. data/spec/io/buffered_file_spec.rb +113 -113
  845. data/spec/io/io_multiplexer_spec.rb +94 -94
  846. data/spec/io/json_drb_object_spec.rb +99 -99
  847. data/spec/io/json_drb_spec.rb +311 -311
  848. data/spec/io/json_rpc_spec.rb +264 -264
  849. data/spec/io/raw_logger_pair_spec.rb +76 -76
  850. data/spec/io/raw_logger_spec.rb +133 -133
  851. data/spec/io/serial_driver_spec.rb +61 -61
  852. data/spec/io/stderr_spec.rb +32 -32
  853. data/spec/io/stdout_spec.rb +32 -32
  854. data/spec/io/tcpip_server_spec.rb +338 -338
  855. data/spec/io/udp_sockets_spec.rb +94 -94
  856. data/spec/io/win32_serial_driver_spec.rb +88 -88
  857. data/spec/packet_logs/meta_packet_log_writer_spec.rb +170 -170
  858. data/spec/packet_logs/packet_log_reader_spec.rb +408 -408
  859. data/spec/packet_logs/packet_log_writer_pair_spec.rb +30 -30
  860. data/spec/packet_logs/packet_log_writer_spec.rb +223 -223
  861. data/spec/packets/binary_accessor_spec.rb +2073 -2073
  862. data/spec/packets/commands_spec.rb +369 -369
  863. data/spec/packets/limits_response_spec.rb +25 -25
  864. data/spec/packets/limits_spec.rb +326 -326
  865. data/spec/packets/packet_config_spec.rb +1620 -1620
  866. data/spec/packets/packet_item_limits_spec.rb +161 -161
  867. data/spec/packets/packet_item_spec.rb +386 -386
  868. data/spec/packets/packet_spec.rb +1057 -949
  869. data/spec/packets/structure_item_spec.rb +195 -195
  870. data/spec/packets/structure_spec.rb +419 -419
  871. data/spec/packets/telemetry_spec.rb +535 -535
  872. data/spec/processors/new_packet_log_processor_spec.rb +39 -39
  873. data/spec/processors/processor_spec.rb +55 -55
  874. data/spec/processors/statistics_processor_spec.rb +60 -60
  875. data/spec/processors/watermark_processor_spec.rb +51 -51
  876. data/spec/script/script_spec.rb +654 -654
  877. data/spec/spec_helper.rb +154 -148
  878. data/spec/streams/burst_stream_protocol_spec.rb +32 -32
  879. data/spec/streams/fixed_stream_protocol_spec.rb +110 -110
  880. data/spec/streams/length_stream_protocol_spec.rb +297 -297
  881. data/spec/streams/preidentified_stream_protocol_spec.rb +118 -118
  882. data/spec/streams/serial_stream_spec.rb +105 -105
  883. data/spec/streams/stream_protocol_spec.rb +332 -332
  884. data/spec/streams/stream_spec.rb +29 -29
  885. data/spec/streams/tcpip_client_stream_spec.rb +54 -54
  886. data/spec/streams/tcpip_socket_stream_spec.rb +146 -146
  887. data/spec/streams/template_stream_protocol_spec.rb +151 -151
  888. data/spec/streams/terminated_stream_protocol_spec.rb +123 -123
  889. data/spec/system/system_spec.rb +645 -645
  890. data/spec/system/target_spec.rb +248 -248
  891. data/spec/tools/cmd_tlm_server/api_spec.rb +1087 -1113
  892. data/spec/tools/cmd_tlm_server/background_task_spec.rb +32 -32
  893. data/spec/tools/cmd_tlm_server/background_tasks_spec.rb +81 -81
  894. data/spec/tools/cmd_tlm_server/cmd_tlm_server_config_spec.rb +411 -411
  895. data/spec/tools/cmd_tlm_server/cmd_tlm_server_spec.rb +415 -415
  896. data/spec/tools/cmd_tlm_server/commanding_spec.rb +123 -123
  897. data/spec/tools/cmd_tlm_server/connections_spec.rb +147 -147
  898. data/spec/tools/cmd_tlm_server/interface_thread_spec.rb +306 -306
  899. data/spec/tools/cmd_tlm_server/interfaces_spec.rb +252 -238
  900. data/spec/tools/cmd_tlm_server/packet_logging_spec.rb +143 -143
  901. data/spec/tools/cmd_tlm_server/router_thread_spec.rb +98 -101
  902. data/spec/tools/cmd_tlm_server/routers_spec.rb +223 -208
  903. data/spec/top_level/top_level_spec.rb +334 -321
  904. data/spec/utilities/crc_spec.rb +45 -45
  905. data/spec/utilities/csv_spec.rb +97 -97
  906. data/spec/utilities/logger_spec.rb +102 -102
  907. data/spec/utilities/message_log_spec.rb +89 -89
  908. data/spec/utilities/quaternion_spec.rb +107 -107
  909. data/spec/utilities/ruby_lex_utils_spec.rb +86 -86
  910. data/tasks/manifest.rake +22 -22
  911. data/tasks/spec.rake +23 -23
  912. metadata +18 -2
@@ -1,40 +1,40 @@
1
- # encoding: ascii-8bit
2
-
3
- # Copyright 2014 Ball Aerospace & Technologies Corp.
4
- # All Rights Reserved.
5
- #
6
- # This program is free software; you can modify and/or redistribute it
7
- # under the terms of the GNU General Public License
8
- # as published by the Free Software Foundation; version 3 with
9
- # attribution addendums as found in the LICENSE.txt
10
-
11
- require 'cosmos'
12
-
13
- module Cosmos
14
-
15
- # This class reads the Script Runner configuration file
16
- class ScriptRunnerConfig
17
- # Processes the config file
18
- def initialize(filename)
19
- parser = ConfigParser.new
20
- parser.parse_file(filename) do |keyword, params|
21
- case keyword
22
- when 'LINE_DELAY'
23
- parser.verify_num_parameters(1, 1, "#{keyword} <Delay in Seconds>")
24
- ScriptRunnerFrame.line_delay = params[0].to_f
25
- when 'MONITOR_LIMITS'
26
- parser.verify_num_parameters(0, 0, keyword)
27
- ScriptRunnerFrame.monitor_limits = true
28
- when 'PAUSE_ON_RED'
29
- parser.verify_num_parameters(0, 0, keyword)
30
- ScriptRunnerFrame.monitor_limits = true
31
- ScriptRunnerFrame.pause_on_red = true
32
- else
33
- # blank config.lines will have a nil keyword and should not raise an exception
34
- raise parser.error("Unknown keyword '#{keyword}'") if keyword
35
- end
36
- end
37
- end
38
- end # class ScriptRunnerConfig
39
-
40
- end # module Cosmos
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2014 Ball Aerospace & Technologies Corp.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is free software; you can modify and/or redistribute it
7
+ # under the terms of the GNU General Public License
8
+ # as published by the Free Software Foundation; version 3 with
9
+ # attribution addendums as found in the LICENSE.txt
10
+
11
+ require 'cosmos'
12
+
13
+ module Cosmos
14
+
15
+ # This class reads the Script Runner configuration file
16
+ class ScriptRunnerConfig
17
+ # Processes the config file
18
+ def initialize(filename)
19
+ parser = ConfigParser.new
20
+ parser.parse_file(filename) do |keyword, params|
21
+ case keyword
22
+ when 'LINE_DELAY'
23
+ parser.verify_num_parameters(1, 1, "#{keyword} <Delay in Seconds>")
24
+ ScriptRunnerFrame.line_delay = params[0].to_f
25
+ when 'MONITOR_LIMITS'
26
+ parser.verify_num_parameters(0, 0, keyword)
27
+ ScriptRunnerFrame.monitor_limits = true
28
+ when 'PAUSE_ON_RED'
29
+ parser.verify_num_parameters(0, 0, keyword)
30
+ ScriptRunnerFrame.monitor_limits = true
31
+ ScriptRunnerFrame.pause_on_red = true
32
+ else
33
+ # blank config.lines will have a nil keyword and should not raise an exception
34
+ raise parser.error("Unknown keyword '#{keyword}'") if keyword
35
+ end
36
+ end
37
+ end
38
+ end # class ScriptRunnerConfig
39
+
40
+ end # module Cosmos
@@ -1,1859 +1,1892 @@
1
- # encoding: ascii-8bit
2
-
3
- # Copyright 2014 Ball Aerospace & Technologies Corp.
4
- # All Rights Reserved.
5
- #
6
- # This program is free software; you can modify and/or redistribute it
7
- # under the terms of the GNU General Public License
8
- # as published by the Free Software Foundation; version 3 with
9
- # attribution addendums as found in the LICENSE.txt
10
-
11
- require 'cosmos'
12
- require 'cosmos/script'
13
- require 'cosmos/gui/utilities/script_module_gui'
14
- require 'cosmos/gui/dialogs/splash'
15
- require 'cosmos/gui/dialogs/exception_dialog'
16
- require 'cosmos/gui/text/completion'
17
- require 'cosmos/gui/text/completion_line_edit'
18
- require 'cosmos/gui/text/ruby_editor'
19
- require 'cosmos/gui/dialogs/progress_dialog'
20
- require 'cosmos/gui/widgets/realtime_button_bar'
21
- require 'cosmos/gui/dialogs/find_replace_dialog'
22
- require 'cosmos/gui/choosers/file_chooser'
23
- require 'cosmos/io/stdout'
24
- require 'cosmos/io/stderr'
25
-
26
- module Cosmos
27
-
28
- class ScriptRunnerDialog < Qt::Dialog
29
- attr_reader :script_runner_frame
30
-
31
- # Constructor
32
- def initialize(parent,
33
- title,
34
- default_tab_text = 'Untitled')
35
- # Call base class constructor
36
- super(parent)
37
- setWindowTitle(title)
38
- setMinimumWidth(parent.width * 0.8)
39
- setMinimumHeight(parent.height * 0.6)
40
-
41
- # Create script runner frame
42
- @script_runner_frame = ScriptRunnerFrame.new(self, default_tab_text)
43
- layout = Qt::VBoxLayout.new
44
- layout.addWidget(@script_runner_frame)
45
- setLayout(layout)
46
- end # def initialize
47
-
48
- # Executes the given text and closes when complete
49
- def execute_text_and_close_on_complete(text, text_binding = nil)
50
- @script_runner_frame.set_text(text)
51
- @script_runner_frame.run_and_close_on_complete(text_binding)
52
- exec
53
- dispose
54
- end
55
-
56
- def reject
57
- # Don't allow the dialog to close if we're running
58
- return if @script_runner_frame.running?
59
- super
60
- end
61
- end # class ScriptRunnerDialog
62
-
63
- class ScriptRunnerFrame < Qt::Widget
64
- slots 'context_menu(const QPoint&)'
65
- slots 'undo_available(bool)'
66
- slots 'breakpoint_set(int)'
67
- slots 'breakpoint_cleared(int)'
68
- slots 'breakpoints_cleared()'
69
- signals 'undoAvailable(bool)'
70
- signals 'modificationChanged(bool)'
71
-
72
- CMD_KEYWORDS = %w(cmd cmd_no_range_check cmd_no_hazardous_check cmd_no_checks)
73
- TLM_KEYWORDS = %w(tlm tlm_raw tlm_formatted tlm_with_units limits_enabled? \
74
- enable_limits disable_limits wait_tolerance wait_tolerance_raw \
75
- check_tolerance check_tolerance_raw wait_check_tolerance \
76
- wait_check_tolerance_raw)
77
- SET_TLM_KEYWORDS = %w(set_tlm set_tlm_raw)
78
- CHECK_KEYWORDS = %w(check check_raw wait wait_raw wait_check wait_check_raw)
79
-
80
- attr_accessor :use_instrumentation
81
- attr_accessor :change_callback
82
- attr_accessor :run_callback
83
- attr_accessor :stop_callback
84
- attr_accessor :error_callback
85
- attr_accessor :pause_callback
86
- attr_reader :filename
87
- attr_accessor :continue_after_error
88
- attr_accessor :exceptions
89
- attr_accessor :script_binding
90
- attr_accessor :inline_return
91
- attr_accessor :inline_return_params
92
- attr_reader :message_log
93
- attr_reader :script_class
94
- attr_reader :top_level_instrumented_cache
95
-
96
- @@instance = nil
97
- @@run_thread = nil
98
- @@breakpoints = {}
99
- @@step_mode = false
100
- @@line_delay = 0.1
101
- @@instrumented_cache = {}
102
- @@file_cache = {}
103
- @@output_thread = nil
104
- @@limits_monitor_thread = nil
105
- @@pause_on_error = true
106
- @@monitor_limits = false
107
- @@pause_on_red = false
108
- @@show_backtrace = false
109
- @@error = nil
110
-
111
- def initialize(parent, default_tab_text = 'Untitled')
112
- super(parent)
113
- @default_tab_text = ' ' + default_tab_text + ' '
114
- # Keep track of whether this frame has been fully initialized
115
- @initialized = false
116
- @debug_frame = nil
117
-
118
- @layout = Qt::VBoxLayout.new
119
- @layout.setContentsMargins(0,0,0,0)
120
-
121
- # Add Realtime Button Bar
122
- @realtime_button_bar = RealtimeButtonBar.new(self)
123
- @realtime_button_bar.state = 'Stopped'
124
- @realtime_button_bar.start_callback = method(:handle_start_go_button)
125
- @realtime_button_bar.pause_callback = method(:handle_pause_retry_button)
126
- @realtime_button_bar.stop_callback = method(:handle_stop_button)
127
- @layout.addWidget(@realtime_button_bar)
128
-
129
- # Create a splitter to hold the script text area and the script output text area.
130
- @splitter = Qt::Splitter.new(Qt::Vertical, self)
131
- @layout.addWidget(@splitter)
132
- @top_widget = Qt::Widget.new(@splitter)
133
- @top_widget.setContentsMargins(0,0,0,0)
134
- @top_frame = Qt::VBoxLayout.new(@top_widget)
135
- @top_frame.setContentsMargins(0,0,0,0)
136
-
137
- # Add Initial Text Window
138
- @script = create_ruby_editor()
139
- @script.connect(SIGNAL('modificationChanged(bool)')) do |changed|
140
- emit modificationChanged(changed)
141
- end
142
- @top_frame.addWidget(@script)
143
-
144
- # Set self as the gui window to allow prompts and other popups to appear
145
- set_cmd_tlm_gui_window(self)
146
-
147
- # Add change handlers
148
- connect(@script,
149
- SIGNAL('undoAvailable(bool)'),
150
- self,
151
- SLOT('undo_available(bool)'))
152
-
153
- # Add Output Text
154
- @bottom_frame = Qt::Widget.new
155
- @bottom_layout = Qt::VBoxLayout.new
156
- @bottom_layout.setContentsMargins(0,0,0,0)
157
- @bottom_layout_label = Qt::Label.new("Script Output:")
158
- @bottom_layout.addWidget(@bottom_layout_label)
159
- @output = Qt::PlainTextEdit.new
160
- @output.setReadOnly(true)
161
- @output.setMaximumBlockCount(10000) # 10000 lines of history will be displayed
162
- @bottom_layout.addWidget(@output)
163
- @bottom_frame.setLayout(@bottom_layout)
164
- @splitter.addWidget(@bottom_frame)
165
- @splitter.setStretchFactor(0,10)
166
- @splitter.setStretchFactor(1,1)
167
-
168
- setLayout(@layout)
169
-
170
- # Configure Variables
171
- @line_offset = 0
172
- @filename = ''
173
- @output_io = StringIO.new('', 'r+')
174
- @output_io_mutex = Mutex.new
175
- @change_callback = nil
176
- @run_callback = nil
177
- @stop_callback = nil
178
- @error_callback = nil
179
- @pause_callback = nil
180
- @key_press_callback = nil
181
- @allow_start = true
182
- @continue_after_error = true
183
- @debug_text = nil
184
- @debug_history = []
185
- @debug_code_completion = nil
186
- @top_level_instrumented_cache = nil
187
- @output_time = Time.now
188
- initialize_variables()
189
-
190
- # Redirect $stdout and $stderr
191
- redirect_io()
192
-
193
- # Create Tabbook
194
- @tab_book = Qt::TabWidget.new
195
- @tab_book_shown = false
196
-
197
- @find_dialog = nil
198
- @replace_dialog = nil
199
-
200
- mark_breakpoints('')
201
- end
202
-
203
- def current_tab_filename
204
- if @tab_book_shown
205
- filename = @tab_book.tabText(@tab_book.currentIndex)
206
- if filename == @default_tab_text
207
- return ''
208
- else
209
- return filename.strip
210
- end
211
- else
212
- return @filename
213
- end
214
- end
215
-
216
- def create_ruby_editor
217
- # Add Initial Text Window
218
- script = RubyEditor.new(self)
219
- script.enable_breakpoints = true if @debug_frame
220
- connect(script,
221
- SIGNAL('breakpoint_set(int)'),
222
- self,
223
- SLOT('breakpoint_set(int)'))
224
- connect(script,
225
- SIGNAL('breakpoint_cleared(int)'),
226
- self,
227
- SLOT('breakpoint_cleared(int)'))
228
- connect(script,
229
- SIGNAL('breakpoints_cleared()'),
230
- self,
231
- SLOT('breakpoints_cleared()'))
232
-
233
- # Add right click menu
234
- script.setContextMenuPolicy(Qt::CustomContextMenu)
235
- connect(script,
236
- SIGNAL('customContextMenuRequested(const QPoint&)'),
237
- self,
238
- SLOT('context_menu(const QPoint&)'))
239
-
240
- return script
241
- end
242
-
243
- def stop_message_log
244
- @message_log.stop if @message_log
245
- @message_log = nil
246
- end
247
-
248
- def filename=(filename)
249
- # Stop the message log so a new one will be created with the new filename
250
- stop_message_log()
251
- @filename = filename
252
- mark_breakpoints(filename)
253
- end
254
-
255
- def modified
256
- @script.document.isModified()
257
- end
258
-
259
- def modified=(bool)
260
- @script.document.setModified(bool)
261
- end
262
-
263
- def undo_available(bool)
264
- emit undoAvailable(bool)
265
- end
266
-
267
- def key_press_callback=(callback)
268
- @script.keyPressCallback = callback
269
- end
270
-
271
- def setFocus
272
- @script.setFocus
273
- end
274
-
275
- def allow_start=(value)
276
- @allow_start = value
277
- if @allow_start
278
- @realtime_button_bar.start_button.setEnabled(true)
279
- elsif not running?
280
- @realtime_button_bar.start_button.setEnabled(false)
281
- @script.setReadOnly(true)
282
- end
283
- end
284
-
285
- def clear
286
- self.set_text('')
287
- self.filename = ''
288
- self.modified = false
289
- end
290
-
291
- def self.instance
292
- @@instance
293
- end
294
-
295
- def self.instance=(value)
296
- @@instance = value
297
- end
298
-
299
- def self.step_mode
300
- @@step_mode
301
- end
302
-
303
- def self.step_mode=(value)
304
- @@step_mode = value
305
- if self.instance
306
- if value
307
- self.instance.pause
308
- else
309
- self.instance.go
310
- end
311
- end
312
- end
313
-
314
- def self.line_delay
315
- @@line_delay
316
- end
317
-
318
- def self.line_delay=(value)
319
- @@line_delay = value
320
- end
321
-
322
- def self.instrumented_cache
323
- @@instrumented_cache
324
- end
325
-
326
- def self.instrumented_cache=(value)
327
- @@instrumented_cache = value
328
- end
329
-
330
- def self.pause_on_error
331
- @@pause_on_error
332
- end
333
-
334
- def self.pause_on_error=(value)
335
- @@pause_on_error = value
336
- end
337
-
338
- def self.monitor_limits
339
- @@monitor_limits
340
- end
341
-
342
- def self.monitor_limits=(value)
343
- @@monitor_limits = value
344
- end
345
-
346
- def self.pause_on_red
347
- @@pause_on_red
348
- end
349
-
350
- def self.pause_on_red=(value)
351
- @@pause_on_red = value
352
- end
353
-
354
- def self.show_backtrace
355
- @@show_backtrace
356
- end
357
-
358
- def self.show_backtrace=(value)
359
- @@show_backtrace = value
360
- if @@show_backtrace and @@error
361
- puts Time.now.formatted + " (SCRIPTRUNNER): " + "Most recent exception:\n" + @@error.formatted
362
- end
363
- end
364
-
365
- def text
366
- @script.toPlainText.gsub("\r", '')
367
- end
368
-
369
- def set_text(text, filename = '')
370
- unless running?()
371
- @script.setPlainText(text)
372
- @script.stop_highlight
373
- @filename = filename
374
- mark_breakpoints(filename)
375
- end
376
- end
377
-
378
- def set_text_from_file(filename)
379
- unless running?()
380
- @@file_cache[filename] = nil
381
- load_file_into_script(filename)
382
- @filename = filename
383
- end
384
- end
385
-
386
- def self.running?
387
- if @@run_thread then true else false end
388
- end
389
-
390
- def running?
391
- if @@instance == self and ScriptRunnerFrame.running?() then true else false end
392
- end
393
-
394
- def go
395
- @go = true
396
- @pause = false unless @@step_mode
397
- end
398
-
399
- def go?
400
- temp = @go
401
- @go = false
402
- temp
403
- end
404
-
405
- def pause
406
- @pause = true
407
- @go = false
408
- end
409
-
410
- def pause?
411
- @pause
412
- end
413
-
414
- def self.stop!
415
- if @@run_thread
416
- @@run_thread.kill
417
- @@run_thread = nil
418
- end
419
- end
420
-
421
- def stop?
422
- @stop
423
- end
424
-
425
- def retry_needed?
426
- @retry_needed
427
- end
428
-
429
- def retry_needed
430
- @retry_needed = true
431
- end
432
-
433
- def disable_retry
434
- @realtime_button_bar.start_button.setText('Skip')
435
- @realtime_button_bar.pause_button.setDisabled(true)
436
- end
437
-
438
- def enable_retry
439
- @realtime_button_bar.start_button.setText('Go')
440
- @realtime_button_bar.pause_button.setDisabled(false)
441
- end
442
-
443
- def run
444
- unless self.class.running?()
445
- run_text(@script.toPlainText)
446
- end
447
- end
448
-
449
- def run_and_close_on_complete(text_binding = nil)
450
- run_text(@script.toPlainText, 0, text_binding, true)
451
- end
452
-
453
- def self.instrument_script(text, filename, mark_private = false)
454
- if filename and !filename.empty?
455
- @@file_cache[filename] = text.clone
456
- end
457
-
458
- ruby_lex_utils = RubyLexUtils.new
459
- instrumented_text = ''
460
- # Add an extra newline because the Ruby 1.9.1 lexer RubyLex has a bug
461
- # where it loops unless the file ends in a newline. If there is already
462
- # a newline then so what, we add an extra.
463
- text += "\n"
464
-
465
- Qt.execute_in_main_thread(true) do
466
- window = Qt::CoreApplication.instance.activeWindow
467
- ProgressDialog.execute(window, # parent
468
- "Instrumenting: #{File.basename(filename.to_s)}",
469
- 500, # width
470
- 100, # height
471
- true, # show overall progress
472
- false, # don't show step progress
473
- false, # don't show text
474
- false, # don't show done
475
- true) do |progress_dialog| # show cancel
476
- progress_dialog.enable_cancel_button
477
- comments_removed_text = ruby_lex_utils.remove_comments(text)
478
- num_lines = comments_removed_text.num_lines.to_f
479
- num_lines = 1 if num_lines < 1
480
- instrumented_text =
481
- instrument_script_implementation(ruby_lex_utils,
482
- comments_removed_text,
483
- num_lines,
484
- progress_dialog,
485
- filename,
486
- mark_private)
487
- progress_dialog.close_done
488
- end
489
- end
490
-
491
- Kernel.raise StopScript if ProgressDialog.canceled?
492
- instrumented_text
493
- end
494
-
495
- def self.instrument_script_implementation(ruby_lex_utils,
496
- comments_removed_text,
497
- num_lines,
498
- progress_dialog,
499
- filename,
500
- mark_private = false)
501
- if mark_private
502
- instrumented_text = 'private; '
503
- else
504
- instrumented_text = ''
505
- end
506
-
507
- ruby_lex_utils.each_lexed_segment(comments_removed_text) do |segment, instrumentable, inside_begin, line_no|
508
- instrumented_line = ''
509
- if instrumentable
510
- # If not inside a begin block then create one to catch exceptions
511
- unless inside_begin
512
- instrumented_line << 'begin; '
513
- end
514
-
515
- # Add preline instrumentation
516
- instrumented_line << "ScriptRunnerFrame.instance.script_binding = binding(); if ScriptRunnerFrame.instance.inline_return then ScriptRunnerFrame.instance.inline_return = nil; return ScriptRunnerFrame.instance.inline_return_params; end; ScriptRunnerFrame.instance.pre_line_instrumentation('#{filename}', #{line_no}); "
517
-
518
- # Add the actual line
519
- instrumented_line << segment
520
- instrumented_line.chomp!
521
-
522
- # Add postline instrumentation
523
- instrumented_line << "; ScriptRunnerFrame.instance.post_line_instrumentation('#{filename}', #{line_no})"
524
-
525
- # Complete begin block to catch exceptions
526
- unless inside_begin
527
- instrumented_line << "; rescue Exception => eval_error; retry if ScriptRunnerFrame.instance.exception_instrumentation(eval_error, '#{filename}', #{line_no}); end"
528
- end
529
-
530
- instrumented_line << "\n"
531
- else
532
- unless segment =~ /^\s*end\s*$/ or segment =~ /^\s*when .*$/
533
- num_left_brackets = segment.count('{')
534
- num_right_brackets = segment.count('}')
535
- num_left_square_brackets = segment.count('[')
536
- num_right_square_brackets = segment.count(']')
537
-
538
- if (num_right_brackets > num_left_brackets) ||
539
- (num_right_square_brackets > num_left_square_brackets)
540
- instrumented_line = segment
541
- else
542
- instrumented_line = "ScriptRunnerFrame.instance.pre_line_instrumentation('#{filename}', #{line_no}); " + segment
543
- end
544
- else
545
- instrumented_line = segment
546
- end
547
- end
548
-
549
- instrumented_text << instrumented_line
550
-
551
- progress_dialog.set_overall_progress(line_no / num_lines) if progress_dialog and line_no
552
- end
553
- instrumented_text
554
- end
555
-
556
- def pre_line_instrumentation(filename, line_number)
557
- @current_filename = filename
558
- @current_line_number = line_number
559
- if @use_instrumentation
560
- # Clear go
561
- @go = false
562
-
563
- # Handle stopping mid-script if necessary
564
- Kernel.raise StopScript if @stop
565
-
566
- # Handle needing to change tabs
567
- handle_potential_tab_change(filename)
568
-
569
- # Adjust line number for offset in main script
570
- line_number = line_number + @line_offset if @active_script.object_id == @script.object_id
571
- detail_string = nil
572
- if filename
573
- detail_string = File.basename(filename) << ':' << line_number.to_s
574
- end
575
- Logger.detail_string = detail_string
576
-
577
- # Highlight the line that is about to run
578
- Qt.execute_in_main_thread(true) {@active_script.highlight_line(line_number)}
579
-
580
- # Handle pausing the script
581
- handle_pause(filename, line_number)
582
-
583
- # Handle delay between lines
584
- handle_line_delay()
585
- end
586
- end
587
-
588
- def post_line_instrumentation(filename, line_number)
589
- if @use_instrumentation
590
- line_number = line_number + @line_offset if @active_script.object_id == @script.object_id
591
- handle_output_io(filename, line_number)
592
- end
593
- end
594
-
595
- def exception_instrumentation(error, filename, line_number)
596
- if error.class == StopScript || error.class == SkipTestCase || !@use_instrumentation
597
- Kernel.raise error
598
- else
599
- line_number = line_number + @line_offset if @active_script.object_id == @script.object_id
600
- handle_exception(error, false, filename, line_number)
601
- end
602
- end
603
-
604
- def perform_pause
605
- mark_paused()
606
- wait_for_go_or_stop()
607
- end
608
-
609
- def perform_breakpoint(filename, line_number)
610
- mark_breakpoint()
611
- scriptrunner_puts "Hit Breakpoint at #{filename}:#{line_number}"
612
- handle_output_io(filename, line_number)
613
- wait_for_go_or_stop()
614
- end
615
-
616
- # Prompts the user that a script is running before they close the app
617
- def prompt_if_running_on_close
618
- safe_to_continue = true
619
- if running?
620
- case Qt::MessageBox.warning(
621
- self, # parent
622
- 'Warning', # title
623
- 'A Script is Running! Close Anyways?', # text
624
- Qt::MessageBox::Yes | Qt::MessageBox::No, # buttons
625
- Qt::MessageBox::No) # default button
626
- when Qt::MessageBox::Yes
627
- safe_to_continue = true
628
- ScriptRunnerFrame.stop!
629
- else
630
- safe_to_continue = false
631
- end
632
- end
633
- return safe_to_continue
634
- end
635
-
636
- ######################################
637
- # Implement the breakpoint callbacks from the RubyEditor
638
- ######################################
639
- def breakpoint_set(line)
640
- ScriptRunnerFrame.breakpoint(current_tab_filename(), line)
641
- end
642
-
643
- def breakpoint_cleared(line)
644
- ScriptRunnerFrame.clear_breakpoint(current_tab_filename(), line)
645
- end
646
-
647
- def breakpoints_cleared
648
- ScriptRunnerFrame.clear_breakpoints(current_tab_filename())
649
- end
650
-
651
- ######################################
652
- # Implement edit functionality in the frame (cut, copy, paste, etc)
653
- ######################################
654
- def undo
655
- @script.undo unless running?()
656
- end
657
-
658
- def redo
659
- @script.redo unless running?()
660
- end
661
-
662
- def cut
663
- @script.cut unless running?()
664
- end
665
-
666
- def copy
667
- @script.copy unless running?()
668
- end
669
-
670
- def paste
671
- @script.paste unless running?()
672
- end
673
-
674
- def select_all
675
- @script.select_all unless running?()
676
- end
677
-
678
- def comment_or_uncomment_lines
679
- @script.comment_or_uncomment_lines unless running?()
680
- end
681
-
682
- ##################################################################
683
- # Implement Search functionality in the frame (find, replace, etc)
684
- ##################################################################
685
- def find
686
- unless @find_dialog
687
- @find_dialog = FindReplaceDialog.new(@script)
688
- @find_dialog.connect(SIGNAL('find_next()')) { dialog_find(@find_dialog) }
689
- end
690
- @find_dialog.show
691
- @find_dialog.raise
692
- @find_dialog.activateWindow
693
- end
694
-
695
- def find_next
696
- flags = FindReplaceDialog.find_flags
697
- flags &= ~Qt::TextDocument::FindBackward.to_i
698
- found = @script.find(FindReplaceDialog.find_text, flags)
699
- if not found and FindReplaceDialog.wrap_around?
700
- cursor = @script.textCursor
701
- cursor.movePosition(Qt::TextCursor::Start)
702
- @script.setTextCursor(cursor)
703
- @script.find(FindReplaceDialog.find_text, flags)
704
- end
705
- end
706
-
707
- def find_previous
708
- flags = FindReplaceDialog.find_flags
709
- flags |= Qt::TextDocument::FindBackward.to_i
710
- found = @script.find(FindReplaceDialog.find_text, flags)
711
- if not found and FindReplaceDialog.wrap_around?
712
- cursor = @script.textCursor
713
- cursor.movePosition(Qt::TextCursor::End)
714
- @script.setTextCursor(cursor)
715
- @script.find(FindReplaceDialog.find_text, flags)
716
- end
717
- end
718
-
719
- def replace
720
- unless @replace_dialog
721
- @replace_dialog = FindReplaceDialog.new(@script, true)
722
- @replace_dialog.connect(SIGNAL('find_next()')) { dialog_find(@replace_dialog) }
723
- @replace_dialog.connect(SIGNAL('replace()')) { dialog_replace(@replace_dialog) }
724
- @replace_dialog.connect(SIGNAL('replace_all()')) { dialog_replace_all(@replace_dialog) }
725
- end
726
- @replace_dialog.show
727
- @replace_dialog.raise
728
- @replace_dialog.activateWindow
729
- end
730
-
731
- def dialog_find(dialog)
732
- found = @script.find(dialog.find_text, dialog.find_flags)
733
- if not found and dialog.wrap_around?
734
- cursor = @script.textCursor
735
- if dialog.find_up?
736
- cursor.movePosition(Qt::TextCursor::End)
737
- else
738
- cursor.movePosition(Qt::TextCursor::Start)
739
- end
740
- @script.setTextCursor(cursor)
741
- @script.find(dialog.find_text, dialog.find_flags)
742
- end
743
- end
744
-
745
- def dialog_replace(dialog)
746
- if @script.textCursor.hasSelection &&
747
- @script.textCursor.selectedText == dialog.find_text
748
- found = true
749
- else
750
- found = @script.find(dialog.find_text, dialog.find_flags)
751
- if not found and dialog.wrap_around?
752
- cursor = @script.textCursor
753
- if dialog.find_up?
754
- cursor.movePosition(Qt::TextCursor::End)
755
- else
756
- cursor.movePosition(Qt::TextCursor::Start)
757
- end
758
- @script.setTextCursor(cursor)
759
- found = @script.find(dialog.find_text, dialog.find_flags)
760
- end
761
- end
762
- if found
763
- @script.textCursor.removeSelectedText
764
- @script.textCursor.insertText(dialog.replace_text)
765
- cursor = @script.textCursor
766
- cursor.setPosition(cursor.position-1)
767
- cursor.select(Qt::TextCursor::WordUnderCursor)
768
- @script.setTextCursor(cursor)
769
- end
770
- end
771
-
772
- def dialog_replace_all(dialog)
773
- cursor = @script.textCursor
774
- cursor.movePosition(Qt::TextCursor::Start)
775
- @script.setTextCursor(cursor)
776
-
777
- while (@script.find(dialog.find_text, dialog.find_flags) == true)
778
- @script.textCursor.removeSelectedText
779
- @script.textCursor.insertText(dialog.replace_text)
780
- end
781
- end
782
-
783
- ##################################################################################
784
- # Implement Script functionality in the frame (run selection, run from cursor, etc
785
- ##################################################################################
786
- def run_selection
787
- unless self.class.running?()
788
- selection = @script.selected_lines
789
- if selection
790
- start_line_number = @script.selection_start_line
791
- end_line_number = @script.selection_end_line
792
- scriptrunner_puts "Running script lines #{start_line_number+1}-#{end_line_number+1}: #{File.basename(@filename)}"
793
- handle_output_io()
794
- run_text(selection, start_line_number)
795
- end
796
- end
797
- end
798
-
799
- def run_selection_while_paused
800
- current_script = @tab_book.tab(@tab_book.currentIndex)
801
- selection = current_script.selected_lines
802
- if selection
803
- start_line_number = current_script.selection_start_line
804
- end_line_number = current_script.selection_end_line
805
- scriptrunner_puts "Debug: Running selected lines #{start_line_number+1}-#{end_line_number+1}: #{@tab_book.tabText(@tab_book.currentIndex)}"
806
- handle_output_io()
807
- dialog = ScriptRunnerDialog.new(self, 'Executing Selected Lines While Paused')
808
- dialog.execute_text_and_close_on_complete(selection, @script_binding)
809
- handle_output_io()
810
- end
811
- end
812
-
813
- def run_from_cursor
814
- unless self.class.running?()
815
- line_number = @script.selection_start_line
816
- text = @script.toPlainText.split("\n")[line_number..-1].join("\n")
817
- scriptrunner_puts "Running script from line #{line_number}: #{File.basename(@filename)}"
818
- handle_output_io()
819
- run_text(text, line_number)
820
- end
821
- end
822
-
823
- def ruby_syntax_check_selection
824
- unless self.class.running?()
825
- selection = @script.selected_lines
826
- ruby_syntax_check_text(selection) if selection
827
- end
828
- end
829
-
830
- def ruby_syntax_check_text(text)
831
- unless self.class.running?()
832
- check_process = IO.popen("ruby -c -rubygems 2>&1", 'r+')
833
- check_process.write("require 'cosmos'; require 'cosmos/script'; " + text)
834
- check_process.close_write
835
- results = check_process.gets
836
- check_process.close
837
- if results
838
- if results =~ /Syntax OK/
839
- Qt::MessageBox.information(self, 'Syntax Check Successful', results)
840
- else
841
- # Results is a string like this: ":2: syntax error ..."
842
- # Normally the procedure comes before the first colon but since we
843
- # are writing to the process this is blank so we throw it away
844
- _, line_no, error = results.split(':')
845
- Qt::MessageBox.warning(self,
846
- 'Syntax Check Failed',
847
- "Error on line #{line_no}: #{error.strip}")
848
- end
849
- else
850
- Qt::MessageBox.critical(self,
851
- 'Syntax Check Exception',
852
- 'Ruby syntax check unexpectedly returned nil')
853
- end
854
- end
855
- end
856
-
857
- def mnemonic_check_selection
858
- unless self.class.running?()
859
- selection = @script.selected_lines
860
- mnemonic_check_text(selection, @script.selection_start_line+1) if selection
861
- end
862
- end
863
-
864
- def mnemonic_check_text(text, start_line_number = 1)
865
- results = []
866
- line_number = start_line_number
867
- text.each_line do |line|
868
- if line =~ /\(/
869
- result = nil
870
- keyword = line.split('(')[0].split[-1]
871
- if CMD_KEYWORDS.include? keyword
872
- result = mnemonic_check_cmd_line(keyword, line_number, line)
873
- elsif TLM_KEYWORDS.include? keyword
874
- result = mnemonic_check_tlm_line(keyword, line_number, line)
875
- elsif SET_TLM_KEYWORDS.include? keyword
876
- result = mnemonic_check_set_tlm_line(keyword, line_number, line)
877
- elsif CHECK_KEYWORDS.include? keyword
878
- result = mnemonic_check_check_line(keyword, line_number, line)
879
- end
880
- results << result if result
881
- end
882
- line_number += 1
883
- end
884
-
885
- if results.empty?
886
- Qt::MessageBox.information(self,
887
- 'Mnemonic Check Successful',
888
- 'Mnemonic Check Found No Errors')
889
- else
890
- dialog = Qt::Dialog.new(self) do |box|
891
- box.setWindowTitle('Mnemonic Check Failed')
892
- text = Qt::PlainTextEdit.new
893
- text.setReadOnly(true)
894
- text.setPlainText(results.join("\n"))
895
- frame = Qt::VBoxLayout.new(box)
896
- ok = Qt::PushButton.new('Ok')
897
- ok.setDefault(true)
898
- ok.connect(SIGNAL('clicked(bool)')) { box.accept }
899
- frame.addWidget(text)
900
- frame.addWidget(ok)
901
- end
902
- dialog.exec
903
- dialog.dispose
904
- end
905
- end
906
-
907
- ######################################################
908
- # Implement the debug capability
909
- ######################################################
910
- def toggle_debug(debug = nil)
911
- if debug.nil?
912
- if @debug_frame
913
- hide_debug()
914
- else
915
- show_debug()
916
- end
917
- else
918
- if debug
919
- if !@debug_frame
920
- show_debug()
921
- end
922
- else
923
- if @debug_frame
924
- hide_debug()
925
- end
926
- end
927
- end
928
- end
929
-
930
- def show_debug
931
- unless @debug_frame
932
- @script.enable_breakpoints = true
933
- if @tab_book_shown
934
- if @tab_book.count > 0
935
- (0..(@tab_book.count - 1)).each do |index|
936
- @tab_book.tab(index).enable_breakpoints = true
937
- end
938
- end
939
- end
940
-
941
- @debug_frame = Qt::HBoxLayout.new
942
- @debug_frame.setContentsMargins(0,0,0,0)
943
- @debug_frame_label = Qt::Label.new("Debug:")
944
- @debug_frame.addWidget(@debug_frame_label)
945
- @debug_text = CompletionLineEdit.new(self)
946
- @debug_text.setFocus(Qt::OtherFocusReason)
947
- @debug_text.keyPressCallback = lambda { |event|
948
- case event.key
949
- when Qt::Key_Return, Qt::Key_Enter
950
- begin
951
- debug_text = @debug_text.toPlainText
952
- @debug_history.unshift(debug_text)
953
- @debug_history_index = 0
954
- @debug_text.setPlainText('')
955
- scriptrunner_puts "Debug: #{debug_text}"
956
- handle_output_io()
957
- if not running?
958
- # Capture STDOUT and STDERR
959
- $stdout.add_stream(@output_io)
960
- $stderr.add_stream(@output_io)
961
- end
962
-
963
- if @script_binding
964
- eval(debug_text, @script_binding, 'debug', 1)
965
- else
966
- Object.class_eval(debug_text, 'debug', 1)
967
- end
968
- handle_output_io()
969
- rescue Exception => error
970
- if error.class == DRb::DRbConnError
971
- Logger.error("Error Connecting to Command and Telemetry Server")
972
- else
973
- Logger.error(error.class.to_s.split('::')[-1] + ' : ' + error.message)
974
- end
975
- handle_output_io()
976
- ensure
977
- if not running?
978
- # Capture STDOUT and STDERR
979
- $stdout.remove_stream(@output_io)
980
- $stderr.remove_stream(@output_io)
981
- end
982
- end
983
- when Qt::Key_Up
984
- if @debug_history.length > 0
985
- @debug_text.setPlainText(@debug_history[@debug_history_index])
986
- @debug_history_index += 1
987
- if @debug_history_index == @debug_history.length
988
- @debug_history_index = @debug_history.length-1
989
- end
990
- end
991
- when Qt::Key_Down
992
- if @debug_history.length > 0
993
- @debug_text.setPlainText(@debug_history[@debug_history_index])
994
- @debug_history_index -= 1
995
- @debug_history_index = 0 if @debug_history_index < 0
996
- end
997
- when Qt::Key_Escape
998
- @debug_text.setPlainText("")
999
- end
1000
- }
1001
-
1002
- @debug_frame.addWidget(@debug_text)
1003
-
1004
- @toggle_button = Qt::PushButton.new('Toggle Run/Step')
1005
- @debug_frame.addWidget(@toggle_button)
1006
- @toggle_button.connect(SIGNAL('clicked(bool)')) do
1007
- if @@step_mode
1008
- scriptrunner_puts "Debug: run_mode"
1009
- handle_output_io()
1010
- self.class.step_mode = false
1011
- else
1012
- scriptrunner_puts "Debug: step_mode"
1013
- handle_output_io()
1014
- self.class.step_mode = true
1015
- end
1016
- end
1017
-
1018
- @return_button = Qt::PushButton.new('Insert Return')
1019
- @debug_frame.addWidget(@return_button)
1020
- @return_button.connect(SIGNAL('clicked(bool)')) do
1021
- scriptrunner_puts "Debug: insert_return(); ScriptRunnerFrame.instance.go()"
1022
- handle_output_io()
1023
- insert_return()
1024
- go()
1025
- end
1026
-
1027
- @bottom_frame.layout.addLayout(@debug_frame)
1028
- end
1029
- end
1030
-
1031
- def hide_debug
1032
- # Since we're disabling debug, clear the breakpoints and disable them
1033
- ScriptRunnerFrame.clear_breakpoints()
1034
- @script.clear_breakpoints
1035
- @script.enable_breakpoints = false
1036
- if @tab_book_shown
1037
- if @tab_book.count > 0
1038
- (0..(@tab_book.count - 1)).each do |index|
1039
- @tab_book.tab(index).enable_breakpoints = false
1040
- end
1041
- end
1042
- end
1043
- # Remove the debug frame
1044
- @bottom_frame.layout.takeAt(@bottom_frame.layout.count - 1) if @debug_frame
1045
- @debug_frame.removeAll
1046
- @debug_frame.dispose
1047
- @debug_frame = nil
1048
- end
1049
-
1050
- def self.breakpoint(filename, line_number)
1051
- filename = File.basename(filename)
1052
- @@breakpoints[filename] ||= {}
1053
- @@breakpoints[filename][line_number] = true
1054
- end
1055
-
1056
- def self.clear_breakpoint(filename, line_number)
1057
- filename = File.basename(filename)
1058
- @@breakpoints[filename] ||= {}
1059
- @@breakpoints[filename].delete(line_number) if @@breakpoints[filename][line_number]
1060
- end
1061
-
1062
- def self.clear_breakpoints(filename = nil)
1063
- filename = File.basename(filename) unless filename.nil?
1064
- if filename == nil or filename.empty?
1065
- @@breakpoints = {}
1066
- else
1067
- @@breakpoints[filename] = {}
1068
- end
1069
- end
1070
-
1071
- def select_tab_and_destroy_tabs_after_index(index)
1072
- Qt.execute_in_main_thread(true) do
1073
- if @tab_book_shown
1074
- @tab_book.setCurrentIndex(index)
1075
- @active_script = @tab_book.tab(@tab_book.currentIndex)
1076
-
1077
- first_to_remove = index + 1
1078
- last_to_remove = @call_stack.length - 1
1079
-
1080
- last_to_remove.downto(first_to_remove) do |tab_index|
1081
- tab = @tab_book.tab(tab_index)
1082
- @tab_book.removeTab(tab_index)
1083
- tab.dispose
1084
- end
1085
-
1086
- @call_stack = @call_stack[0..index]
1087
- @current_file = @call_stack[index]
1088
- end
1089
- end
1090
- end
1091
-
1092
- def toggle_disconnect(config_file)
1093
- if get_cmd_tlm_disconnect
1094
- set_cmd_tlm_disconnect(false)
1095
- self.parent.setPalette(Cosmos::DEFAULT_PALETTE)
1096
- else
1097
- dialog = Qt::Dialog.new(self, Qt::WindowTitleHint | Qt::WindowSystemMenuHint)
1098
- dialog.setWindowTitle(tr("Server Config File"))
1099
- dialog_layout = Qt::VBoxLayout.new
1100
-
1101
- chooser = FileChooser.new(self, "Config File", config_file, 'Select',
1102
- File.join('config', 'tools', 'cmd_tlm_server', config_file))
1103
- chooser.callback = lambda do |filename|
1104
- chooser.filename = File.basename(filename)
1105
- end
1106
- dialog_layout.addWidget(chooser)
1107
-
1108
- button_layout = Qt::HBoxLayout.new
1109
- ok = Qt::PushButton.new("Ok")
1110
- ok.setDefault(true)
1111
- ok.connect(SIGNAL('clicked()')) do
1112
- dialog.accept()
1113
- end
1114
- button_layout.addWidget(ok)
1115
- cancel = Qt::PushButton.new("Cancel")
1116
- cancel.connect(SIGNAL('clicked()')) do
1117
- dialog.reject()
1118
- end
1119
- button_layout.addWidget(cancel)
1120
- dialog_layout.addLayout(button_layout)
1121
-
1122
- dialog.setLayout(dialog_layout)
1123
- if dialog.exec == Qt::Dialog::Accepted
1124
- config_file = chooser.filename
1125
- self.parent.setPalette(Cosmos::RED_PALETTE)
1126
- Splash.execute(self) do |splash|
1127
- ConfigParser.splash = splash
1128
- splash.message = "Initializing Command and Telemetry Server"
1129
- set_cmd_tlm_disconnect(true, config_file)
1130
- ConfigParser.splash = nil
1131
- end
1132
- end
1133
- dialog.dispose
1134
- end
1135
- config_file
1136
- end
1137
-
1138
- def current_backtrace
1139
- trace = []
1140
- Qt.execute_in_main_thread(true) do
1141
- if @@run_thread
1142
- temp_trace = @@run_thread.backtrace
1143
- cosmos_lib = Regexp.new(File.join(Cosmos::PATH, 'lib'))
1144
- temp_trace.each do |line|
1145
- next if line =~ cosmos_lib
1146
- trace << line
1147
- end
1148
- end
1149
- end
1150
- trace
1151
- end
1152
-
1153
- def scriptrunner_puts(string)
1154
- puts Time.now.formatted + " (SCRIPTRUNNER): " + string
1155
- end
1156
-
1157
- def handle_output_io(filename = @current_filename, line_number = @current_line_number)
1158
- @output_time = Time.now
1159
- Qt.execute_in_main_thread(true) do
1160
- if @output_io.string[-1..-1] == "\n"
1161
- time_formatted = Time.now.formatted
1162
- lines_to_write = ''
1163
- out_line_number = line_number.to_s
1164
- out_filename = File.basename(filename) if filename
1165
-
1166
- # Build each line to write
1167
- string = @output_io.string.clone
1168
- @output_io.string = @output_io.string[string.length..-1]
1169
- line_count = 0
1170
- string.each_line do |out_line|
1171
- color = nil
1172
- if out_line[0..1] == '20' and out_line[10] == ' ' and out_line[23..24] == ' ('
1173
- line_to_write = out_line
1174
- else
1175
- if filename
1176
- line_to_write = time_formatted + " (#{out_filename}:#{out_line_number}): " + out_line
1177
- else
1178
- line_to_write = time_formatted + " (SCRIPTRUNNER): " + out_line
1179
- color = Cosmos::BLUE
1180
- end
1181
- end
1182
- @output.add_formatted_text(line_to_write, color)
1183
- lines_to_write += line_to_write
1184
-
1185
- line_count += 1
1186
- if line_count > 1000
1187
- out_line = "ERROR: Too much written to stdout. Truncating output to 1000 lines.\n"
1188
- if filename
1189
- line_to_write = time_formatted + " (#{out_filename}:#{out_line_number}): " + out_line
1190
- else
1191
- line_to_write = time_formatted + " (SCRIPTRUNNER): " + out_line
1192
- end
1193
- @output.addText(line_to_write, Cosmos::RED)
1194
- lines_to_write += line_to_write
1195
- break
1196
- end
1197
- end # string.each_line
1198
-
1199
- # Actually add to the GUI
1200
- @output.flush
1201
-
1202
- # Add to the message log
1203
- if @filename.empty?
1204
- @message_log ||= MessageLog.new("sr_untitled")
1205
- else
1206
- @message_log ||= MessageLog.new("sr_#{File.basename(@filename).split('.')[0]}")
1207
- end
1208
- @message_log.write(lines_to_write)
1209
- end
1210
- end
1211
- end
1212
-
1213
- protected
1214
-
1215
- def initialize_variables
1216
- @@file_cache = {}
1217
- @@error = nil
1218
- @go = false
1219
- if @@step_mode
1220
- @pause = true
1221
- else
1222
- @pause = false
1223
- end
1224
- @stop = false
1225
- @retry_needed = false
1226
- @use_instrumentation = true
1227
- @active_script = @script
1228
- @call_stack = []
1229
- @pre_line_time = Time.now
1230
- @current_file = @filename
1231
- @exceptions = nil
1232
- @script_binding = nil
1233
- @inline_eval = nil
1234
- @current_filename = nil
1235
- @current_line_number = 0
1236
-
1237
- @script.stop_highlight
1238
- @call_stack.push(@current_file.dup)
1239
- end
1240
-
1241
- def wait_for_go_or_stop(error = nil)
1242
- @go = false
1243
- sleep(0.01) until @go or @stop
1244
- @go = false
1245
- mark_running()
1246
- Kernel.raise StopScript if @stop
1247
- Kernel.raise error if error and !@continue_after_error
1248
- end
1249
-
1250
- def wait_for_go_or_stop_or_retry(error = nil)
1251
- @go = false
1252
- sleep(0.01) until @go or @stop or @retry_needed
1253
- @go = false
1254
- mark_running()
1255
- Kernel.raise StopScript if @stop
1256
- Kernel.raise error if error and !@continue_after_error
1257
- end
1258
-
1259
- def mark_running
1260
- Qt.execute_in_main_thread(true) do
1261
- @run_callback.call(self) if @run_callback
1262
- @active_script.rehighlight
1263
- @realtime_button_bar.state = 'Running'
1264
- @realtime_button_bar.start_button.setText('Go')
1265
- @realtime_button_bar.pause_button.setText('Pause')
1266
- end
1267
- end
1268
-
1269
- def mark_paused
1270
- Qt.execute_in_main_thread(true) do
1271
- @pause_callback.call(self) if @pause_callback
1272
- @active_script.rehighlight('lightblue')
1273
- @realtime_button_bar.state = 'Paused'
1274
- end
1275
- end
1276
-
1277
- def mark_error
1278
- Qt.execute_in_main_thread(true) do
1279
- @error_callback.call(self) if @error_callback
1280
- @active_script.rehighlight('pink')
1281
- @realtime_button_bar.state = 'Error'
1282
- @realtime_button_bar.pause_button.setText('Retry')
1283
- end
1284
- end
1285
-
1286
- def mark_stopped
1287
- stop_message_log()
1288
- Qt.execute_in_main_thread(true) do
1289
- @realtime_button_bar.start_button.setText('Start')
1290
- @realtime_button_bar.pause_button.setText('Pause')
1291
- @realtime_button_bar.state = 'Stopped'
1292
- @stop_callback.call(self) if @stop_callback
1293
- end
1294
- end
1295
-
1296
- def mark_breakpoint
1297
- Qt.execute_in_main_thread(true) do
1298
- @active_script.rehighlight('tan')
1299
- @realtime_button_bar.state = 'Breakpoint'
1300
- end
1301
- end
1302
-
1303
- def run_text(text,
1304
- line_offset = 0,
1305
- text_binding = nil,
1306
- close_on_complete = false)
1307
- @run_callback.call(self) if @run_callback
1308
- @realtime_button_bar.start_button.setEnabled(true)
1309
- initialize_variables()
1310
- create_tabs()
1311
- @line_offset = line_offset
1312
- @script.setReadOnly(true)
1313
- @realtime_button_bar.start_button.setText(' Go ')
1314
- @realtime_button_bar.state = 'Running'
1315
-
1316
- saved_instance = @@instance
1317
- saved_run_thread = @@run_thread
1318
- @@instance = self
1319
- @@run_thread = Thread.new do
1320
- uncaught_exception = false
1321
- begin
1322
- # Capture STDOUT and STDERR
1323
- $stdout.add_stream(@output_io)
1324
- $stderr.add_stream(@output_io)
1325
-
1326
- scriptrunner_puts "Starting script: #{File.basename(@filename)}" unless close_on_complete
1327
- handle_output_io()
1328
-
1329
- # Start Limits Monitoring
1330
- @@limits_monitor_thread = Thread.new { limits_monitor_thread() } if @@monitor_limits and !@@limits_monitor_thread
1331
-
1332
- # Start Output Thread
1333
- @@output_thread = Thread.new { output_thread() } unless @@output_thread
1334
-
1335
- # Check top level cache
1336
- if @top_level_instrumented_cache &&
1337
- (@top_level_instrumented_cache[1] == line_offset) &&
1338
- (@top_level_instrumented_cache[2] == @filename) &&
1339
- (@top_level_instrumented_cache[0] == text)
1340
- # Use the instrumented cache
1341
- instrumented_script = @top_level_instrumented_cache[3]
1342
- else
1343
- # Instrument the script
1344
- if text_binding
1345
- instrumented_script = self.class.instrument_script(text, @filename, false)
1346
- else
1347
- instrumented_script = self.class.instrument_script(text, @filename, true)
1348
- end
1349
- @top_level_instrumented_cache = [text, line_offset, @filename, instrumented_script]
1350
- end
1351
-
1352
- # Execute the script with warnings disabled
1353
- Cosmos.disable_warnings do
1354
- @pre_line_time = Time.now
1355
- Cosmos.set_working_dir do
1356
- if text_binding
1357
- eval(instrumented_script, text_binding, @filename, 1)
1358
- else
1359
- Object.class_eval(instrumented_script, @filename, 1)
1360
- end
1361
- end
1362
- end
1363
-
1364
- scriptrunner_puts "Script completed: #{File.basename(@filename)}" unless close_on_complete
1365
- handle_output_io()
1366
-
1367
- rescue Exception => error
1368
- if error.class == StopScript or error.class == SkipTestCase
1369
- scriptrunner_puts "Script stopped: #{File.basename(@filename)}"
1370
- handle_output_io()
1371
- else
1372
- uncaught_exception = true
1373
- filename, line_number = error.source
1374
- handle_exception(error, true, filename, line_number)
1375
- scriptrunner_puts "Exception in Control Statement - Script stopped: #{File.basename(@filename)}"
1376
- handle_output_io()
1377
- Qt.execute_in_main_thread(true) {@script.rehighlight('red') }
1378
- end
1379
- ensure
1380
- # Change Go Button to Start Button and remove highlight
1381
- Qt.execute_in_main_thread(true) do
1382
- # Stop Capturing STDOUT and STDERR
1383
- # Check for remove_stream because if the tool is quitting the
1384
- # Cosmos::restore_io may have been called which sets $stdout and
1385
- # $stderr to the IO constant
1386
- $stdout.remove_stream(@output_io) if $stdout.respond_to? :remove_stream
1387
- $stderr.remove_stream(@output_io) if $stderr.respond_to? :remove_stream
1388
-
1389
- # Clear run thread and instance to indicate we are no longer running
1390
- @@instance = saved_instance
1391
- @@run_thread = saved_run_thread
1392
- @active_script = @script
1393
- @script_binding = nil
1394
- @current_filename = nil
1395
- @current_line_number = 0
1396
- if @@limits_monitor_thread and not @@instance
1397
- @@limits_monitor_thread.kill
1398
- @@limits_monitor_thread = nil
1399
- end
1400
- if @@output_thread and not @@instance
1401
- @@output_thread.kill
1402
- @@output_thread = nil
1403
- end
1404
-
1405
- @script.setReadOnly(false)
1406
- @script.stop_highlight unless uncaught_exception
1407
- select_tab_and_destroy_tabs_after_index(0)
1408
- remove_tabs()
1409
- unless @allow_start
1410
- @realtime_button_bar.start_button.setEnabled(false)
1411
- @script.setReadOnly(true)
1412
- end
1413
- mark_stopped()
1414
- if close_on_complete
1415
- self.parent.done(0)
1416
- end
1417
- end
1418
- end
1419
- end
1420
- end
1421
-
1422
- def handle_potential_tab_change(filename)
1423
- # Make sure the correct file is shown in script runner
1424
- if @current_file != filename and @tab_book_shown
1425
- Qt.execute_in_main_thread(true) do
1426
- if @call_stack.include?(filename)
1427
- index = @call_stack.index(filename)
1428
- select_tab_and_destroy_tabs_after_index(index)
1429
- else # new file
1430
- # Create new tab
1431
- new_script = create_ruby_editor()
1432
- @tab_book.addTab(new_script, ' ' + File.basename(filename) + ' ')
1433
-
1434
- @call_stack.push(filename.dup)
1435
-
1436
- # Switch to new tab
1437
- @tab_book.setCurrentIndex(@tab_book.count - 1)
1438
- @active_script = new_script
1439
- load_file_into_script(filename)
1440
- new_script.setReadOnly(true)
1441
- end
1442
-
1443
- @current_file = filename
1444
- end
1445
- end
1446
- end
1447
-
1448
- def show_active_tab
1449
- @tab_book.setCurrentIndex(@call_stack.length - 1) if @tab_book_shown
1450
- end
1451
-
1452
- def handle_pause(filename, line_number)
1453
- filename = File.basename(filename)
1454
- breakpoint = false
1455
- breakpoint = true if @@breakpoints[filename] and @@breakpoints[filename][line_number]
1456
- if @pause
1457
- @pause = false unless @@step_mode
1458
- if breakpoint
1459
- perform_breakpoint(filename, line_number)
1460
- else
1461
- perform_pause()
1462
- end
1463
- else
1464
- perform_breakpoint(filename, line_number) if breakpoint
1465
- end
1466
- end
1467
-
1468
- def handle_line_delay
1469
- if @@line_delay > 0.0
1470
- sleep_time = @@line_delay - (Time.now - @pre_line_time)
1471
- sleep(sleep_time) if sleep_time > 0.0
1472
- end
1473
- @pre_line_time = Time.now
1474
- end
1475
-
1476
- def continue_without_pausing_on_errors?
1477
- if !@@pause_on_error
1478
- if Qt::MessageBox.warning(self, "Warning", "If an error occurs, the script will not pause and will run to completion. Continue?", Qt::MessageBox::Yes | Qt::MessageBox::No, Qt::MessageBox::Yes) == Qt::MessageBox::No
1479
- return false
1480
- end
1481
- end
1482
- true
1483
- end
1484
-
1485
- def handle_start_go_button
1486
- scriptrunner_puts "User pressed #{@realtime_button_bar.start_button.text.strip}"
1487
- handle_output_io()
1488
- @realtime_button_bar.start_button.clear_focus()
1489
-
1490
- if running?()
1491
- show_active_tab()
1492
- go()
1493
- else
1494
- if @allow_start
1495
- run() if continue_without_pausing_on_errors?()
1496
- end
1497
- end
1498
- end
1499
-
1500
- def handle_pause_retry_button
1501
- scriptrunner_puts "User pressed #{@realtime_button_bar.pause_button.text.strip}"
1502
- handle_output_io()
1503
- @realtime_button_bar.pause_button.clear_focus()
1504
- show_active_tab() if running?
1505
- if @realtime_button_bar.pause_button.text.to_s == 'Pause'
1506
- pause()
1507
- else
1508
- retry_needed()
1509
- end
1510
- end
1511
-
1512
- def handle_stop_button
1513
- scriptrunner_puts "User pressed #{@realtime_button_bar.stop_button.text.strip}"
1514
- handle_output_io()
1515
- @realtime_button_bar.stop_button.clear_focus()
1516
-
1517
- if @stop
1518
- # If we're already stopped and they click Stop again, kill the run
1519
- # thread. This will break any ruby sleeps or other code blocks.
1520
- ScriptRunnerFrame.stop!
1521
- handle_output_io()
1522
- else
1523
- @stop = true
1524
- end
1525
- if !running?()
1526
- # Stop highlight if there was a red syntax error
1527
- @script.stop_highlight
1528
- end
1529
- end
1530
-
1531
- def handle_exception(error, fatal, filename = nil, line_number = 0)
1532
- @error_callback.call(self) if @error_callback
1533
-
1534
- @exceptions ||= []
1535
- @exceptions << error
1536
- @@error = error
1537
-
1538
- if error.class == DRb::DRbConnError
1539
- Logger.error("Error Connecting to Command and Telemetry Server")
1540
- elsif error.class == CheckError
1541
- Logger.error(error.message)
1542
- else
1543
- Logger.error(error.class.to_s.split('::')[-1] + ' : ' + error.message)
1544
- end
1545
- Logger.error(error.backtrace.join("\n")) if @@show_backtrace
1546
- handle_output_io(filename, line_number)
1547
-
1548
- Kernel.raise error if !@@pause_on_error and !@continue_after_error and !fatal
1549
-
1550
- if !fatal and @@pause_on_error
1551
- mark_error()
1552
- wait_for_go_or_stop_or_retry(error)
1553
- end
1554
-
1555
- if @retry_needed
1556
- @retry_needed = false
1557
- true
1558
- else
1559
- false
1560
- end
1561
- end
1562
-
1563
- # Right click context_menu for the script
1564
- def context_menu(point)
1565
- if @tab_book_shown
1566
- current_script = @tab_book.tab(@tab_book.currentIndex)
1567
- else
1568
- current_script = @script
1569
- end
1570
- menu = current_script.context_menu(point)
1571
- menu.addSeparator()
1572
- if not self.class.running?
1573
- exec_selected_action = Qt::Action.new(tr("Execute Selected Lines"), self)
1574
- exec_selected_action.statusTip = tr("Execute the selected lines as a standalone script")
1575
- exec_selected_action.connect(SIGNAL('triggered()')) { run_selection() }
1576
- menu.addAction(exec_selected_action)
1577
-
1578
- exec_cursor_action = Qt::Action.new(tr("Execute From Cursor"), self)
1579
- exec_cursor_action.statusTip = tr("Execute the script starting at the line containing the cursor")
1580
- exec_cursor_action.connect(SIGNAL('triggered()')) { run_from_cursor() }
1581
- menu.addAction(exec_cursor_action)
1582
-
1583
- menu.addSeparator()
1584
-
1585
- if RUBY_VERSION.split('.')[0].to_i > 1
1586
- syntax_action = Qt::Action.new(tr("Ruby Syntax Check Selected Lines"), self)
1587
- syntax_action.statusTip = tr("Check the selected lines for valid Ruby syntax")
1588
- syntax_action.connect(SIGNAL('triggered()')) { ruby_syntax_check_selection() }
1589
- menu.addAction(syntax_action)
1590
- end
1591
-
1592
- mnemonic_action = Qt::Action.new(tr("Mnemonic Check Selected Lines"), self)
1593
- mnemonic_action.statusTip = tr("Check the selected lines for valid targets, packets, mnemonics and parameters")
1594
- mnemonic_action.connect(SIGNAL('triggered()')) { mnemonic_check_selection() }
1595
- menu.addAction(mnemonic_action)
1596
-
1597
- elsif running?() and @realtime_button_bar.state != 'Running'
1598
- exec_selected_action = Qt::Action.new(tr("Execute Selected Lines While Paused"), self)
1599
- exec_selected_action.statusTip = tr("Execute the selected lines as a standalone script")
1600
- exec_selected_action.connect(SIGNAL('triggered()')) { run_selection_while_paused() }
1601
- menu.addAction(exec_selected_action)
1602
- end
1603
- menu.exec(current_script.mapToGlobal(point))
1604
- menu.dispose
1605
- end
1606
-
1607
- def load_file_into_script(filename)
1608
- cached = @@file_cache[filename]
1609
- if cached
1610
- @active_script.setPlainText(cached.gsub("\r", ''))
1611
- else
1612
- @active_script.setPlainText(File.read(filename).gsub("\r", ''))
1613
- end
1614
- mark_breakpoints(filename)
1615
-
1616
- @active_script.stop_highlight
1617
- end
1618
-
1619
- def mark_breakpoints(filename)
1620
- @active_script.clear_breakpoints
1621
- breakpoints = @@breakpoints[File.basename(filename)]
1622
- if breakpoints
1623
- breakpoints.each do |line_number, present|
1624
- @active_script.breakpoint(line_number) if present
1625
- end
1626
- end
1627
- end
1628
-
1629
- def redirect_io
1630
- # Redirect Standard Output and Standard Error
1631
- $stdout = Stdout.instance
1632
- $stderr = Stderr.instance
1633
-
1634
- # Disable outputting to default io file descriptors
1635
- $stdout.remove_default_io
1636
- $stderr.remove_default_io
1637
-
1638
- Logger.level = Logger::INFO
1639
- Logger::INFO_SEVERITY_STRING.replace('')
1640
- Logger::WARN_SEVERITY_STRING.replace('<Y> WARN:')
1641
- Logger::ERROR_SEVERITY_STRING.replace('<R> ERROR:')
1642
- end
1643
-
1644
- def create_tabs
1645
- tab_text = @default_tab_text
1646
- tab_text = ' ' + File.basename(@filename) + ' ' unless @filename.empty?
1647
- @tab_book.addTab(@script, tab_text)
1648
- @top_frame.insertWidget(0, @tab_book)
1649
- @tab_book_shown = true
1650
- end
1651
-
1652
- def remove_tabs
1653
- @top_frame.takeAt(0) # Remove the @tab_book from the layout
1654
- @top_frame.addWidget(@script) # Add back the script
1655
- @script.show
1656
- @tab_book_shown = false
1657
- end
1658
-
1659
- def isolate_string(keyword, line)
1660
- found_string = nil
1661
-
1662
- # Find keyword
1663
- keyword_index = line.index(keyword)
1664
-
1665
- # Remove keyword from line
1666
- line = line[(keyword_index + keyword.length)..-1]
1667
-
1668
- # Find start parens
1669
- start_parens = line.index('(')
1670
- if start_parens
1671
- end_parens = line[start_parens..-1].index(')')
1672
- found_string = line[(start_parens + 1)..(end_parens + start_parens - 1)].remove_quotes if end_parens
1673
- if keyword == 'wait' or keyword == 'wait_check'
1674
- quote_index = found_string.rindex('"')
1675
- quote_index = found_string.rindex("'") unless quote_index
1676
- if quote_index
1677
- found_string = found_string[0..quote_index].remove_quotes
1678
- else
1679
- found_string = nil
1680
- end
1681
- end
1682
- end
1683
- found_string
1684
- end
1685
-
1686
- def mnemonic_check_cmd_line(keyword, line_number, line)
1687
- result = nil
1688
-
1689
- # Isolate the string
1690
- string = isolate_string(keyword, line)
1691
- if string
1692
- begin
1693
- target_name, cmd_name, cmd_params = extract_fields_from_cmd_text(string)
1694
- result = "At line #{line_number}: Unknown command: #{target_name} #{cmd_name}"
1695
- packet = System.commands.packet(target_name, cmd_name)
1696
- Kernel.raise "Command not found" unless packet
1697
- cmd_params.each do |param_name, param_value|
1698
- result = "At line #{line_number}: Unknown command parameter: #{target_name} #{cmd_name} #{param_name}"
1699
- packet.get_item(param_name)
1700
- end
1701
- result = nil
1702
- rescue
1703
- if result
1704
- if string.index('=>')
1705
- # Assume alternative syntax
1706
- result = nil
1707
- end
1708
- else
1709
- result = "At line #{line_number}: Potentially malformed command: #{line}"
1710
- end
1711
- end
1712
- end
1713
-
1714
- result
1715
- end
1716
-
1717
- def _mnemonic_check_tlm_line(keyword, line_number, line)
1718
- result = nil
1719
-
1720
- # Isolate the string
1721
- string = isolate_string(keyword, line)
1722
- if string
1723
- begin
1724
- target_name, packet_name, item_name = yield string
1725
- result = "At line #{line_number}: Unknown telemetry item: #{target_name} #{packet_name} #{item_name}"
1726
- System.telemetry.packet_and_item(target_name, packet_name, item_name)
1727
- result = nil
1728
- rescue
1729
- if result
1730
- if string.index(',')
1731
- # Assume alternative syntax
1732
- result = nil
1733
- end
1734
- else
1735
- result = "At line #{line_number}: Potentially malformed telemetry: #{line}"
1736
- end
1737
- end
1738
- end
1739
-
1740
- result
1741
- end
1742
-
1743
- def mnemonic_check_tlm_line(keyword, line_number, line)
1744
- _mnemonic_check_tlm_line(keyword, line_number, line) do |string|
1745
- extract_fields_from_tlm_text(string)
1746
- end
1747
- end
1748
-
1749
- def mnemonic_check_set_tlm_line(keyword, line_number, line)
1750
- _mnemonic_check_tlm_line(keyword, line_number, line) do |string|
1751
- extract_fields_from_set_tlm_text(string)
1752
- end
1753
- end
1754
-
1755
- def mnemonic_check_check_line(keyword, line_number, line)
1756
- _mnemonic_check_tlm_line(keyword, line_number, line) do |string|
1757
- extract_fields_from_check_text(string)
1758
- end
1759
- end
1760
-
1761
- def output_thread
1762
- begin
1763
- loop do
1764
- handle_output_io() if (Time.now - @output_time) > 5.0
1765
- sleep(1.0)
1766
- end # loop
1767
- rescue => error
1768
- Qt.execute_in_main_thread(true) do
1769
- ExceptionDialog.new(self, error, "Output Thread")
1770
- end
1771
- end
1772
- end
1773
-
1774
- def limits_monitor_thread
1775
- queue_id = nil
1776
- begin
1777
- loop do
1778
- begin
1779
- # Subscribe to limits notifications
1780
- queue_id = subscribe_limits_events(100000) unless queue_id
1781
-
1782
- # Get the next limits event
1783
- begin
1784
- type, data = get_limits_event(queue_id, true)
1785
- rescue ThreadError
1786
- sleep(0.5)
1787
- next
1788
- end
1789
-
1790
- # Display limits state changes
1791
- if type == :LIMITS_CHANGE
1792
- target_name = data[0]
1793
- packet_name = data[1]
1794
- item_name = data[2]
1795
- old_limits_state = data[3]
1796
- new_limits_state = data[4]
1797
-
1798
- if old_limits_state == nil # Changing from nil
1799
- if (new_limits_state != :GREEN) &&
1800
- (new_limits_state != :GREEN_LOW) &&
1801
- (new_limits_state != :GREEN_HIGH) &&
1802
- (new_limits_state != :BLUE)
1803
- msg = "#{target_name} #{packet_name} #{item_name} is #{new_limits_state.to_s}"
1804
- case new_limits_state
1805
- when :YELLOW, :YELLOW_LOW, :YELLOW_HIGH
1806
- scriptrunner_puts "<Y>#{msg}"
1807
- when :RED, :RED_LOW, :RED_HIGH
1808
- scriptrunner_puts "<R>#{msg}"
1809
- else
1810
- # Print nothing
1811
- end
1812
- handle_output_io()
1813
- end
1814
- else # changing from a color
1815
- msg = "#{target_name} #{packet_name} #{item_name} is #{new_limits_state.to_s}"
1816
- case new_limits_state
1817
- when :BLUE
1818
- scriptrunner_puts "<B>#{msg}"
1819
- when :GREEN, :GREEN_LOW, :GREEN_HIGH
1820
- scriptrunner_puts "<G>#{msg}"
1821
- when :YELLOW, :YELLOW_LOW, :YELLOW_HIGH
1822
- scriptrunner_puts "<Y>#{msg}"
1823
- when :RED, :RED_LOW, :RED_HIGH
1824
- scriptrunner_puts "<R>#{msg}"
1825
- else
1826
- # Print nothing
1827
- end
1828
- handle_output_io()
1829
- end
1830
-
1831
- if @@pause_on_red && (new_limits_state == :RED ||
1832
- new_limits_state == :RED_LOW ||
1833
- new_limits_state == :RED_HIGH)
1834
- pause()
1835
- end
1836
- end
1837
-
1838
- rescue DRb::DRbConnError
1839
- queue_id = nil
1840
- sleep(1)
1841
- end
1842
-
1843
- end # loop
1844
- rescue => error
1845
- Qt.execute_in_main_thread(true) do
1846
- ExceptionDialog.new(self, error, "Limits Monitor Thread")
1847
- end
1848
- end
1849
- ensure
1850
- begin
1851
- unsubscribe_limits_events(queue_id) if queue_id
1852
- rescue
1853
- # Oh Well
1854
- end
1855
- end
1856
-
1857
- end # class ScriptRunnerFrame
1858
-
1859
- end # module Cosmos
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2014 Ball Aerospace & Technologies Corp.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is free software; you can modify and/or redistribute it
7
+ # under the terms of the GNU General Public License
8
+ # as published by the Free Software Foundation; version 3 with
9
+ # attribution addendums as found in the LICENSE.txt
10
+
11
+ require 'cosmos'
12
+ require 'cosmos/script'
13
+ require 'cosmos/gui/utilities/script_module_gui'
14
+ require 'cosmos/gui/dialogs/splash'
15
+ require 'cosmos/gui/dialogs/exception_dialog'
16
+ require 'cosmos/gui/text/completion'
17
+ require 'cosmos/gui/text/completion_line_edit'
18
+ require 'cosmos/gui/text/ruby_editor'
19
+ require 'cosmos/gui/dialogs/progress_dialog'
20
+ require 'cosmos/gui/widgets/realtime_button_bar'
21
+ require 'cosmos/gui/dialogs/find_replace_dialog'
22
+ require 'cosmos/gui/choosers/file_chooser'
23
+ require 'cosmos/io/stdout'
24
+ require 'cosmos/io/stderr'
25
+
26
+ module Cosmos
27
+
28
+ class ScriptRunnerDialog < Qt::Dialog
29
+ attr_reader :script_runner_frame
30
+
31
+ # Constructor
32
+ def initialize(parent,
33
+ title,
34
+ default_tab_text = 'Untitled')
35
+ # Call base class constructor
36
+ super(parent)
37
+ setWindowTitle(title)
38
+ setMinimumWidth(parent.width * 0.8)
39
+ setMinimumHeight(parent.height * 0.6)
40
+
41
+ # Create script runner frame
42
+ @script_runner_frame = ScriptRunnerFrame.new(self, default_tab_text)
43
+ layout = Qt::VBoxLayout.new
44
+ layout.addWidget(@script_runner_frame)
45
+ setLayout(layout)
46
+ end # def initialize
47
+
48
+ # Executes the given text and closes when complete
49
+ def execute_text_and_close_on_complete(text, text_binding = nil)
50
+ @script_runner_frame.set_text(text)
51
+ @script_runner_frame.run_and_close_on_complete(text_binding)
52
+ exec
53
+ dispose
54
+ end
55
+
56
+ def reject
57
+ # Don't allow the dialog to close if we're running
58
+ return if @script_runner_frame.running?
59
+ super
60
+ end
61
+ end # class ScriptRunnerDialog
62
+
63
+ class ScriptRunnerFrame < Qt::Widget
64
+ slots 'context_menu(const QPoint&)'
65
+ slots 'undo_available(bool)'
66
+ slots 'breakpoint_set(int)'
67
+ slots 'breakpoint_cleared(int)'
68
+ slots 'breakpoints_cleared()'
69
+ signals 'undoAvailable(bool)'
70
+ signals 'modificationChanged(bool)'
71
+
72
+ CMD_KEYWORDS = %w(cmd cmd_no_range_check cmd_no_hazardous_check cmd_no_checks)
73
+ TLM_KEYWORDS = %w(tlm tlm_raw tlm_formatted tlm_with_units limits_enabled? \
74
+ enable_limits disable_limits wait_tolerance wait_tolerance_raw \
75
+ check_tolerance check_tolerance_raw wait_check_tolerance \
76
+ wait_check_tolerance_raw)
77
+ SET_TLM_KEYWORDS = %w(set_tlm set_tlm_raw)
78
+ CHECK_KEYWORDS = %w(check check_raw wait wait_raw wait_check wait_check_raw)
79
+
80
+ attr_accessor :use_instrumentation
81
+ attr_accessor :change_callback
82
+ attr_accessor :run_callback
83
+ attr_accessor :stop_callback
84
+ attr_accessor :error_callback
85
+ attr_accessor :pause_callback
86
+ attr_reader :filename
87
+ attr_accessor :continue_after_error
88
+ attr_accessor :exceptions
89
+ attr_accessor :script_binding
90
+ attr_accessor :inline_return
91
+ attr_accessor :inline_return_params
92
+ attr_reader :message_log
93
+ attr_reader :script_class
94
+ attr_reader :top_level_instrumented_cache
95
+
96
+ @@instance = nil
97
+ @@run_thread = nil
98
+ @@breakpoints = {}
99
+ @@step_mode = false
100
+ @@line_delay = 0.1
101
+ @@instrumented_cache = {}
102
+ @@file_cache = {}
103
+ @@output_thread = nil
104
+ @@limits_monitor_thread = nil
105
+ @@pause_on_error = true
106
+ @@monitor_limits = false
107
+ @@pause_on_red = false
108
+ @@show_backtrace = false
109
+ @@error = nil
110
+ @@output_sleeper = Sleeper.new
111
+ @@limits_sleeper = Sleeper.new
112
+ @@cancel_output = false
113
+ @@cancel_limits = false
114
+
115
+ def initialize(parent, default_tab_text = 'Untitled')
116
+ super(parent)
117
+ @default_tab_text = ' ' + default_tab_text + ' '
118
+ # Keep track of whether this frame has been fully initialized
119
+ @initialized = false
120
+ @debug_frame = nil
121
+
122
+ @layout = Qt::VBoxLayout.new
123
+ @layout.setContentsMargins(0,0,0,0)
124
+
125
+ # Add Realtime Button Bar
126
+ @realtime_button_bar = RealtimeButtonBar.new(self)
127
+ @realtime_button_bar.state = 'Stopped'
128
+ @realtime_button_bar.start_callback = method(:handle_start_go_button)
129
+ @realtime_button_bar.pause_callback = method(:handle_pause_retry_button)
130
+ @realtime_button_bar.stop_callback = method(:handle_stop_button)
131
+ @layout.addWidget(@realtime_button_bar)
132
+
133
+ # Create a splitter to hold the script text area and the script output text area.
134
+ @splitter = Qt::Splitter.new(Qt::Vertical, self)
135
+ @layout.addWidget(@splitter)
136
+ @top_widget = Qt::Widget.new(@splitter)
137
+ @top_widget.setContentsMargins(0,0,0,0)
138
+ @top_frame = Qt::VBoxLayout.new(@top_widget)
139
+ @top_frame.setContentsMargins(0,0,0,0)
140
+
141
+ # Add Initial Text Window
142
+ @script = create_ruby_editor()
143
+ @script.connect(SIGNAL('modificationChanged(bool)')) do |changed|
144
+ emit modificationChanged(changed)
145
+ end
146
+ @top_frame.addWidget(@script)
147
+
148
+ # Set self as the gui window to allow prompts and other popups to appear
149
+ set_cmd_tlm_gui_window(self)
150
+
151
+ # Add change handlers
152
+ connect(@script,
153
+ SIGNAL('undoAvailable(bool)'),
154
+ self,
155
+ SLOT('undo_available(bool)'))
156
+
157
+ # Add Output Text
158
+ @bottom_frame = Qt::Widget.new
159
+ @bottom_layout = Qt::VBoxLayout.new
160
+ @bottom_layout.setContentsMargins(0,0,0,0)
161
+ @bottom_layout_label = Qt::Label.new("Script Output:")
162
+ @bottom_layout.addWidget(@bottom_layout_label)
163
+ @output = Qt::PlainTextEdit.new
164
+ @output.setReadOnly(true)
165
+ @output.setMaximumBlockCount(10000) # 10000 lines of history will be displayed
166
+ @bottom_layout.addWidget(@output)
167
+ @bottom_frame.setLayout(@bottom_layout)
168
+ @splitter.addWidget(@bottom_frame)
169
+ @splitter.setStretchFactor(0,10)
170
+ @splitter.setStretchFactor(1,1)
171
+
172
+ setLayout(@layout)
173
+
174
+ # Configure Variables
175
+ @line_offset = 0
176
+ @filename = ''
177
+ @output_io = StringIO.new('', 'r+')
178
+ @output_io_mutex = Mutex.new
179
+ @change_callback = nil
180
+ @run_callback = nil
181
+ @stop_callback = nil
182
+ @error_callback = nil
183
+ @pause_callback = nil
184
+ @key_press_callback = nil
185
+ @allow_start = true
186
+ @continue_after_error = true
187
+ @debug_text = nil
188
+ @debug_history = []
189
+ @debug_code_completion = nil
190
+ @top_level_instrumented_cache = nil
191
+ @output_time = Time.now
192
+ initialize_variables()
193
+
194
+ # Redirect $stdout and $stderr
195
+ redirect_io()
196
+
197
+ # Create Tabbook
198
+ @tab_book = Qt::TabWidget.new
199
+ @tab_book_shown = false
200
+
201
+ @find_dialog = nil
202
+ @replace_dialog = nil
203
+
204
+ mark_breakpoints('')
205
+ end
206
+
207
+ def current_tab_filename
208
+ if @tab_book_shown
209
+ filename = @tab_book.tabText(@tab_book.currentIndex)
210
+ if filename == @default_tab_text
211
+ return ''
212
+ else
213
+ return filename.strip
214
+ end
215
+ else
216
+ return @filename
217
+ end
218
+ end
219
+
220
+ def create_ruby_editor
221
+ # Add Initial Text Window
222
+ script = RubyEditor.new(self)
223
+ script.enable_breakpoints = true if @debug_frame
224
+ connect(script,
225
+ SIGNAL('breakpoint_set(int)'),
226
+ self,
227
+ SLOT('breakpoint_set(int)'))
228
+ connect(script,
229
+ SIGNAL('breakpoint_cleared(int)'),
230
+ self,
231
+ SLOT('breakpoint_cleared(int)'))
232
+ connect(script,
233
+ SIGNAL('breakpoints_cleared()'),
234
+ self,
235
+ SLOT('breakpoints_cleared()'))
236
+
237
+ # Add right click menu
238
+ script.setContextMenuPolicy(Qt::CustomContextMenu)
239
+ connect(script,
240
+ SIGNAL('customContextMenuRequested(const QPoint&)'),
241
+ self,
242
+ SLOT('context_menu(const QPoint&)'))
243
+
244
+ return script
245
+ end
246
+
247
+ def stop_message_log
248
+ @message_log.stop if @message_log
249
+ @message_log = nil
250
+ end
251
+
252
+ def filename=(filename)
253
+ # Stop the message log so a new one will be created with the new filename
254
+ stop_message_log()
255
+ @filename = filename
256
+ mark_breakpoints(filename)
257
+ end
258
+
259
+ def modified
260
+ @script.document.isModified()
261
+ end
262
+
263
+ def modified=(bool)
264
+ @script.document.setModified(bool)
265
+ end
266
+
267
+ def undo_available(bool)
268
+ emit undoAvailable(bool)
269
+ end
270
+
271
+ def key_press_callback=(callback)
272
+ @script.keyPressCallback = callback
273
+ end
274
+
275
+ def setFocus
276
+ @script.setFocus
277
+ end
278
+
279
+ def allow_start=(value)
280
+ @allow_start = value
281
+ if @allow_start
282
+ @realtime_button_bar.start_button.setEnabled(true)
283
+ elsif not running?
284
+ @realtime_button_bar.start_button.setEnabled(false)
285
+ @script.setReadOnly(true)
286
+ end
287
+ end
288
+
289
+ def clear
290
+ self.set_text('')
291
+ self.filename = ''
292
+ self.modified = false
293
+ end
294
+
295
+ def self.instance
296
+ @@instance
297
+ end
298
+
299
+ def self.instance=(value)
300
+ @@instance = value
301
+ end
302
+
303
+ def self.step_mode
304
+ @@step_mode
305
+ end
306
+
307
+ def self.step_mode=(value)
308
+ @@step_mode = value
309
+ if self.instance
310
+ if value
311
+ self.instance.pause
312
+ else
313
+ self.instance.go
314
+ end
315
+ end
316
+ end
317
+
318
+ def self.line_delay
319
+ @@line_delay
320
+ end
321
+
322
+ def self.line_delay=(value)
323
+ @@line_delay = value
324
+ end
325
+
326
+ def self.instrumented_cache
327
+ @@instrumented_cache
328
+ end
329
+
330
+ def self.instrumented_cache=(value)
331
+ @@instrumented_cache = value
332
+ end
333
+
334
+ def self.pause_on_error
335
+ @@pause_on_error
336
+ end
337
+
338
+ def self.pause_on_error=(value)
339
+ @@pause_on_error = value
340
+ end
341
+
342
+ def self.monitor_limits
343
+ @@monitor_limits
344
+ end
345
+
346
+ def self.monitor_limits=(value)
347
+ @@monitor_limits = value
348
+ end
349
+
350
+ def self.pause_on_red
351
+ @@pause_on_red
352
+ end
353
+
354
+ def self.pause_on_red=(value)
355
+ @@pause_on_red = value
356
+ end
357
+
358
+ def self.show_backtrace
359
+ @@show_backtrace
360
+ end
361
+
362
+ def self.show_backtrace=(value)
363
+ @@show_backtrace = value
364
+ if @@show_backtrace and @@error
365
+ puts Time.now.formatted + " (SCRIPTRUNNER): " + "Most recent exception:\n" + @@error.formatted
366
+ end
367
+ end
368
+
369
+ def text
370
+ @script.toPlainText.gsub("\r", '')
371
+ end
372
+
373
+ def set_text(text, filename = '')
374
+ unless running?()
375
+ @script.setPlainText(text)
376
+ @script.stop_highlight
377
+ @filename = filename
378
+ mark_breakpoints(filename)
379
+ end
380
+ end
381
+
382
+ def set_text_from_file(filename)
383
+ unless running?()
384
+ @@file_cache[filename] = nil
385
+ load_file_into_script(filename)
386
+ @filename = filename
387
+ end
388
+ end
389
+
390
+ def self.running?
391
+ if @@run_thread then true else false end
392
+ end
393
+
394
+ def running?
395
+ if @@instance == self and ScriptRunnerFrame.running?() then true else false end
396
+ end
397
+
398
+ def go
399
+ @go = true
400
+ @pause = false unless @@step_mode
401
+ end
402
+
403
+ def go?
404
+ temp = @go
405
+ @go = false
406
+ temp
407
+ end
408
+
409
+ def pause
410
+ @pause = true
411
+ @go = false
412
+ end
413
+
414
+ def pause?
415
+ @pause
416
+ end
417
+
418
+ def self.stop!
419
+ if @@run_thread
420
+ Cosmos.kill_thread(nil, @@run_thread)
421
+ @@run_thread = nil
422
+ end
423
+ end
424
+
425
+ def stop?
426
+ @stop
427
+ end
428
+
429
+ def retry_needed?
430
+ @retry_needed
431
+ end
432
+
433
+ def retry_needed
434
+ @retry_needed = true
435
+ end
436
+
437
+ def disable_retry
438
+ @realtime_button_bar.start_button.setText('Skip')
439
+ @realtime_button_bar.pause_button.setDisabled(true)
440
+ end
441
+
442
+ def enable_retry
443
+ @realtime_button_bar.start_button.setText('Go')
444
+ @realtime_button_bar.pause_button.setDisabled(false)
445
+ end
446
+
447
+ def run
448
+ unless self.class.running?()
449
+ run_text(@script.toPlainText)
450
+ end
451
+ end
452
+
453
+ def run_and_close_on_complete(text_binding = nil)
454
+ run_text(@script.toPlainText, 0, text_binding, true)
455
+ end
456
+
457
+ def self.instrument_script(text, filename, mark_private = false)
458
+ if filename and !filename.empty?
459
+ @@file_cache[filename] = text.clone
460
+ end
461
+
462
+ ruby_lex_utils = RubyLexUtils.new
463
+ instrumented_text = ''
464
+ # Add an extra newline because the Ruby 1.9.1 lexer RubyLex has a bug
465
+ # where it loops unless the file ends in a newline. If there is already
466
+ # a newline then so what, we add an extra.
467
+ text += "\n"
468
+
469
+ Qt.execute_in_main_thread(true) do
470
+ window = Qt::CoreApplication.instance.activeWindow
471
+ @cancel_instrumentation = false
472
+ ProgressDialog.execute(window, # parent
473
+ "Instrumenting: #{File.basename(filename.to_s)}",
474
+ 500, # width
475
+ 100, # height
476
+ true, # show overall progress
477
+ false, # don't show step progress
478
+ false, # don't show text
479
+ false, # don't show done
480
+ true) do |progress_dialog| # show cancel
481
+ progress_dialog.cancel_callback = lambda {|dialog| @cancel_instrumentation = true; [true, false]}
482
+ progress_dialog.enable_cancel_button
483
+ comments_removed_text = ruby_lex_utils.remove_comments(text)
484
+ num_lines = comments_removed_text.num_lines.to_f
485
+ num_lines = 1 if num_lines < 1
486
+ instrumented_text =
487
+ instrument_script_implementation(ruby_lex_utils,
488
+ comments_removed_text,
489
+ num_lines,
490
+ progress_dialog,
491
+ filename,
492
+ mark_private)
493
+ progress_dialog.close_done
494
+ end
495
+ end
496
+
497
+ Kernel.raise StopScript if @cancel_instrumentation or ProgressDialog.canceled?
498
+ instrumented_text
499
+ end
500
+
501
+ def self.instrument_script_implementation(ruby_lex_utils,
502
+ comments_removed_text,
503
+ num_lines,
504
+ progress_dialog,
505
+ filename,
506
+ mark_private = false)
507
+ if mark_private
508
+ instrumented_text = 'private; '
509
+ else
510
+ instrumented_text = ''
511
+ end
512
+
513
+ ruby_lex_utils.each_lexed_segment(comments_removed_text) do |segment, instrumentable, inside_begin, line_no|
514
+ return nil if @cancel_instrumentation
515
+ instrumented_line = ''
516
+ if instrumentable
517
+ # If not inside a begin block then create one to catch exceptions
518
+ unless inside_begin
519
+ instrumented_line << 'begin; '
520
+ end
521
+
522
+ # Add preline instrumentation
523
+ instrumented_line << "ScriptRunnerFrame.instance.script_binding = binding(); if ScriptRunnerFrame.instance.inline_return then ScriptRunnerFrame.instance.inline_return = nil; return ScriptRunnerFrame.instance.inline_return_params; end; ScriptRunnerFrame.instance.pre_line_instrumentation('#{filename}', #{line_no}); "
524
+
525
+ # Add the actual line
526
+ instrumented_line << segment
527
+ instrumented_line.chomp!
528
+
529
+ # Add postline instrumentation
530
+ instrumented_line << "; ScriptRunnerFrame.instance.post_line_instrumentation('#{filename}', #{line_no})"
531
+
532
+ # Complete begin block to catch exceptions
533
+ unless inside_begin
534
+ instrumented_line << "; rescue Exception => eval_error; retry if ScriptRunnerFrame.instance.exception_instrumentation(eval_error, '#{filename}', #{line_no}); end"
535
+ end
536
+
537
+ instrumented_line << "\n"
538
+ else
539
+ unless segment =~ /^\s*end\s*$/ or segment =~ /^\s*when .*$/
540
+ num_left_brackets = segment.count('{')
541
+ num_right_brackets = segment.count('}')
542
+ num_left_square_brackets = segment.count('[')
543
+ num_right_square_brackets = segment.count(']')
544
+
545
+ if (num_right_brackets > num_left_brackets) ||
546
+ (num_right_square_brackets > num_left_square_brackets)
547
+ instrumented_line = segment
548
+ else
549
+ instrumented_line = "ScriptRunnerFrame.instance.pre_line_instrumentation('#{filename}', #{line_no}); " + segment
550
+ end
551
+ else
552
+ instrumented_line = segment
553
+ end
554
+ end
555
+
556
+ instrumented_text << instrumented_line
557
+
558
+ progress_dialog.set_overall_progress(line_no / num_lines) if progress_dialog and line_no
559
+ end
560
+ instrumented_text
561
+ end
562
+
563
+ def pre_line_instrumentation(filename, line_number)
564
+ @current_filename = filename
565
+ @current_line_number = line_number
566
+ if @use_instrumentation
567
+ # Clear go
568
+ @go = false
569
+
570
+ # Handle stopping mid-script if necessary
571
+ Kernel.raise StopScript if @stop
572
+
573
+ # Handle needing to change tabs
574
+ handle_potential_tab_change(filename)
575
+
576
+ # Adjust line number for offset in main script
577
+ line_number = line_number + @line_offset if @active_script.object_id == @script.object_id
578
+ detail_string = nil
579
+ if filename
580
+ detail_string = File.basename(filename) << ':' << line_number.to_s
581
+ end
582
+ Logger.detail_string = detail_string
583
+
584
+ # Highlight the line that is about to run
585
+ Qt.execute_in_main_thread(true) {@active_script.highlight_line(line_number)}
586
+
587
+ # Handle pausing the script
588
+ handle_pause(filename, line_number)
589
+
590
+ # Handle delay between lines
591
+ handle_line_delay()
592
+ end
593
+ end
594
+
595
+ def post_line_instrumentation(filename, line_number)
596
+ if @use_instrumentation
597
+ line_number = line_number + @line_offset if @active_script.object_id == @script.object_id
598
+ handle_output_io(filename, line_number)
599
+ end
600
+ end
601
+
602
+ def exception_instrumentation(error, filename, line_number)
603
+ if error.class == StopScript || error.class == SkipTestCase || !@use_instrumentation
604
+ Kernel.raise error
605
+ else
606
+ line_number = line_number + @line_offset if @active_script.object_id == @script.object_id
607
+ handle_exception(error, false, filename, line_number)
608
+ end
609
+ end
610
+
611
+ def perform_pause
612
+ mark_paused()
613
+ wait_for_go_or_stop()
614
+ end
615
+
616
+ def perform_breakpoint(filename, line_number)
617
+ mark_breakpoint()
618
+ scriptrunner_puts "Hit Breakpoint at #{filename}:#{line_number}"
619
+ handle_output_io(filename, line_number)
620
+ wait_for_go_or_stop()
621
+ end
622
+
623
+ # Prompts the user that a script is running before they close the app
624
+ def prompt_if_running_on_close
625
+ safe_to_continue = true
626
+ if running?
627
+ case Qt::MessageBox.warning(
628
+ self, # parent
629
+ 'Warning', # title
630
+ 'A Script is Running! Close Anyways?', # text
631
+ Qt::MessageBox::Yes | Qt::MessageBox::No, # buttons
632
+ Qt::MessageBox::No) # default button
633
+ when Qt::MessageBox::Yes
634
+ safe_to_continue = true
635
+ ScriptRunnerFrame.stop!
636
+ else
637
+ safe_to_continue = false
638
+ end
639
+ end
640
+ return safe_to_continue
641
+ end
642
+
643
+ ######################################
644
+ # Implement the breakpoint callbacks from the RubyEditor
645
+ ######################################
646
+ def breakpoint_set(line)
647
+ ScriptRunnerFrame.breakpoint(current_tab_filename(), line)
648
+ end
649
+
650
+ def breakpoint_cleared(line)
651
+ ScriptRunnerFrame.clear_breakpoint(current_tab_filename(), line)
652
+ end
653
+
654
+ def breakpoints_cleared
655
+ ScriptRunnerFrame.clear_breakpoints(current_tab_filename())
656
+ end
657
+
658
+ ######################################
659
+ # Implement edit functionality in the frame (cut, copy, paste, etc)
660
+ ######################################
661
+ def undo
662
+ @script.undo unless running?()
663
+ end
664
+
665
+ def redo
666
+ @script.redo unless running?()
667
+ end
668
+
669
+ def cut
670
+ @script.cut unless running?()
671
+ end
672
+
673
+ def copy
674
+ @script.copy unless running?()
675
+ end
676
+
677
+ def paste
678
+ @script.paste unless running?()
679
+ end
680
+
681
+ def select_all
682
+ @script.select_all unless running?()
683
+ end
684
+
685
+ def comment_or_uncomment_lines
686
+ @script.comment_or_uncomment_lines unless running?()
687
+ end
688
+
689
+ ##################################################################
690
+ # Implement Search functionality in the frame (find, replace, etc)
691
+ ##################################################################
692
+ def find
693
+ unless @find_dialog
694
+ @find_dialog = FindReplaceDialog.new(@script)
695
+ @find_dialog.connect(SIGNAL('find_next()')) { dialog_find(@find_dialog) }
696
+ end
697
+ @find_dialog.show
698
+ @find_dialog.raise
699
+ @find_dialog.activateWindow
700
+ end
701
+
702
+ def find_next
703
+ flags = FindReplaceDialog.find_flags
704
+ flags &= ~Qt::TextDocument::FindBackward.to_i
705
+ found = @script.find(FindReplaceDialog.find_text, flags)
706
+ if not found and FindReplaceDialog.wrap_around?
707
+ cursor = @script.textCursor
708
+ cursor.movePosition(Qt::TextCursor::Start)
709
+ @script.setTextCursor(cursor)
710
+ @script.find(FindReplaceDialog.find_text, flags)
711
+ end
712
+ end
713
+
714
+ def find_previous
715
+ flags = FindReplaceDialog.find_flags
716
+ flags |= Qt::TextDocument::FindBackward.to_i
717
+ found = @script.find(FindReplaceDialog.find_text, flags)
718
+ if not found and FindReplaceDialog.wrap_around?
719
+ cursor = @script.textCursor
720
+ cursor.movePosition(Qt::TextCursor::End)
721
+ @script.setTextCursor(cursor)
722
+ @script.find(FindReplaceDialog.find_text, flags)
723
+ end
724
+ end
725
+
726
+ def replace
727
+ unless @replace_dialog
728
+ @replace_dialog = FindReplaceDialog.new(@script, true)
729
+ @replace_dialog.connect(SIGNAL('find_next()')) { dialog_find(@replace_dialog) }
730
+ @replace_dialog.connect(SIGNAL('replace()')) { dialog_replace(@replace_dialog) }
731
+ @replace_dialog.connect(SIGNAL('replace_all()')) { dialog_replace_all(@replace_dialog) }
732
+ end
733
+ @replace_dialog.show
734
+ @replace_dialog.raise
735
+ @replace_dialog.activateWindow
736
+ end
737
+
738
+ def dialog_find(dialog)
739
+ found = @script.find(dialog.find_text, dialog.find_flags)
740
+ if not found and dialog.wrap_around?
741
+ cursor = @script.textCursor
742
+ if dialog.find_up?
743
+ cursor.movePosition(Qt::TextCursor::End)
744
+ else
745
+ cursor.movePosition(Qt::TextCursor::Start)
746
+ end
747
+ @script.setTextCursor(cursor)
748
+ @script.find(dialog.find_text, dialog.find_flags)
749
+ end
750
+ end
751
+
752
+ def dialog_replace(dialog)
753
+ if @script.textCursor.hasSelection &&
754
+ @script.textCursor.selectedText == dialog.find_text
755
+ found = true
756
+ else
757
+ found = @script.find(dialog.find_text, dialog.find_flags)
758
+ if not found and dialog.wrap_around?
759
+ cursor = @script.textCursor
760
+ if dialog.find_up?
761
+ cursor.movePosition(Qt::TextCursor::End)
762
+ else
763
+ cursor.movePosition(Qt::TextCursor::Start)
764
+ end
765
+ @script.setTextCursor(cursor)
766
+ found = @script.find(dialog.find_text, dialog.find_flags)
767
+ end
768
+ end
769
+ if found
770
+ @script.textCursor.removeSelectedText
771
+ @script.textCursor.insertText(dialog.replace_text)
772
+ cursor = @script.textCursor
773
+ cursor.setPosition(cursor.position-1)
774
+ cursor.select(Qt::TextCursor::WordUnderCursor)
775
+ @script.setTextCursor(cursor)
776
+ end
777
+ end
778
+
779
+ def dialog_replace_all(dialog)
780
+ cursor = @script.textCursor
781
+ cursor.movePosition(Qt::TextCursor::Start)
782
+ @script.setTextCursor(cursor)
783
+
784
+ while (@script.find(dialog.find_text, dialog.find_flags) == true)
785
+ @script.textCursor.removeSelectedText
786
+ @script.textCursor.insertText(dialog.replace_text)
787
+ end
788
+ end
789
+
790
+ ##################################################################################
791
+ # Implement Script functionality in the frame (run selection, run from cursor, etc
792
+ ##################################################################################
793
+ def run_selection
794
+ unless self.class.running?()
795
+ selection = @script.selected_lines
796
+ if selection
797
+ start_line_number = @script.selection_start_line
798
+ end_line_number = @script.selection_end_line
799
+ scriptrunner_puts "Running script lines #{start_line_number+1}-#{end_line_number+1}: #{File.basename(@filename)}"
800
+ handle_output_io()
801
+ run_text(selection, start_line_number)
802
+ end
803
+ end
804
+ end
805
+
806
+ def run_selection_while_paused
807
+ current_script = @tab_book.tab(@tab_book.currentIndex)
808
+ selection = current_script.selected_lines
809
+ if selection
810
+ start_line_number = current_script.selection_start_line
811
+ end_line_number = current_script.selection_end_line
812
+ scriptrunner_puts "Debug: Running selected lines #{start_line_number+1}-#{end_line_number+1}: #{@tab_book.tabText(@tab_book.currentIndex)}"
813
+ handle_output_io()
814
+ dialog = ScriptRunnerDialog.new(self, 'Executing Selected Lines While Paused')
815
+ dialog.execute_text_and_close_on_complete(selection, @script_binding)
816
+ handle_output_io()
817
+ end
818
+ end
819
+
820
+ def run_from_cursor
821
+ unless self.class.running?()
822
+ line_number = @script.selection_start_line
823
+ text = @script.toPlainText.split("\n")[line_number..-1].join("\n")
824
+ scriptrunner_puts "Running script from line #{line_number}: #{File.basename(@filename)}"
825
+ handle_output_io()
826
+ run_text(text, line_number)
827
+ end
828
+ end
829
+
830
+ def ruby_syntax_check_selection
831
+ unless self.class.running?()
832
+ selection = @script.selected_lines
833
+ ruby_syntax_check_text(selection) if selection
834
+ end
835
+ end
836
+
837
+ def ruby_syntax_check_text(text)
838
+ unless self.class.running?()
839
+ check_process = IO.popen("ruby -c -rubygems 2>&1", 'r+')
840
+ check_process.write("require 'cosmos'; require 'cosmos/script'; " + text)
841
+ check_process.close_write
842
+ results = check_process.gets
843
+ check_process.close
844
+ if results
845
+ if results =~ /Syntax OK/
846
+ Qt::MessageBox.information(self, 'Syntax Check Successful', results)
847
+ else
848
+ # Results is a string like this: ":2: syntax error ..."
849
+ # Normally the procedure comes before the first colon but since we
850
+ # are writing to the process this is blank so we throw it away
851
+ _, line_no, error = results.split(':')
852
+ Qt::MessageBox.warning(self,
853
+ 'Syntax Check Failed',
854
+ "Error on line #{line_no}: #{error.strip}")
855
+ end
856
+ else
857
+ Qt::MessageBox.critical(self,
858
+ 'Syntax Check Exception',
859
+ 'Ruby syntax check unexpectedly returned nil')
860
+ end
861
+ end
862
+ end
863
+
864
+ def mnemonic_check_selection
865
+ unless self.class.running?()
866
+ selection = @script.selected_lines
867
+ mnemonic_check_text(selection, @script.selection_start_line+1) if selection
868
+ end
869
+ end
870
+
871
+ def mnemonic_check_text(text, start_line_number = 1)
872
+ results = []
873
+ line_number = start_line_number
874
+ text.each_line do |line|
875
+ if line =~ /\(/
876
+ result = nil
877
+ keyword = line.split('(')[0].split[-1]
878
+ if CMD_KEYWORDS.include? keyword
879
+ result = mnemonic_check_cmd_line(keyword, line_number, line)
880
+ elsif TLM_KEYWORDS.include? keyword
881
+ result = mnemonic_check_tlm_line(keyword, line_number, line)
882
+ elsif SET_TLM_KEYWORDS.include? keyword
883
+ result = mnemonic_check_set_tlm_line(keyword, line_number, line)
884
+ elsif CHECK_KEYWORDS.include? keyword
885
+ result = mnemonic_check_check_line(keyword, line_number, line)
886
+ end
887
+ results << result if result
888
+ end
889
+ line_number += 1
890
+ end
891
+
892
+ if results.empty?
893
+ Qt::MessageBox.information(self,
894
+ 'Mnemonic Check Successful',
895
+ 'Mnemonic Check Found No Errors')
896
+ else
897
+ dialog = Qt::Dialog.new(self) do |box|
898
+ box.setWindowTitle('Mnemonic Check Failed')
899
+ text = Qt::PlainTextEdit.new
900
+ text.setReadOnly(true)
901
+ text.setPlainText(results.join("\n"))
902
+ frame = Qt::VBoxLayout.new(box)
903
+ ok = Qt::PushButton.new('Ok')
904
+ ok.setDefault(true)
905
+ ok.connect(SIGNAL('clicked(bool)')) { box.accept }
906
+ frame.addWidget(text)
907
+ frame.addWidget(ok)
908
+ end
909
+ dialog.exec
910
+ dialog.dispose
911
+ end
912
+ end
913
+
914
+ ######################################################
915
+ # Implement the debug capability
916
+ ######################################################
917
+ def toggle_debug(debug = nil)
918
+ if debug.nil?
919
+ if @debug_frame
920
+ hide_debug()
921
+ else
922
+ show_debug()
923
+ end
924
+ else
925
+ if debug
926
+ if !@debug_frame
927
+ show_debug()
928
+ end
929
+ else
930
+ if @debug_frame
931
+ hide_debug()
932
+ end
933
+ end
934
+ end
935
+ end
936
+
937
+ def show_debug
938
+ unless @debug_frame
939
+ @script.enable_breakpoints = true
940
+ if @tab_book_shown
941
+ if @tab_book.count > 0
942
+ (0..(@tab_book.count - 1)).each do |index|
943
+ @tab_book.tab(index).enable_breakpoints = true
944
+ end
945
+ end
946
+ end
947
+
948
+ @debug_frame = Qt::HBoxLayout.new
949
+ @debug_frame.setContentsMargins(0,0,0,0)
950
+ @debug_frame_label = Qt::Label.new("Debug:")
951
+ @debug_frame.addWidget(@debug_frame_label)
952
+ @debug_text = CompletionLineEdit.new(self)
953
+ @debug_text.setFocus(Qt::OtherFocusReason)
954
+ @debug_text.keyPressCallback = lambda { |event|
955
+ case event.key
956
+ when Qt::Key_Return, Qt::Key_Enter
957
+ begin
958
+ debug_text = @debug_text.toPlainText
959
+ @debug_history.unshift(debug_text)
960
+ @debug_history_index = 0
961
+ @debug_text.setPlainText('')
962
+ scriptrunner_puts "Debug: #{debug_text}"
963
+ handle_output_io()
964
+ if not running?
965
+ # Capture STDOUT and STDERR
966
+ $stdout.add_stream(@output_io)
967
+ $stderr.add_stream(@output_io)
968
+ end
969
+
970
+ if @script_binding
971
+ eval(debug_text, @script_binding, 'debug', 1)
972
+ else
973
+ Object.class_eval(debug_text, 'debug', 1)
974
+ end
975
+ handle_output_io()
976
+ rescue Exception => error
977
+ if error.class == DRb::DRbConnError
978
+ Logger.error("Error Connecting to Command and Telemetry Server")
979
+ else
980
+ Logger.error(error.class.to_s.split('::')[-1] + ' : ' + error.message)
981
+ end
982
+ handle_output_io()
983
+ ensure
984
+ if not running?
985
+ # Capture STDOUT and STDERR
986
+ $stdout.remove_stream(@output_io)
987
+ $stderr.remove_stream(@output_io)
988
+ end
989
+ end
990
+ when Qt::Key_Up
991
+ if @debug_history.length > 0
992
+ @debug_text.setPlainText(@debug_history[@debug_history_index])
993
+ @debug_history_index += 1
994
+ if @debug_history_index == @debug_history.length
995
+ @debug_history_index = @debug_history.length-1
996
+ end
997
+ end
998
+ when Qt::Key_Down
999
+ if @debug_history.length > 0
1000
+ @debug_text.setPlainText(@debug_history[@debug_history_index])
1001
+ @debug_history_index -= 1
1002
+ @debug_history_index = 0 if @debug_history_index < 0
1003
+ end
1004
+ when Qt::Key_Escape
1005
+ @debug_text.setPlainText("")
1006
+ end
1007
+ }
1008
+
1009
+ @debug_frame.addWidget(@debug_text)
1010
+
1011
+ @toggle_button = Qt::PushButton.new('Toggle Run/Step')
1012
+ @debug_frame.addWidget(@toggle_button)
1013
+ @toggle_button.connect(SIGNAL('clicked(bool)')) do
1014
+ if @@step_mode
1015
+ scriptrunner_puts "Debug: run_mode"
1016
+ handle_output_io()
1017
+ self.class.step_mode = false
1018
+ else
1019
+ scriptrunner_puts "Debug: step_mode"
1020
+ handle_output_io()
1021
+ self.class.step_mode = true
1022
+ end
1023
+ end
1024
+
1025
+ @return_button = Qt::PushButton.new('Insert Return')
1026
+ @debug_frame.addWidget(@return_button)
1027
+ @return_button.connect(SIGNAL('clicked(bool)')) do
1028
+ scriptrunner_puts "Debug: insert_return(); ScriptRunnerFrame.instance.go()"
1029
+ handle_output_io()
1030
+ insert_return()
1031
+ go()
1032
+ end
1033
+
1034
+ @bottom_frame.layout.addLayout(@debug_frame)
1035
+ end
1036
+ end
1037
+
1038
+ def hide_debug
1039
+ # Since we're disabling debug, clear the breakpoints and disable them
1040
+ ScriptRunnerFrame.clear_breakpoints()
1041
+ @script.clear_breakpoints
1042
+ @script.enable_breakpoints = false
1043
+ if @tab_book_shown
1044
+ if @tab_book.count > 0
1045
+ (0..(@tab_book.count - 1)).each do |index|
1046
+ @tab_book.tab(index).enable_breakpoints = false
1047
+ end
1048
+ end
1049
+ end
1050
+ # Remove the debug frame
1051
+ @bottom_frame.layout.takeAt(@bottom_frame.layout.count - 1) if @debug_frame
1052
+ @debug_frame.removeAll
1053
+ @debug_frame.dispose
1054
+ @debug_frame = nil
1055
+ end
1056
+
1057
+ def self.breakpoint(filename, line_number)
1058
+ filename = File.basename(filename)
1059
+ @@breakpoints[filename] ||= {}
1060
+ @@breakpoints[filename][line_number] = true
1061
+ end
1062
+
1063
+ def self.clear_breakpoint(filename, line_number)
1064
+ filename = File.basename(filename)
1065
+ @@breakpoints[filename] ||= {}
1066
+ @@breakpoints[filename].delete(line_number) if @@breakpoints[filename][line_number]
1067
+ end
1068
+
1069
+ def self.clear_breakpoints(filename = nil)
1070
+ filename = File.basename(filename) unless filename.nil?
1071
+ if filename == nil or filename.empty?
1072
+ @@breakpoints = {}
1073
+ else
1074
+ @@breakpoints[filename] = {}
1075
+ end
1076
+ end
1077
+
1078
+ def select_tab_and_destroy_tabs_after_index(index)
1079
+ Qt.execute_in_main_thread(true) do
1080
+ if @tab_book_shown
1081
+ @tab_book.setCurrentIndex(index)
1082
+ @active_script = @tab_book.tab(@tab_book.currentIndex)
1083
+
1084
+ first_to_remove = index + 1
1085
+ last_to_remove = @call_stack.length - 1
1086
+
1087
+ last_to_remove.downto(first_to_remove) do |tab_index|
1088
+ tab = @tab_book.tab(tab_index)
1089
+ @tab_book.removeTab(tab_index)
1090
+ tab.dispose
1091
+ end
1092
+
1093
+ @call_stack = @call_stack[0..index]
1094
+ @current_file = @call_stack[index]
1095
+ end
1096
+ end
1097
+ end
1098
+
1099
+ def toggle_disconnect(config_file)
1100
+ if get_cmd_tlm_disconnect
1101
+ set_cmd_tlm_disconnect(false)
1102
+ self.parent.setPalette(Cosmos::DEFAULT_PALETTE)
1103
+ else
1104
+ dialog = Qt::Dialog.new(self, Qt::WindowTitleHint | Qt::WindowSystemMenuHint)
1105
+ dialog.setWindowTitle(tr("Server Config File"))
1106
+ dialog_layout = Qt::VBoxLayout.new
1107
+
1108
+ chooser = FileChooser.new(self, "Config File", config_file, 'Select',
1109
+ File.join('config', 'tools', 'cmd_tlm_server', config_file))
1110
+ chooser.callback = lambda do |filename|
1111
+ chooser.filename = File.basename(filename)
1112
+ end
1113
+ dialog_layout.addWidget(chooser)
1114
+
1115
+ button_layout = Qt::HBoxLayout.new
1116
+ ok = Qt::PushButton.new("Ok")
1117
+ ok.setDefault(true)
1118
+ ok.connect(SIGNAL('clicked()')) do
1119
+ dialog.accept()
1120
+ end
1121
+ button_layout.addWidget(ok)
1122
+ cancel = Qt::PushButton.new("Cancel")
1123
+ cancel.connect(SIGNAL('clicked()')) do
1124
+ dialog.reject()
1125
+ end
1126
+ button_layout.addWidget(cancel)
1127
+ dialog_layout.addLayout(button_layout)
1128
+
1129
+ dialog.setLayout(dialog_layout)
1130
+ if dialog.exec == Qt::Dialog::Accepted
1131
+ config_file = chooser.filename
1132
+ self.parent.setPalette(Cosmos::RED_PALETTE)
1133
+ Splash.execute(self) do |splash|
1134
+ ConfigParser.splash = splash
1135
+ splash.message = "Initializing Command and Telemetry Server"
1136
+ set_cmd_tlm_disconnect(true, config_file)
1137
+ ConfigParser.splash = nil
1138
+ end
1139
+ end
1140
+ dialog.dispose
1141
+ end
1142
+ config_file
1143
+ end
1144
+
1145
+ def current_backtrace
1146
+ trace = []
1147
+ Qt.execute_in_main_thread(true) do
1148
+ if @@run_thread
1149
+ temp_trace = @@run_thread.backtrace
1150
+ cosmos_lib = Regexp.new(File.join(Cosmos::PATH, 'lib'))
1151
+ temp_trace.each do |line|
1152
+ next if line =~ cosmos_lib
1153
+ trace << line
1154
+ end
1155
+ end
1156
+ end
1157
+ trace
1158
+ end
1159
+
1160
+ def scriptrunner_puts(string)
1161
+ puts Time.now.formatted + " (SCRIPTRUNNER): " + string
1162
+ end
1163
+
1164
+ def handle_output_io(filename = @current_filename, line_number = @current_line_number)
1165
+ @output_time = Time.now
1166
+ Qt.execute_in_main_thread(true) do
1167
+ if @output_io.string[-1..-1] == "\n"
1168
+ time_formatted = Time.now.formatted
1169
+ lines_to_write = ''
1170
+ out_line_number = line_number.to_s
1171
+ out_filename = File.basename(filename) if filename
1172
+
1173
+ # Build each line to write
1174
+ string = @output_io.string.clone
1175
+ @output_io.string = @output_io.string[string.length..-1]
1176
+ line_count = 0
1177
+ string.each_line do |out_line|
1178
+ color = nil
1179
+ if out_line[0..1] == '20' and out_line[10] == ' ' and out_line[23..24] == ' ('
1180
+ line_to_write = out_line
1181
+ else
1182
+ if filename
1183
+ line_to_write = time_formatted + " (#{out_filename}:#{out_line_number}): " + out_line
1184
+ else
1185
+ line_to_write = time_formatted + " (SCRIPTRUNNER): " + out_line
1186
+ color = Cosmos::BLUE
1187
+ end
1188
+ end
1189
+ @output.add_formatted_text(line_to_write, color)
1190
+ lines_to_write += line_to_write
1191
+
1192
+ line_count += 1
1193
+ if line_count > 1000
1194
+ out_line = "ERROR: Too much written to stdout. Truncating output to 1000 lines.\n"
1195
+ if filename
1196
+ line_to_write = time_formatted + " (#{out_filename}:#{out_line_number}): " + out_line
1197
+ else
1198
+ line_to_write = time_formatted + " (SCRIPTRUNNER): " + out_line
1199
+ end
1200
+ @output.addText(line_to_write, Cosmos::RED)
1201
+ lines_to_write += line_to_write
1202
+ break
1203
+ end
1204
+ end # string.each_line
1205
+
1206
+ # Actually add to the GUI
1207
+ @output.flush
1208
+
1209
+ # Add to the message log
1210
+ if @filename.empty?
1211
+ @message_log ||= MessageLog.new("sr_untitled")
1212
+ else
1213
+ @message_log ||= MessageLog.new("sr_#{File.basename(@filename).split('.')[0]}")
1214
+ end
1215
+ @message_log.write(lines_to_write)
1216
+ end
1217
+ end
1218
+ end
1219
+
1220
+ def graceful_kill
1221
+ # Just to avoid warning
1222
+ end
1223
+
1224
+ protected
1225
+
1226
+ def initialize_variables
1227
+ @@file_cache = {}
1228
+ @@error = nil
1229
+ @go = false
1230
+ if @@step_mode
1231
+ @pause = true
1232
+ else
1233
+ @pause = false
1234
+ end
1235
+ @stop = false
1236
+ @retry_needed = false
1237
+ @use_instrumentation = true
1238
+ @active_script = @script
1239
+ @call_stack = []
1240
+ @pre_line_time = Time.now
1241
+ @current_file = @filename
1242
+ @exceptions = nil
1243
+ @script_binding = nil
1244
+ @inline_eval = nil
1245
+ @current_filename = nil
1246
+ @current_line_number = 0
1247
+
1248
+ @script.stop_highlight
1249
+ @call_stack.push(@current_file.dup)
1250
+ end
1251
+
1252
+ def wait_for_go_or_stop(error = nil)
1253
+ @go = false
1254
+ sleep(0.01) until @go or @stop
1255
+ @go = false
1256
+ mark_running()
1257
+ Kernel.raise StopScript if @stop
1258
+ Kernel.raise error if error and !@continue_after_error
1259
+ end
1260
+
1261
+ def wait_for_go_or_stop_or_retry(error = nil)
1262
+ @go = false
1263
+ sleep(0.01) until @go or @stop or @retry_needed
1264
+ @go = false
1265
+ mark_running()
1266
+ Kernel.raise StopScript if @stop
1267
+ Kernel.raise error if error and !@continue_after_error
1268
+ end
1269
+
1270
+ def mark_running
1271
+ Qt.execute_in_main_thread(true) do
1272
+ @run_callback.call(self) if @run_callback
1273
+ @active_script.rehighlight
1274
+ @realtime_button_bar.state = 'Running'
1275
+ @realtime_button_bar.start_button.setText('Go')
1276
+ @realtime_button_bar.pause_button.setText('Pause')
1277
+ end
1278
+ end
1279
+
1280
+ def mark_paused
1281
+ Qt.execute_in_main_thread(true) do
1282
+ @pause_callback.call(self) if @pause_callback
1283
+ @active_script.rehighlight('lightblue')
1284
+ @realtime_button_bar.state = 'Paused'
1285
+ end
1286
+ end
1287
+
1288
+ def mark_error
1289
+ Qt.execute_in_main_thread(true) do
1290
+ @error_callback.call(self) if @error_callback
1291
+ @active_script.rehighlight('pink')
1292
+ @realtime_button_bar.state = 'Error'
1293
+ @realtime_button_bar.pause_button.setText('Retry')
1294
+ end
1295
+ end
1296
+
1297
+ def mark_stopped
1298
+ stop_message_log()
1299
+ Qt.execute_in_main_thread(true) do
1300
+ @realtime_button_bar.start_button.setText('Start')
1301
+ @realtime_button_bar.pause_button.setText('Pause')
1302
+ @realtime_button_bar.state = 'Stopped'
1303
+ @stop_callback.call(self) if @stop_callback
1304
+ end
1305
+ end
1306
+
1307
+ def mark_breakpoint
1308
+ Qt.execute_in_main_thread(true) do
1309
+ @active_script.rehighlight('tan')
1310
+ @realtime_button_bar.state = 'Breakpoint'
1311
+ end
1312
+ end
1313
+
1314
+ def run_text(text,
1315
+ line_offset = 0,
1316
+ text_binding = nil,
1317
+ close_on_complete = false)
1318
+ @run_callback.call(self) if @run_callback
1319
+ @realtime_button_bar.start_button.setEnabled(true)
1320
+ initialize_variables()
1321
+ create_tabs()
1322
+ @line_offset = line_offset
1323
+ @script.setReadOnly(true)
1324
+ @realtime_button_bar.start_button.setText(' Go ')
1325
+ @realtime_button_bar.state = 'Running'
1326
+
1327
+ saved_instance = @@instance
1328
+ saved_run_thread = @@run_thread
1329
+ @@instance = self
1330
+ @@run_thread = Thread.new do
1331
+ uncaught_exception = false
1332
+ begin
1333
+ # Capture STDOUT and STDERR
1334
+ $stdout.add_stream(@output_io)
1335
+ $stderr.add_stream(@output_io)
1336
+
1337
+ scriptrunner_puts "Starting script: #{File.basename(@filename)}" unless close_on_complete
1338
+ handle_output_io()
1339
+
1340
+ # Start Limits Monitoring
1341
+ @@limits_monitor_thread = Thread.new { limits_monitor_thread() } if @@monitor_limits and !@@limits_monitor_thread
1342
+
1343
+ # Start Output Thread
1344
+ @@output_thread = Thread.new { output_thread() } unless @@output_thread
1345
+
1346
+ # Check top level cache
1347
+ if @top_level_instrumented_cache &&
1348
+ (@top_level_instrumented_cache[1] == line_offset) &&
1349
+ (@top_level_instrumented_cache[2] == @filename) &&
1350
+ (@top_level_instrumented_cache[0] == text)
1351
+ # Use the instrumented cache
1352
+ instrumented_script = @top_level_instrumented_cache[3]
1353
+ else
1354
+ # Instrument the script
1355
+ if text_binding
1356
+ instrumented_script = self.class.instrument_script(text, @filename, false)
1357
+ else
1358
+ instrumented_script = self.class.instrument_script(text, @filename, true)
1359
+ end
1360
+ @top_level_instrumented_cache = [text, line_offset, @filename, instrumented_script]
1361
+ end
1362
+
1363
+ # Execute the script with warnings disabled
1364
+ Cosmos.disable_warnings do
1365
+ @pre_line_time = Time.now
1366
+ Cosmos.set_working_dir do
1367
+ if text_binding
1368
+ eval(instrumented_script, text_binding, @filename, 1)
1369
+ else
1370
+ Object.class_eval(instrumented_script, @filename, 1)
1371
+ end
1372
+ end
1373
+ end
1374
+
1375
+ scriptrunner_puts "Script completed: #{File.basename(@filename)}" unless close_on_complete
1376
+ handle_output_io()
1377
+
1378
+ rescue Exception => error
1379
+ if error.class == StopScript or error.class == SkipTestCase
1380
+ scriptrunner_puts "Script stopped: #{File.basename(@filename)}"
1381
+ handle_output_io()
1382
+ else
1383
+ uncaught_exception = true
1384
+ filename, line_number = error.source
1385
+ handle_exception(error, true, filename, line_number)
1386
+ scriptrunner_puts "Exception in Control Statement - Script stopped: #{File.basename(@filename)}"
1387
+ handle_output_io()
1388
+ Qt.execute_in_main_thread(true) {@script.rehighlight('red') }
1389
+ end
1390
+ ensure
1391
+ # Change Go Button to Start Button and remove highlight
1392
+ Qt.execute_in_main_thread(true) do
1393
+ # Stop Capturing STDOUT and STDERR
1394
+ # Check for remove_stream because if the tool is quitting the
1395
+ # Cosmos::restore_io may have been called which sets $stdout and
1396
+ # $stderr to the IO constant
1397
+ $stdout.remove_stream(@output_io) if $stdout.respond_to? :remove_stream
1398
+ $stderr.remove_stream(@output_io) if $stderr.respond_to? :remove_stream
1399
+
1400
+ # Clear run thread and instance to indicate we are no longer running
1401
+ @@instance = saved_instance
1402
+ @@run_thread = saved_run_thread
1403
+ @active_script = @script
1404
+ @script_binding = nil
1405
+ @current_filename = nil
1406
+ @current_line_number = 0
1407
+ if @@limits_monitor_thread and not @@instance
1408
+ @@cancel_limits = true
1409
+ @@limits_sleeper.cancel
1410
+ Qt::CoreApplication.processEvents()
1411
+ Cosmos.kill_thread(self, @@limits_monitor_thread)
1412
+ @@limits_monitor_thread = nil
1413
+ end
1414
+ if @@output_thread and not @@instance
1415
+ @@cancel_output = true
1416
+ @@output_sleeper.cancel
1417
+ Qt::CoreApplication.processEvents()
1418
+ Cosmos.kill_thread(self, @@output_thread)
1419
+ @@output_thread = nil
1420
+ end
1421
+
1422
+ @script.setReadOnly(false)
1423
+ @script.stop_highlight unless uncaught_exception
1424
+ select_tab_and_destroy_tabs_after_index(0)
1425
+ remove_tabs()
1426
+ unless @allow_start
1427
+ @realtime_button_bar.start_button.setEnabled(false)
1428
+ @script.setReadOnly(true)
1429
+ end
1430
+ mark_stopped()
1431
+ if close_on_complete
1432
+ self.parent.done(0)
1433
+ end
1434
+ end
1435
+ end
1436
+ end
1437
+ end
1438
+
1439
+ def handle_potential_tab_change(filename)
1440
+ # Make sure the correct file is shown in script runner
1441
+ if @current_file != filename and @tab_book_shown
1442
+ Qt.execute_in_main_thread(true) do
1443
+ if @call_stack.include?(filename)
1444
+ index = @call_stack.index(filename)
1445
+ select_tab_and_destroy_tabs_after_index(index)
1446
+ else # new file
1447
+ # Create new tab
1448
+ new_script = create_ruby_editor()
1449
+ @tab_book.addTab(new_script, ' ' + File.basename(filename) + ' ')
1450
+
1451
+ @call_stack.push(filename.dup)
1452
+
1453
+ # Switch to new tab
1454
+ @tab_book.setCurrentIndex(@tab_book.count - 1)
1455
+ @active_script = new_script
1456
+ load_file_into_script(filename)
1457
+ new_script.setReadOnly(true)
1458
+ end
1459
+
1460
+ @current_file = filename
1461
+ end
1462
+ end
1463
+ end
1464
+
1465
+ def show_active_tab
1466
+ @tab_book.setCurrentIndex(@call_stack.length - 1) if @tab_book_shown
1467
+ end
1468
+
1469
+ def handle_pause(filename, line_number)
1470
+ filename = File.basename(filename)
1471
+ breakpoint = false
1472
+ breakpoint = true if @@breakpoints[filename] and @@breakpoints[filename][line_number]
1473
+ if @pause
1474
+ @pause = false unless @@step_mode
1475
+ if breakpoint
1476
+ perform_breakpoint(filename, line_number)
1477
+ else
1478
+ perform_pause()
1479
+ end
1480
+ else
1481
+ perform_breakpoint(filename, line_number) if breakpoint
1482
+ end
1483
+ end
1484
+
1485
+ def handle_line_delay
1486
+ if @@line_delay > 0.0
1487
+ sleep_time = @@line_delay - (Time.now - @pre_line_time)
1488
+ sleep(sleep_time) if sleep_time > 0.0
1489
+ end
1490
+ @pre_line_time = Time.now
1491
+ end
1492
+
1493
+ def continue_without_pausing_on_errors?
1494
+ if !@@pause_on_error
1495
+ if Qt::MessageBox.warning(self, "Warning", "If an error occurs, the script will not pause and will run to completion. Continue?", Qt::MessageBox::Yes | Qt::MessageBox::No, Qt::MessageBox::Yes) == Qt::MessageBox::No
1496
+ return false
1497
+ end
1498
+ end
1499
+ true
1500
+ end
1501
+
1502
+ def handle_start_go_button
1503
+ scriptrunner_puts "User pressed #{@realtime_button_bar.start_button.text.strip}"
1504
+ handle_output_io()
1505
+ @realtime_button_bar.start_button.clear_focus()
1506
+
1507
+ if running?()
1508
+ show_active_tab()
1509
+ go()
1510
+ else
1511
+ if @allow_start
1512
+ run() if continue_without_pausing_on_errors?()
1513
+ end
1514
+ end
1515
+ end
1516
+
1517
+ def handle_pause_retry_button
1518
+ scriptrunner_puts "User pressed #{@realtime_button_bar.pause_button.text.strip}"
1519
+ handle_output_io()
1520
+ @realtime_button_bar.pause_button.clear_focus()
1521
+ show_active_tab() if running?
1522
+ if @realtime_button_bar.pause_button.text.to_s == 'Pause'
1523
+ pause()
1524
+ else
1525
+ retry_needed()
1526
+ end
1527
+ end
1528
+
1529
+ def handle_stop_button
1530
+ scriptrunner_puts "User pressed #{@realtime_button_bar.stop_button.text.strip}"
1531
+ handle_output_io()
1532
+ @realtime_button_bar.stop_button.clear_focus()
1533
+
1534
+ if @stop
1535
+ # If we're already stopped and they click Stop again, kill the run
1536
+ # thread. This will break any ruby sleeps or other code blocks.
1537
+ ScriptRunnerFrame.stop!
1538
+ handle_output_io()
1539
+ else
1540
+ @stop = true
1541
+ end
1542
+ if !running?()
1543
+ # Stop highlight if there was a red syntax error
1544
+ @script.stop_highlight
1545
+ end
1546
+ end
1547
+
1548
+ def handle_exception(error, fatal, filename = nil, line_number = 0)
1549
+ @error_callback.call(self) if @error_callback
1550
+
1551
+ @exceptions ||= []
1552
+ @exceptions << error
1553
+ @@error = error
1554
+
1555
+ if error.class == DRb::DRbConnError
1556
+ Logger.error("Error Connecting to Command and Telemetry Server")
1557
+ elsif error.class == CheckError
1558
+ Logger.error(error.message)
1559
+ else
1560
+ Logger.error(error.class.to_s.split('::')[-1] + ' : ' + error.message)
1561
+ end
1562
+ Logger.error(error.backtrace.join("\n")) if @@show_backtrace
1563
+ handle_output_io(filename, line_number)
1564
+
1565
+ Kernel.raise error if !@@pause_on_error and !@continue_after_error and !fatal
1566
+
1567
+ if !fatal and @@pause_on_error
1568
+ mark_error()
1569
+ wait_for_go_or_stop_or_retry(error)
1570
+ end
1571
+
1572
+ if @retry_needed
1573
+ @retry_needed = false
1574
+ true
1575
+ else
1576
+ false
1577
+ end
1578
+ end
1579
+
1580
+ # Right click context_menu for the script
1581
+ def context_menu(point)
1582
+ if @tab_book_shown
1583
+ current_script = @tab_book.tab(@tab_book.currentIndex)
1584
+ else
1585
+ current_script = @script
1586
+ end
1587
+ menu = current_script.context_menu(point)
1588
+ menu.addSeparator()
1589
+ if not self.class.running?
1590
+ exec_selected_action = Qt::Action.new(tr("Execute Selected Lines"), self)
1591
+ exec_selected_action.statusTip = tr("Execute the selected lines as a standalone script")
1592
+ exec_selected_action.connect(SIGNAL('triggered()')) { run_selection() }
1593
+ menu.addAction(exec_selected_action)
1594
+
1595
+ exec_cursor_action = Qt::Action.new(tr("Execute From Cursor"), self)
1596
+ exec_cursor_action.statusTip = tr("Execute the script starting at the line containing the cursor")
1597
+ exec_cursor_action.connect(SIGNAL('triggered()')) { run_from_cursor() }
1598
+ menu.addAction(exec_cursor_action)
1599
+
1600
+ menu.addSeparator()
1601
+
1602
+ if RUBY_VERSION.split('.')[0].to_i > 1
1603
+ syntax_action = Qt::Action.new(tr("Ruby Syntax Check Selected Lines"), self)
1604
+ syntax_action.statusTip = tr("Check the selected lines for valid Ruby syntax")
1605
+ syntax_action.connect(SIGNAL('triggered()')) { ruby_syntax_check_selection() }
1606
+ menu.addAction(syntax_action)
1607
+ end
1608
+
1609
+ mnemonic_action = Qt::Action.new(tr("Mnemonic Check Selected Lines"), self)
1610
+ mnemonic_action.statusTip = tr("Check the selected lines for valid targets, packets, mnemonics and parameters")
1611
+ mnemonic_action.connect(SIGNAL('triggered()')) { mnemonic_check_selection() }
1612
+ menu.addAction(mnemonic_action)
1613
+
1614
+ elsif running?() and @realtime_button_bar.state != 'Running'
1615
+ exec_selected_action = Qt::Action.new(tr("Execute Selected Lines While Paused"), self)
1616
+ exec_selected_action.statusTip = tr("Execute the selected lines as a standalone script")
1617
+ exec_selected_action.connect(SIGNAL('triggered()')) { run_selection_while_paused() }
1618
+ menu.addAction(exec_selected_action)
1619
+ end
1620
+ menu.exec(current_script.mapToGlobal(point))
1621
+ menu.dispose
1622
+ end
1623
+
1624
+ def load_file_into_script(filename)
1625
+ cached = @@file_cache[filename]
1626
+ if cached
1627
+ @active_script.setPlainText(cached.gsub("\r", ''))
1628
+ else
1629
+ @active_script.setPlainText(File.read(filename).gsub("\r", ''))
1630
+ end
1631
+ mark_breakpoints(filename)
1632
+
1633
+ @active_script.stop_highlight
1634
+ end
1635
+
1636
+ def mark_breakpoints(filename)
1637
+ @active_script.clear_breakpoints
1638
+ breakpoints = @@breakpoints[File.basename(filename)]
1639
+ if breakpoints
1640
+ breakpoints.each do |line_number, present|
1641
+ @active_script.breakpoint(line_number) if present
1642
+ end
1643
+ end
1644
+ end
1645
+
1646
+ def redirect_io
1647
+ # Redirect Standard Output and Standard Error
1648
+ $stdout = Stdout.instance
1649
+ $stderr = Stderr.instance
1650
+
1651
+ # Disable outputting to default io file descriptors
1652
+ $stdout.remove_default_io
1653
+ $stderr.remove_default_io
1654
+
1655
+ Logger.level = Logger::INFO
1656
+ Logger::INFO_SEVERITY_STRING.replace('')
1657
+ Logger::WARN_SEVERITY_STRING.replace('<Y> WARN:')
1658
+ Logger::ERROR_SEVERITY_STRING.replace('<R> ERROR:')
1659
+ end
1660
+
1661
+ def create_tabs
1662
+ tab_text = @default_tab_text
1663
+ tab_text = ' ' + File.basename(@filename) + ' ' unless @filename.empty?
1664
+ @tab_book.addTab(@script, tab_text)
1665
+ @top_frame.insertWidget(0, @tab_book)
1666
+ @tab_book_shown = true
1667
+ end
1668
+
1669
+ def remove_tabs
1670
+ @top_frame.takeAt(0) # Remove the @tab_book from the layout
1671
+ @top_frame.addWidget(@script) # Add back the script
1672
+ @script.show
1673
+ @tab_book_shown = false
1674
+ end
1675
+
1676
+ def isolate_string(keyword, line)
1677
+ found_string = nil
1678
+
1679
+ # Find keyword
1680
+ keyword_index = line.index(keyword)
1681
+
1682
+ # Remove keyword from line
1683
+ line = line[(keyword_index + keyword.length)..-1]
1684
+
1685
+ # Find start parens
1686
+ start_parens = line.index('(')
1687
+ if start_parens
1688
+ end_parens = line[start_parens..-1].index(')')
1689
+ found_string = line[(start_parens + 1)..(end_parens + start_parens - 1)].remove_quotes if end_parens
1690
+ if keyword == 'wait' or keyword == 'wait_check'
1691
+ quote_index = found_string.rindex('"')
1692
+ quote_index = found_string.rindex("'") unless quote_index
1693
+ if quote_index
1694
+ found_string = found_string[0..quote_index].remove_quotes
1695
+ else
1696
+ found_string = nil
1697
+ end
1698
+ end
1699
+ end
1700
+ found_string
1701
+ end
1702
+
1703
+ def mnemonic_check_cmd_line(keyword, line_number, line)
1704
+ result = nil
1705
+
1706
+ # Isolate the string
1707
+ string = isolate_string(keyword, line)
1708
+ if string
1709
+ begin
1710
+ target_name, cmd_name, cmd_params = extract_fields_from_cmd_text(string)
1711
+ result = "At line #{line_number}: Unknown command: #{target_name} #{cmd_name}"
1712
+ packet = System.commands.packet(target_name, cmd_name)
1713
+ Kernel.raise "Command not found" unless packet
1714
+ cmd_params.each do |param_name, param_value|
1715
+ result = "At line #{line_number}: Unknown command parameter: #{target_name} #{cmd_name} #{param_name}"
1716
+ packet.get_item(param_name)
1717
+ end
1718
+ result = nil
1719
+ rescue
1720
+ if result
1721
+ if string.index('=>')
1722
+ # Assume alternative syntax
1723
+ result = nil
1724
+ end
1725
+ else
1726
+ result = "At line #{line_number}: Potentially malformed command: #{line}"
1727
+ end
1728
+ end
1729
+ end
1730
+
1731
+ result
1732
+ end
1733
+
1734
+ def _mnemonic_check_tlm_line(keyword, line_number, line)
1735
+ result = nil
1736
+
1737
+ # Isolate the string
1738
+ string = isolate_string(keyword, line)
1739
+ if string
1740
+ begin
1741
+ target_name, packet_name, item_name = yield string
1742
+ result = "At line #{line_number}: Unknown telemetry item: #{target_name} #{packet_name} #{item_name}"
1743
+ System.telemetry.packet_and_item(target_name, packet_name, item_name)
1744
+ result = nil
1745
+ rescue
1746
+ if result
1747
+ if string.index(',')
1748
+ # Assume alternative syntax
1749
+ result = nil
1750
+ end
1751
+ else
1752
+ result = "At line #{line_number}: Potentially malformed telemetry: #{line}"
1753
+ end
1754
+ end
1755
+ end
1756
+
1757
+ result
1758
+ end
1759
+
1760
+ def mnemonic_check_tlm_line(keyword, line_number, line)
1761
+ _mnemonic_check_tlm_line(keyword, line_number, line) do |string|
1762
+ extract_fields_from_tlm_text(string)
1763
+ end
1764
+ end
1765
+
1766
+ def mnemonic_check_set_tlm_line(keyword, line_number, line)
1767
+ _mnemonic_check_tlm_line(keyword, line_number, line) do |string|
1768
+ extract_fields_from_set_tlm_text(string)
1769
+ end
1770
+ end
1771
+
1772
+ def mnemonic_check_check_line(keyword, line_number, line)
1773
+ _mnemonic_check_tlm_line(keyword, line_number, line) do |string|
1774
+ extract_fields_from_check_text(string)
1775
+ end
1776
+ end
1777
+
1778
+ def output_thread
1779
+ @@cancel_output = false
1780
+ @@output_sleeper = Sleeper.new
1781
+ begin
1782
+ loop do
1783
+ break if @@cancel_output
1784
+ handle_output_io() if (Time.now - @output_time) > 5.0
1785
+ break if @@cancel_output
1786
+ break if @@output_sleeper.sleep(1.0)
1787
+ end # loop
1788
+ rescue => error
1789
+ Qt.execute_in_main_thread(true) do
1790
+ ExceptionDialog.new(self, error, "Output Thread")
1791
+ end
1792
+ end
1793
+ end
1794
+
1795
+ def limits_monitor_thread
1796
+ @@cancel_limits = false
1797
+ @@limits_sleeper = Sleeper.new
1798
+ queue_id = nil
1799
+ begin
1800
+ loop do
1801
+ break if @@cancel_limits
1802
+ begin
1803
+ # Subscribe to limits notifications
1804
+ queue_id = subscribe_limits_events(100000) unless queue_id
1805
+
1806
+ # Get the next limits event
1807
+ break if @@cancel_limits
1808
+ begin
1809
+ type, data = get_limits_event(queue_id, true)
1810
+ rescue ThreadError
1811
+ break if @@cancel_limits
1812
+ break if @@limits_sleeper.sleep(0.5)
1813
+ next
1814
+ end
1815
+
1816
+ break if @@cancel_limits
1817
+
1818
+ # Display limits state changes
1819
+ if type == :LIMITS_CHANGE
1820
+ target_name = data[0]
1821
+ packet_name = data[1]
1822
+ item_name = data[2]
1823
+ old_limits_state = data[3]
1824
+ new_limits_state = data[4]
1825
+
1826
+ if old_limits_state == nil # Changing from nil
1827
+ if (new_limits_state != :GREEN) &&
1828
+ (new_limits_state != :GREEN_LOW) &&
1829
+ (new_limits_state != :GREEN_HIGH) &&
1830
+ (new_limits_state != :BLUE)
1831
+ msg = "#{target_name} #{packet_name} #{item_name} is #{new_limits_state.to_s}"
1832
+ case new_limits_state
1833
+ when :YELLOW, :YELLOW_LOW, :YELLOW_HIGH
1834
+ scriptrunner_puts "<Y>#{msg}"
1835
+ when :RED, :RED_LOW, :RED_HIGH
1836
+ scriptrunner_puts "<R>#{msg}"
1837
+ else
1838
+ # Print nothing
1839
+ end
1840
+ handle_output_io()
1841
+ end
1842
+ else # changing from a color
1843
+ msg = "#{target_name} #{packet_name} #{item_name} is #{new_limits_state.to_s}"
1844
+ case new_limits_state
1845
+ when :BLUE
1846
+ scriptrunner_puts "<B>#{msg}"
1847
+ when :GREEN, :GREEN_LOW, :GREEN_HIGH
1848
+ scriptrunner_puts "<G>#{msg}"
1849
+ when :YELLOW, :YELLOW_LOW, :YELLOW_HIGH
1850
+ scriptrunner_puts "<Y>#{msg}"
1851
+ when :RED, :RED_LOW, :RED_HIGH
1852
+ scriptrunner_puts "<R>#{msg}"
1853
+ else
1854
+ # Print nothing
1855
+ end
1856
+ break if @@cancel_limits
1857
+ handle_output_io()
1858
+ break if @@cancel_limits
1859
+ end
1860
+
1861
+ if @@pause_on_red && (new_limits_state == :RED ||
1862
+ new_limits_state == :RED_LOW ||
1863
+ new_limits_state == :RED_HIGH)
1864
+ break if @@cancel_limits
1865
+ pause()
1866
+ break if @@cancel_limits
1867
+ end
1868
+ end
1869
+
1870
+ rescue DRb::DRbConnError
1871
+ queue_id = nil
1872
+ break if @@cancel_limits
1873
+ break if @@limits_sleeper.sleep(1)
1874
+ end
1875
+
1876
+ end # loop
1877
+ rescue => error
1878
+ Qt.execute_in_main_thread(true) do
1879
+ ExceptionDialog.new(self, error, "Limits Monitor Thread")
1880
+ end
1881
+ end
1882
+ ensure
1883
+ begin
1884
+ unsubscribe_limits_events(queue_id) if queue_id
1885
+ rescue
1886
+ # Oh Well
1887
+ end
1888
+ end
1889
+
1890
+ end # class ScriptRunnerFrame
1891
+
1892
+ end # module Cosmos