cosmos 3.9.2 → 4.0.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 (438) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +23 -0
  3. data/.travis.yml +1 -0
  4. data/.yardopts +3 -0
  5. data/Gemfile +1 -1
  6. data/Manifest.txt +137 -52
  7. data/Rakefile +50 -44
  8. data/autohotkey/config/system/system.txt +0 -5
  9. data/autohotkey/config/targets/INST/cmd_tlm/inst_cmds.txt +6 -1
  10. data/autohotkey/config/targets/INST/screens/extra.txt +19 -0
  11. data/autohotkey/config/targets/INST/sequences/run_sequence.txt +1 -0
  12. data/autohotkey/config/targets/META/screens/data.txt +12 -0
  13. data/autohotkey/config/targets/SYSTEM/cmd_tlm/meta_cmd_tlm.txt +16 -0
  14. data/autohotkey/config/targets/{COSMOS/cmd_tlm/cosmos_server_cmds.txt → SYSTEM/cmd_tlm/system_cmds.txt} +8 -8
  15. data/autohotkey/config/targets/SYSTEM/cmd_tlm/system_tlm.txt +7 -0
  16. data/autohotkey/config/targets/SYSTEM/screens/limits_change.txt +14 -0
  17. data/autohotkey/config/targets/SYSTEM/screens/meta.txt +14 -0
  18. data/autohotkey/config/targets/SYSTEM/target.txt +12 -0
  19. data/autohotkey/config/tools/cmd_tlm_server/cmd_tlm_server.txt +2 -2
  20. data/autohotkey/config/tools/test_runner/test_runner2.txt +1 -1
  21. data/autohotkey/tools/CmdSequence +14 -0
  22. data/autohotkey/tools/CmdSequenceAHK +23 -0
  23. data/autohotkey/tools/CmdSequenceAHK2 +16 -0
  24. data/autohotkey/tools/cmd_extractor.ahk +2 -2
  25. data/autohotkey/tools/cmd_sender.ahk +4 -6
  26. data/autohotkey/tools/cmd_sequence.ahk +215 -0
  27. data/autohotkey/tools/cmd_sequence2.ahk +23 -0
  28. data/autohotkey/tools/data_viewer.ahk +2 -3
  29. data/autohotkey/tools/limits_monitor.ahk +9 -11
  30. data/autohotkey/tools/open_gl_builder.ahk +1 -2
  31. data/autohotkey/tools/packet_viewer.ahk +51 -35
  32. data/autohotkey/tools/replay.ahk +1 -2
  33. data/autohotkey/tools/script_runner.ahk +1 -2
  34. data/autohotkey/tools/script_runner2.ahk +1 -2
  35. data/autohotkey/tools/test_runner2.ahk +1 -5
  36. data/autohotkey/tools/test_runner3.ahk +1 -3
  37. data/autohotkey/tools/tlm_grapher.ahk +1 -3
  38. data/autohotkey/tools/tlm_grapher3.ahk +1 -2
  39. data/autohotkey/tools/tlm_viewer.ahk +8 -3
  40. data/autohotkey/tools/tlm_viewer2.ahk +2 -3
  41. data/autohotkey/tools/tlm_viewer5.ahk +1 -2
  42. data/cosmos.gemspec +26 -20
  43. data/data/cmd_sequence.png +0 -0
  44. data/data/config/_array_params.yaml +23 -0
  45. data/data/config/_id_items.yaml +24 -0
  46. data/data/config/_id_params.yaml +58 -0
  47. data/data/config/_interfaces.yaml +206 -0
  48. data/data/config/_items.yaml +20 -0
  49. data/data/config/_params.yaml +58 -0
  50. data/data/config/cmd_tlm_server.yaml +110 -0
  51. data/data/config/command.yaml +38 -0
  52. data/data/config/command_modifiers.yaml +127 -0
  53. data/data/config/command_telemetry.yaml +3 -0
  54. data/data/config/data_viewer.yaml +43 -0
  55. data/data/config/handbook_creator.yaml +23 -0
  56. data/data/config/housekeeping_params.yaml +71 -0
  57. data/data/config/interface_modifiers.yaml +44 -0
  58. data/data/config/item_modifiers.yaml +172 -0
  59. data/data/config/launcher.yaml +117 -0
  60. data/data/config/limits_monitor.yaml +53 -0
  61. data/data/config/linegraph_params.yaml +30 -0
  62. data/data/config/linegraph_plot.yaml +106 -0
  63. data/data/config/page_modifiers.yaml +128 -0
  64. data/data/config/param_item_modifiers.yaml +41 -0
  65. data/data/config/parameter_modifiers.yaml +144 -0
  66. data/data/config/protocols.yaml +257 -0
  67. data/data/config/screen.yaml +151 -0
  68. data/data/config/script_runner.yaml +15 -0
  69. data/data/config/system.yaml +153 -0
  70. data/data/config/table_manager.yaml +76 -0
  71. data/data/config/table_parameter_modifiers.yaml +9 -0
  72. data/data/config/target.yaml +71 -0
  73. data/data/config/telemetry.yaml +73 -0
  74. data/data/config/telemetry_modifiers.yaml +129 -0
  75. data/data/config/test_runner.yaml +118 -0
  76. data/data/config/tlm_extractor.yaml +109 -0
  77. data/data/config/tlm_grapher.yaml +78 -0
  78. data/data/config/tlm_viewer.yaml +107 -0
  79. data/data/config/unknown.yaml +3 -0
  80. data/data/config/widgets.yaml +1339 -0
  81. data/data/config/xy_params.yaml +50 -0
  82. data/data/config/xy_plot.yaml +12 -0
  83. data/data/config_editor.png +0 -0
  84. data/data/crc.txt +172 -161
  85. data/data/delete.png +0 -0
  86. data/demo/config/data/crc.txt +56 -36
  87. data/demo/config/data/meta_init.txt +1 -4
  88. data/demo/config/system/system.txt +15 -3
  89. data/demo/config/system/system2.txt +13 -3
  90. data/demo/config/targets/EXAMPLE/lib/example_interface.rb +2 -6
  91. data/demo/config/targets/EXAMPLE/target.txt +3 -1
  92. data/demo/config/targets/INST/cmd_tlm/inst_cmds.txt +1 -0
  93. data/demo/config/targets/INST/lib/inst_dump_component.rb +34 -0
  94. data/demo/config/targets/INST/screens/adcs.txt +39 -15
  95. data/demo/config/targets/INST/screens/commanding.txt +26 -19
  96. data/demo/config/targets/INST/screens/spacing_box.txt +44 -0
  97. data/demo/config/targets/INST/screens/spacing_grid.txt +78 -0
  98. data/demo/config/targets/INST/screens/tabs.txt +0 -2
  99. data/demo/config/targets/INST/sequences/sequence.tsv +3 -0
  100. data/demo/config/targets/INST/tables/EventAction.csv +9 -0
  101. data/demo/config/targets/INST/tables/EventAction.dat +0 -0
  102. data/demo/config/targets/INST/tables/McConfigTable.csv +20 -0
  103. data/demo/config/targets/INST/tables/McConfigTable.dat +0 -0
  104. data/demo/config/targets/INST/target.txt +4 -0
  105. data/demo/config/targets/INST/tools/data_viewer/data_viewer.txt +2 -0
  106. data/demo/config/targets/INST/tools/data_viewer/data_viewer2.txt +2 -0
  107. data/demo/config/targets/INST/tools/table_manager/EventAction_def.txt +6 -0
  108. data/demo/config/targets/INST/tools/table_manager/McConfigTable_def.txt +38 -0
  109. data/demo/config/targets/SYSTEM/cmd_tlm/limits_groups.txt +7 -3
  110. data/demo/config/targets/SYSTEM/cmd_tlm/meta_cmd_tlm.txt +16 -0
  111. data/demo/config/targets/{COSMOS/cmd_tlm/cosmos_server_cmds.txt → SYSTEM/cmd_tlm/system_cmds.txt} +8 -8
  112. data/demo/config/targets/SYSTEM/cmd_tlm/system_tlm.txt +7 -0
  113. data/demo/config/targets/{COSMOS → SYSTEM}/cmd_tlm_server.txt +2 -2
  114. data/demo/config/targets/SYSTEM/lib/limits_groups.rb +39 -0
  115. data/demo/config/targets/SYSTEM/screens/status.txt +1 -1
  116. data/demo/config/targets/SYSTEM/target.txt +12 -0
  117. data/demo/config/targets/TEMPLATED/cmd_tlm_server.txt +1 -1
  118. data/demo/config/targets/TEMPLATED/lib/templated_interface.rb +8 -5
  119. data/demo/config/targets/TEMPLATED/target.txt +2 -0
  120. data/demo/config/tools/cmd_tlm_server/cmd_tlm_server.txt +15 -6
  121. data/demo/config/tools/cmd_tlm_server/cmd_tlm_server2.txt +13 -5
  122. data/demo/config/tools/data_viewer/data_viewer.txt +9 -0
  123. data/demo/config/tools/launcher/launcher.txt +9 -6
  124. data/demo/config/tools/launcher/launcher2.txt +16 -13
  125. data/demo/config/tools/launcher/launcher_mini.txt +45 -0
  126. data/demo/config/tools/table_manager/MCConfigurationTable_fsw1_def.txt +12 -11
  127. data/demo/config/tools/table_manager/MCConfigurationTable_fsw2_def.txt +12 -11
  128. data/demo/config/tools/test_runner/test_runner.txt +1 -1
  129. data/demo/config/tools/tlm_viewer/tlm_viewer.txt +5 -5
  130. data/demo/lib/example_background_task.rb +9 -5
  131. data/demo/lib/example_target.rb +5 -15
  132. data/demo/lib/scpi_target.rb +4 -10
  133. data/demo/procedures/cosmos_api_test.rb +17 -0
  134. data/demo/tools/CmdSequence +16 -0
  135. data/demo/tools/CmdSequence.bat +9 -0
  136. data/demo/tools/ConfigEditor +16 -0
  137. data/demo/tools/ConfigEditor.bat +9 -0
  138. data/demo/tools/mac/CmdSequence.app/Contents/Info.plist +38 -0
  139. data/demo/tools/mac/CmdSequence.app/Contents/MacOS/CmdSequence.rb +16 -0
  140. data/demo/tools/mac/CmdSequence.app/Contents/MacOS/main.sh +10 -0
  141. data/demo/tools/mac/CmdSequence.app/Contents/MacOS/tool_launch.rb +38 -0
  142. data/demo/tools/mac/CmdSequence.app/Contents/Resources/appIcon.icns +0 -0
  143. data/ext/cosmos/ext/packet/packet.c +5 -5
  144. data/install/config/data/crc.txt +12 -8
  145. data/install/config/system/system.txt +13 -3
  146. data/install/config/targets/SYSTEM/cmd_tlm/meta_cmd_tlm.txt +14 -0
  147. data/install/config/targets/SYSTEM/target.txt +12 -0
  148. data/install/tools/CmdSequence +16 -0
  149. data/install/tools/CmdSequence.bat +9 -0
  150. data/install/tools/ConfigEditor +16 -0
  151. data/install/tools/ConfigEditor.bat +9 -0
  152. data/install/tools/mac/CmdSequence.app/Contents/Info.plist +38 -0
  153. data/install/tools/mac/CmdSequence.app/Contents/MacOS/CmdSequence.rb +16 -0
  154. data/install/tools/mac/CmdSequence.app/Contents/MacOS/main.sh +10 -0
  155. data/install/tools/mac/CmdSequence.app/Contents/MacOS/tool_launch.rb +38 -0
  156. data/install/tools/mac/CmdSequence.app/Contents/Resources/appIcon.icns +0 -0
  157. data/lib/cosmos.rb +1 -1
  158. data/lib/cosmos/config/config_parser.rb +147 -59
  159. data/lib/cosmos/config/meta_config_parser.rb +57 -0
  160. data/lib/cosmos/conversions/polynomial_conversion.rb +20 -4
  161. data/lib/cosmos/conversions/unix_time_conversion.rb +4 -4
  162. data/lib/cosmos/core_ext/array.rb +45 -5
  163. data/lib/cosmos/core_ext/cosmos_io.rb +31 -15
  164. data/lib/cosmos/core_ext/file.rb +2 -2
  165. data/lib/cosmos/core_ext/kernel.rb +1 -6
  166. data/lib/cosmos/core_ext/objectspace.rb +0 -2
  167. data/lib/cosmos/core_ext/string.rb +27 -4
  168. data/lib/cosmos/core_ext/time.rb +39 -10
  169. data/lib/cosmos/gui/choosers/combobox_chooser.rb +37 -26
  170. data/lib/cosmos/gui/choosers/file_chooser.rb +23 -6
  171. data/lib/cosmos/gui/choosers/float_chooser.rb +13 -11
  172. data/lib/cosmos/gui/choosers/integer_chooser.rb +13 -11
  173. data/lib/cosmos/gui/choosers/string_chooser.rb +18 -36
  174. data/lib/cosmos/gui/choosers/telemetry_chooser.rb +64 -64
  175. data/lib/cosmos/gui/choosers/value_chooser.rb +15 -15
  176. data/lib/cosmos/gui/dialogs/about_dialog.rb +18 -13
  177. data/lib/cosmos/gui/dialogs/calendar_dialog.rb +11 -3
  178. data/lib/cosmos/gui/dialogs/cmd_tlm_raw_dialog.rb +1 -1
  179. data/lib/cosmos/gui/dialogs/details_dialog.rb +1 -1
  180. data/lib/cosmos/gui/dialogs/exception_dialog.rb +7 -7
  181. data/lib/cosmos/gui/dialogs/find_replace_dialog.rb +20 -15
  182. data/lib/cosmos/gui/dialogs/interface_raw_dialog.rb +143 -0
  183. data/lib/cosmos/gui/dialogs/legal_dialog.rb +6 -5
  184. data/lib/cosmos/gui/dialogs/packet_log_dialog.rb +5 -2
  185. data/lib/cosmos/gui/dialogs/progress_dialog.rb +1 -1
  186. data/lib/cosmos/gui/dialogs/pry_dialog.rb +4 -4
  187. data/lib/cosmos/gui/dialogs/scroll_text_dialog.rb +3 -0
  188. data/lib/cosmos/gui/dialogs/set_tlm_dialog.rb +7 -6
  189. data/lib/cosmos/gui/dialogs/splash.rb +1 -1
  190. data/lib/cosmos/gui/dialogs/tlm_details_dialog.rb +1 -1
  191. data/lib/cosmos/gui/dialogs/tlm_graph_dialog.rb +114 -0
  192. data/lib/cosmos/gui/line_graph/line_graph.rb +9 -10
  193. data/lib/cosmos/gui/line_graph/line_graph_dialog.rb +7 -5
  194. data/lib/cosmos/gui/line_graph/line_graph_drawing.rb +3 -7
  195. data/lib/cosmos/gui/line_graph/line_graph_popups.rb +3 -8
  196. data/lib/cosmos/gui/line_graph/line_graph_scaling.rb +2 -7
  197. data/lib/cosmos/gui/line_graph/overview_graph.rb +6 -1
  198. data/lib/cosmos/gui/opengl/earth_model.rb +6 -3
  199. data/lib/cosmos/gui/opengl/gl_bounds.rb +11 -23
  200. data/lib/cosmos/gui/opengl/gl_light.rb +3 -4
  201. data/lib/cosmos/gui/opengl/gl_material.rb +3 -4
  202. data/lib/cosmos/gui/opengl/gl_scene.rb +10 -4
  203. data/lib/cosmos/gui/opengl/gl_shape.rb +6 -2
  204. data/lib/cosmos/gui/opengl/gl_viewer.rb +5 -5
  205. data/lib/cosmos/gui/opengl/gl_viewport.rb +11 -12
  206. data/lib/cosmos/gui/opengl/moon_model.rb +6 -3
  207. data/lib/cosmos/gui/opengl/stl_reader.rb +8 -9
  208. data/lib/cosmos/gui/opengl/stl_shape.rb +4 -5
  209. data/lib/cosmos/gui/opengl/texture_mapped_sphere.rb +7 -7
  210. data/lib/cosmos/gui/qt.rb +1 -1
  211. data/lib/cosmos/gui/qt_tool.rb +21 -10
  212. data/lib/cosmos/gui/text/completion.rb +23 -2
  213. data/lib/cosmos/gui/text/completion_text_edit.rb +38 -23
  214. data/lib/cosmos/gui/utilities/analyze_log.rb +1 -1
  215. data/lib/cosmos/gui/utilities/screenshot.rb +2 -2
  216. data/lib/cosmos/gui/widgets/full_text_search_line_edit.rb +11 -1
  217. data/lib/cosmos/gui/widgets/packet_log_frame.rb +19 -6
  218. data/lib/cosmos/interfaces.rb +10 -0
  219. data/lib/cosmos/interfaces/cmd_tlm_server_interface.rb +28 -47
  220. data/lib/cosmos/interfaces/interface.rb +240 -22
  221. data/lib/cosmos/interfaces/linc_interface.rb +3 -5
  222. data/lib/cosmos/interfaces/protocols/burst_protocol.rb +173 -0
  223. data/lib/cosmos/interfaces/protocols/crc_protocol.rb +141 -0
  224. data/lib/cosmos/{streams/fixed_stream_protocol.rb → interfaces/protocols/fixed_protocol.rb} +40 -37
  225. data/lib/cosmos/{streams/length_stream_protocol.rb → interfaces/protocols/length_protocol.rb} +55 -48
  226. data/lib/cosmos/interfaces/protocols/override_protocol.rb +52 -0
  227. data/lib/cosmos/interfaces/protocols/preidentified_protocol.rb +141 -0
  228. data/lib/cosmos/interfaces/protocols/protocol.rb +60 -0
  229. data/lib/cosmos/interfaces/protocols/template_protocol.rb +209 -0
  230. data/lib/cosmos/interfaces/protocols/terminated_protocol.rb +81 -0
  231. data/lib/cosmos/interfaces/serial_interface.rb +28 -23
  232. data/lib/cosmos/interfaces/simulated_target_interface.rb +27 -16
  233. data/lib/cosmos/interfaces/stream_interface.rb +36 -108
  234. data/lib/cosmos/interfaces/tcpip_client_interface.rb +21 -21
  235. data/lib/cosmos/interfaces/tcpip_server_interface.rb +555 -94
  236. data/lib/cosmos/interfaces/udp_interface.rb +51 -83
  237. data/lib/cosmos/io/buffered_file.rb +92 -2
  238. data/lib/cosmos/io/json_drb.rb +2 -2
  239. data/lib/cosmos/io/posix_serial_driver.rb +3 -1
  240. data/lib/cosmos/io/raw_logger.rb +3 -3
  241. data/lib/cosmos/io/serial_driver.rb +14 -5
  242. data/lib/cosmos/io/win32_serial_driver.rb +16 -4
  243. data/lib/cosmos/packet_logs.rb +0 -1
  244. data/lib/cosmos/packet_logs/packet_log_reader.rb +11 -1
  245. data/lib/cosmos/packet_logs/packet_log_writer.rb +31 -13
  246. data/lib/cosmos/packets/binary_accessor.rb +599 -32
  247. data/lib/cosmos/packets/commands.rb +48 -24
  248. data/lib/cosmos/packets/packet.rb +140 -54
  249. data/lib/cosmos/packets/packet_config.rb +0 -2
  250. data/lib/cosmos/packets/parsers/packet_item_parser.rb +10 -2
  251. data/lib/cosmos/packets/structure.rb +81 -33
  252. data/lib/cosmos/packets/structure_item.rb +45 -5
  253. data/lib/cosmos/packets/telemetry.rb +149 -55
  254. data/lib/cosmos/script/api_shared.rb +1000 -0
  255. data/lib/cosmos/script/commands.rb +2 -2
  256. data/lib/cosmos/script/extract.rb +19 -4
  257. data/lib/cosmos/script/limits.rb +2 -0
  258. data/lib/cosmos/script/script.rb +1 -1
  259. data/lib/cosmos/script/scripting.rb +4 -784
  260. data/lib/cosmos/script/telemetry.rb +44 -23
  261. data/lib/cosmos/script/tools.rb +15 -69
  262. data/lib/cosmos/streams/serial_stream.rb +12 -19
  263. data/lib/cosmos/streams/stream.rb +2 -11
  264. data/lib/cosmos/streams/tcpip_socket_stream.rb +3 -13
  265. data/lib/cosmos/system/system.rb +187 -31
  266. data/lib/cosmos/system/target.rb +11 -2
  267. data/lib/cosmos/tools/cmd_extractor/cmd_extractor.rb +12 -11
  268. data/lib/cosmos/tools/cmd_sender/{cmd_sender_item_delegate.rb → cmd_param_table_item_delegate.rb} +11 -10
  269. data/lib/cosmos/tools/cmd_sender/cmd_sender.rb +209 -164
  270. data/lib/cosmos/tools/cmd_sequence/cmd_sequence.rb +652 -0
  271. data/lib/cosmos/tools/cmd_sequence/sequence_item.rb +510 -0
  272. data/lib/cosmos/tools/cmd_sequence/sequence_list.rb +194 -0
  273. data/lib/cosmos/tools/cmd_tlm_server/api.rb +179 -5
  274. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server.rb +31 -14
  275. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_config.rb +23 -16
  276. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_gui.rb +92 -20
  277. data/lib/cosmos/tools/cmd_tlm_server/commanding.rb +1 -1
  278. data/lib/cosmos/tools/cmd_tlm_server/gui/interfaces_tab.rb +17 -4
  279. data/lib/cosmos/tools/cmd_tlm_server/gui/targets_tab.rb +0 -5
  280. data/lib/cosmos/tools/cmd_tlm_server/interface_thread.rb +1 -2
  281. data/lib/cosmos/tools/cmd_tlm_server/interfaces.rb +4 -4
  282. data/lib/cosmos/tools/cmd_tlm_server/limits_groups_background_task.rb +121 -0
  283. data/lib/cosmos/tools/cmd_tlm_server/routers.rb +8 -4
  284. data/lib/cosmos/tools/config_editor/config_editor.rb +720 -0
  285. data/lib/cosmos/tools/config_editor/config_editor_frame.rb +675 -0
  286. data/lib/cosmos/tools/data_viewer/data_viewer.rb +44 -27
  287. data/lib/cosmos/tools/data_viewer/data_viewer_component.rb +8 -22
  288. data/lib/cosmos/tools/launcher/launcher.rb +29 -12
  289. data/lib/cosmos/tools/launcher/launcher_config.rb +1 -1
  290. data/lib/cosmos/tools/limits_monitor/limits_monitor.rb +153 -42
  291. data/lib/cosmos/tools/packet_viewer/packet_viewer.rb +44 -6
  292. data/lib/cosmos/tools/replay/replay.rb +36 -20
  293. data/lib/cosmos/tools/replay/replay_server.rb +1 -1
  294. data/lib/cosmos/tools/script_runner/script_runner_config.rb +1 -1
  295. data/lib/cosmos/tools/script_runner/script_runner_frame.rb +31 -21
  296. data/lib/cosmos/tools/table_manager/table_config.rb +9 -3
  297. data/lib/cosmos/tools/table_manager/table_manager.rb +27 -7
  298. data/lib/cosmos/tools/test_runner/results_writer.rb +6 -6
  299. data/lib/cosmos/tools/test_runner/test_runner.rb +4 -6
  300. data/lib/cosmos/tools/tlm_extractor/tlm_extractor.rb +4 -5
  301. data/lib/cosmos/tools/tlm_extractor/tlm_extractor_config.rb +1 -1
  302. data/lib/cosmos/tools/tlm_extractor/tlm_extractor_processor.rb +1 -1
  303. data/lib/cosmos/tools/tlm_grapher/data_object_adders/housekeeping_data_object_adder.rb +23 -6
  304. data/lib/cosmos/tools/tlm_grapher/data_object_editors/housekeeping_data_object_editor.rb +44 -3
  305. data/lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb +20 -7
  306. data/lib/cosmos/tools/tlm_grapher/tabbed_plots/overview_tabbed_plots.rb +1 -1
  307. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_config.rb +11 -4
  308. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_plot_editor.rb +2 -2
  309. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_realtime_thread.rb +1 -1
  310. data/lib/cosmos/tools/tlm_grapher/tlm_grapher.rb +16 -0
  311. data/lib/cosmos/tools/tlm_viewer/screen.rb +36 -32
  312. data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +59 -50
  313. data/lib/cosmos/tools/tlm_viewer/tlm_viewer_config.rb +2 -2
  314. data/lib/cosmos/tools/tlm_viewer/widgets.rb +1 -0
  315. data/lib/cosmos/tools/tlm_viewer/widgets/canvasvalue_widget.rb +1 -0
  316. data/lib/cosmos/tools/tlm_viewer/widgets/labelvalue_widget.rb +22 -4
  317. data/lib/cosmos/tools/tlm_viewer/widgets/matrixbycolumns_widget.rb +9 -0
  318. data/lib/cosmos/tools/tlm_viewer/widgets/spacer_widget.rb +55 -0
  319. data/lib/cosmos/tools/tlm_viewer/widgets/vertical_widget.rb +3 -2
  320. data/lib/cosmos/tools/tlm_viewer/widgets/verticalbox_widget.rb +3 -2
  321. data/lib/cosmos/tools/tlm_viewer/widgets/widget.rb +12 -12
  322. data/lib/cosmos/top_level.rb +34 -24
  323. data/lib/cosmos/utilities/crc.rb +108 -6
  324. data/lib/cosmos/utilities/csv.rb +68 -14
  325. data/lib/cosmos/utilities/logger.rb +2 -2
  326. data/lib/cosmos/utilities/low_fragmentation_array.rb +9 -1
  327. data/lib/cosmos/version.rb +6 -6
  328. data/lib/cosmos/win32/win32_main.rb +50 -46
  329. data/run_gui_tests.bat +3 -1
  330. data/spec/conversions/unix_time_formatted_conversion_spec.rb +2 -2
  331. data/spec/conversions/unix_time_seconds_conversion_spec.rb +2 -2
  332. data/spec/core_ext/file_spec.rb +1 -1
  333. data/spec/core_ext/objectspace_spec.rb +12 -9
  334. data/spec/core_ext/string_spec.rb +6 -0
  335. data/spec/core_ext/time_spec.rb +10 -0
  336. data/spec/gui/line_graph/line_clip_spec.rb +226 -224
  337. data/spec/gui/qt_spec.rb +81 -79
  338. data/spec/install/config/system/system.txt +0 -6
  339. data/spec/install/config/targets/INST/cmd_tlm/inst_cmd_linc.txt +5 -5
  340. data/spec/install/config/targets/INST/cmd_tlm/inst_tlm_linc.txt +8 -8
  341. data/spec/install/config/targets/SYSTEM/cmd_tlm/meta_cmd_tlm.txt +16 -0
  342. data/{install/config/targets/COSMOS/cmd_tlm/cosmos_server_cmds.txt → spec/install/config/targets/SYSTEM/cmd_tlm/system_cmds.txt} +8 -8
  343. data/spec/install/config/targets/SYSTEM/cmd_tlm/system_tlm.txt +7 -0
  344. data/spec/install/config/targets/{COSMOS → SYSTEM}/cmd_tlm_server.txt +2 -2
  345. data/spec/install/config/targets/SYSTEM/screens/status.txt +12 -0
  346. data/spec/install/config/targets/SYSTEM/target.txt +12 -0
  347. data/spec/interfaces/cmd_tlm_server_interface_spec.rb +9 -13
  348. data/spec/interfaces/interface_spec.rb +402 -18
  349. data/spec/interfaces/linc_interface_spec.rb +37 -39
  350. data/spec/interfaces/protocols/burst_protocol_spec.rb +300 -0
  351. data/spec/interfaces/protocols/crc_protocol_spec.rb +709 -0
  352. data/spec/interfaces/protocols/fixed_protocol_spec.rb +119 -0
  353. data/spec/interfaces/protocols/length_protocol_spec.rb +499 -0
  354. data/spec/interfaces/protocols/override_protocol_spec.rb +158 -0
  355. data/spec/interfaces/protocols/preidentified_protocol_spec.rb +149 -0
  356. data/spec/interfaces/protocols/template_protocol_spec.rb +218 -0
  357. data/spec/interfaces/protocols/terminated_protocol_spec.rb +174 -0
  358. data/spec/interfaces/serial_interface_spec.rb +35 -34
  359. data/spec/interfaces/simulated_target_interface_spec.rb +13 -13
  360. data/spec/interfaces/tcpip_client_interface_spec.rb +21 -16
  361. data/spec/interfaces/tcpip_server_interface_spec.rb +66 -69
  362. data/spec/interfaces/udp_interface_spec.rb +120 -55
  363. data/spec/io/serial_driver_spec.rb +41 -39
  364. data/spec/io/udp_sockets_spec.rb +13 -8
  365. data/spec/io/win32_serial_driver_spec.rb +62 -59
  366. data/spec/packet_logs/packet_log_reader_spec.rb +68 -47
  367. data/spec/packet_logs/packet_log_writer_spec.rb +7 -5
  368. data/spec/packets/commands_spec.rb +5 -5
  369. data/spec/packets/packet_spec.rb +2 -14
  370. data/spec/script/extract_spec.rb +21 -7
  371. data/spec/script/scripting_spec.rb +261 -6
  372. data/spec/script/telemetry_spec.rb +17 -9
  373. data/spec/spec_helper.rb +26 -10
  374. data/spec/streams/serial_stream_spec.rb +87 -82
  375. data/spec/streams/tcpip_client_stream_spec.rb +12 -4
  376. data/spec/streams/tcpip_socket_stream_spec.rb +5 -0
  377. data/spec/system/system_spec.rb +66 -50
  378. data/spec/system/target_spec.rb +33 -11
  379. data/spec/tools/cmd_tlm_server/api_spec.rb +5 -5
  380. data/spec/tools/cmd_tlm_server/background_tasks_spec.rb +75 -15
  381. data/spec/tools/cmd_tlm_server/cmd_tlm_server_config_spec.rb +125 -5
  382. data/spec/tools/cmd_tlm_server/cmd_tlm_server_spec.rb +244 -232
  383. data/spec/tools/cmd_tlm_server/commanding_spec.rb +18 -18
  384. data/spec/tools/cmd_tlm_server/interface_thread_spec.rb +124 -29
  385. data/spec/tools/cmd_tlm_server/interfaces_spec.rb +2 -2
  386. data/spec/tools/cmd_tlm_server/limits_groups_background_task_spec.rb +145 -0
  387. data/spec/tools/cmd_tlm_server/router_thread_spec.rb +50 -10
  388. data/spec/tools/table_manager/tablemanager_core_spec.rb +0 -1
  389. data/spec/top_level/top_level_spec.rb +39 -11
  390. data/spec/utilities/csv_spec.rb +62 -20
  391. data/tasks/gemfile_stats.rake +6 -3
  392. data/test/performance/config/system/system_packets.txt +0 -1
  393. data/test/performance/config/system/system_threads.txt +0 -1
  394. metadata +177 -92
  395. data/autohotkey/config/targets/COSMOS/cmd_tlm/cosmos_server_tlm.txt +0 -15
  396. data/autohotkey/config/targets/COSMOS/cmd_tlm_server.txt +0 -6
  397. data/autohotkey/config/targets/COSMOS/target.txt +0 -5
  398. data/autohotkey/userpath.txt +0 -1
  399. data/demo/config/targets/COSMOS/cmd_tlm/cosmos_server_tlm.txt +0 -15
  400. data/demo/config/targets/COSMOS/screens/limits_change.txt +0 -20
  401. data/demo/config/targets/COSMOS/screens/version.txt +0 -19
  402. data/demo/config/targets/COSMOS/target.txt +0 -11
  403. data/demo/config/targets/META/cmd_tlm/meta_cmd.txt +0 -10
  404. data/demo/config/targets/META/cmd_tlm/meta_tlm.txt +0 -13
  405. data/demo/userpath.txt +0 -1
  406. data/install/config/targets/COSMOS/cmd_tlm/cosmos_server_tlm.txt +0 -15
  407. data/install/config/targets/COSMOS/cmd_tlm_server.txt +0 -6
  408. data/install/config/targets/COSMOS/screens/limits_change.txt +0 -20
  409. data/install/config/targets/COSMOS/screens/version.txt +0 -19
  410. data/install/config/targets/COSMOS/target.txt +0 -9
  411. data/install/config/targets/SYSTEM/README.txt +0 -1
  412. data/install/userpath.txt +0 -1
  413. data/lib/cosmos/io/tcpip_server.rb +0 -571
  414. data/lib/cosmos/packet_logs/meta_packet_log_writer.rb +0 -107
  415. data/lib/cosmos/streams/burst_stream_protocol.rb +0 -25
  416. data/lib/cosmos/streams/preidentified_stream_protocol.rb +0 -118
  417. data/lib/cosmos/streams/stream_protocol.rb +0 -373
  418. data/lib/cosmos/streams/template_stream_protocol.rb +0 -140
  419. data/lib/cosmos/streams/terminated_stream_protocol.rb +0 -85
  420. data/spec/install/config/targets/COSMOS/cmd_tlm/cosmos_server_cmds.txt +0 -41
  421. data/spec/install/config/targets/COSMOS/cmd_tlm/cosmos_server_tlm.txt +0 -15
  422. data/spec/install/config/targets/COSMOS/screens/limits_change.txt +0 -20
  423. data/spec/install/config/targets/COSMOS/screens/version.txt +0 -19
  424. data/spec/install/config/targets/COSMOS/target.txt +0 -5
  425. data/spec/install/config/targets/META/cmd_tlm/meta_cmd.txt +0 -4
  426. data/spec/install/config/targets/META/cmd_tlm/meta_tlm.txt +0 -4
  427. data/spec/install/userpath.txt +0 -1
  428. data/spec/interfaces/stream_interface_spec.rb +0 -157
  429. data/spec/io/tcpip_server_spec.rb +0 -338
  430. data/spec/packet_logs/meta_packet_log_writer_spec.rb +0 -170
  431. data/spec/streams/burst_stream_protocol_spec.rb +0 -32
  432. data/spec/streams/fixed_stream_protocol_spec.rb +0 -113
  433. data/spec/streams/length_stream_protocol_spec.rb +0 -300
  434. data/spec/streams/preidentified_stream_protocol_spec.rb +0 -121
  435. data/spec/streams/stream_protocol_spec.rb +0 -346
  436. data/spec/streams/template_stream_protocol_spec.rb +0 -156
  437. data/spec/streams/terminated_stream_protocol_spec.rb +0 -127
  438. data/test/performance/userpath.txt +0 -1
@@ -1,6 +1,6 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- # Copyright 2014 Ball Aerospace & Technologies Corp.
3
+ # Copyright 2017 Ball Aerospace & Technologies Corp.
4
4
  # All Rights Reserved.
5
5
  #
6
6
  # This program is free software; you can modify and/or redistribute it
@@ -12,7 +12,6 @@ require 'cosmos/interfaces/stream_interface'
12
12
  require 'cosmos/streams/tcpip_client_stream'
13
13
 
14
14
  module Cosmos
15
-
16
15
  # Base class for interfaces that act as a TCP/IP client
17
16
  class TcpipClientInterface < StreamInterface
18
17
 
@@ -21,16 +20,19 @@ module Cosmos
21
20
  # @param read_port [Integer] Port to read telemetry from
22
21
  # @param write_timeout [Integer] Seconds to wait before aborting writes
23
22
  # @param read_timeout [Integer] Seconds to wait before aborting reads
24
- # @param stream_protocol_type (see StreamInterface#initialize)
25
- # @param stream_protocol_args (see StreamInterface#initialize)
26
- def initialize(hostname,
27
- write_port,
28
- read_port,
29
- write_timeout,
30
- read_timeout,
31
- stream_protocol_type,
32
- *stream_protocol_args)
33
- super(stream_protocol_type, *stream_protocol_args)
23
+ # @param protocol_type [String] Name of the protocol to use
24
+ # with this interface
25
+ # @param protocol_args [Array<String>] Arguments to pass to the protocol
26
+ def initialize(
27
+ hostname,
28
+ write_port,
29
+ read_port,
30
+ write_timeout,
31
+ read_timeout,
32
+ protocol_type = nil,
33
+ *protocol_args)
34
+
35
+ super(protocol_type, protocol_args)
34
36
 
35
37
  @hostname = hostname
36
38
  @write_port = ConfigParser.handle_nil(write_port)
@@ -42,19 +44,17 @@ module Cosmos
42
44
  @write_raw_allowed = false unless @write_port
43
45
  end
44
46
 
45
- # Connects the {StreamProtocol} to a {TcpipClientStream} by passing the
47
+ # Connects the {TcpipClientStream} by passing the
46
48
  # initialization parameters to the {TcpipClientStream}.
47
49
  def connect
48
- stream = TcpipClientStream.new(
50
+ @stream = TcpipClientStream.new(
49
51
  @hostname,
50
52
  @write_port,
51
53
  @read_port,
52
54
  @write_timeout,
53
- @read_timeout)
54
- stream.raw_logger_pair = @raw_logger_pair
55
- @stream_protocol.connect(stream)
55
+ @read_timeout
56
+ )
57
+ super()
56
58
  end
57
-
58
- end # class TcpipClientInterface
59
-
60
- end # module Cosmos
59
+ end
60
+ end
@@ -1,6 +1,6 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- # Copyright 2014 Ball Aerospace & Technologies Corp.
3
+ # Copyright 2017 Ball Aerospace & Technologies Corp.
4
4
  # All Rights Reserved.
5
5
  #
6
6
  # This program is free software; you can modify and/or redistribute it
@@ -8,157 +8,618 @@
8
8
  # as published by the Free Software Foundation; version 3 with
9
9
  # attribution addendums as found in the LICENSE.txt
10
10
 
11
- require 'cosmos/interfaces/interface'
12
- require 'cosmos/io/tcpip_server'
11
+ require 'socket'
12
+ require 'thread' # For Mutex
13
+ require 'timeout' # For Timeout::Error
14
+ require 'cosmos/interfaces/stream_interface'
15
+ require 'cosmos/streams/tcpip_socket_stream'
16
+ require 'cosmos/config/config_parser'
13
17
 
14
18
  module Cosmos
15
19
 
16
- # Base class for interfaces that act as a TCP/IP server
17
- class TcpipServerInterface < Interface
20
+ # TCP/IP Server which can both read and write on a single port or two
21
+ # independent ports. A listen thread is setup which waits for client
22
+ # connections. For each connection to the read port, a thread is spawned that
23
+ # calls the read method from the interface. This data is then
24
+ # available by calling the TcpipServer read method. For each connection to the
25
+ # write port, a thread is spawned that calls the write method from the
26
+ # interface when data is send to the TcpipServer via the write method.
27
+ class TcpipServerInterface < StreamInterface
28
+ # Data class which stores the interface and associated information
29
+ class InterfaceInfo
30
+ attr_reader :interface, :hostname, :host_ip, :port
31
+ def initialize(interface, hostname, host_ip, port)
32
+ @interface = interface
33
+ @hostname = hostname
34
+ @host_ip = host_ip
35
+ @port = port
36
+ end
37
+ end
18
38
 
19
- # @param write_port [Integer] Port that clients should write commands to
20
- # @param read_port [Integer] Port that clients should read telemetry from
21
- # @param write_timeout [Integer] Seconds to wait before aborting writes
22
- # @param read_timeout [Integer] Seconds to wait before aborting reads
23
- # @param stream_protocol_type (see StreamInterface#initialize)
24
- # @param stream_protocol_args (see StreamInterface#initialize)
39
+ # Callback method to call when a new client connects to the write port.
40
+ # This method will be called with the Interface as the only argument.
41
+ attr_accessor :write_connection_callback
42
+ # Callback method to call when a new client connects to the read port.
43
+ # This method will be called with the Interface as the only argument.
44
+ attr_accessor :read_connection_callback
45
+ # @return [RawLoggerPair] RawLoggerPair instance or nil
46
+ attr_accessor :raw_logger_pair
47
+ # @return [String] The ip address to bind to. Default to ANY (0.0.0.0)
48
+ attr_accessor :listen_address
49
+ # @return [boolean] Automatically send SYSTEM META on connect - Default false - Can be CMD/TLM
50
+ attr_accessor :auto_system_meta
51
+
52
+ # @param write_port [Integer] The server write port. Clients should connect
53
+ # and expect to receive data from this port.
54
+ # @param read_port [Integer] The server read port. Clients should connect
55
+ # and expect to send data to this port.
56
+ # @param write_timeout [Float|nil] The number of seconds to wait for the
57
+ # write to complete. Pass nil to block until the write is complete.
58
+ # @param read_timeout [Float|nil] The number of seconds to wait for the
59
+ # read to complete. Pass nil to block until the read is complete.
60
+ # @param protocol_type [String] The name of the stream to
61
+ # use for both the read and write ports. This name is combined with
62
+ # 'Protocol' to result in a COSMOS Protocol class.
63
+ # @param protocol_args [Array] Arguments to pass to the Protocol
25
64
  def initialize(write_port,
26
65
  read_port,
27
66
  write_timeout,
28
67
  read_timeout,
29
- stream_protocol_type,
30
- *stream_protocol_args)
31
- super()
32
-
33
- @tcpip_server = TcpipServer.new(write_port,
34
- read_port,
35
- write_timeout,
36
- read_timeout,
37
- stream_protocol_type,
38
- *stream_protocol_args)
39
- @tcpip_server.interface = self
68
+ protocol_type = nil,
69
+ *protocol_args)
70
+ super(protocol_type, protocol_args)
71
+ @write_port = ConfigParser.handle_nil(write_port)
72
+ @write_port = Integer(write_port) if @write_port
73
+ @read_port = ConfigParser.handle_nil(read_port)
74
+ @read_port = Integer(read_port) if @read_port
75
+ @write_timeout = ConfigParser.handle_nil(write_timeout)
76
+ @write_timeout = @write_timeout.to_f if @write_timeout
77
+ @read_timeout = ConfigParser.handle_nil(read_timeout)
78
+ @read_timeout = @read_timeout.to_f if @read_timeout
79
+ @listen_sockets = []
80
+ @listen_pipes = []
81
+ @listen_threads = []
82
+ @read_threads = []
83
+ @write_thread = nil
84
+ @write_raw_thread = nil
85
+ @write_interface_infos = []
86
+ @read_interface_infos = []
87
+ @write_queue = nil
88
+ @write_queue = Queue.new if @write_port
89
+ @write_raw_queue = nil
90
+ @write_raw_queue = Queue.new if @write_port
91
+ @read_queue = nil
92
+ @read_queue = Queue.new if @read_port
93
+ @write_condition_variable = nil
94
+ @write_condition_variable = ConditionVariable.new if @write_port
95
+ @write_raw_mutex = nil
96
+ @write_raw_mutex = Mutex.new if @write_port
97
+ @write_raw_condition_variable = nil
98
+ @write_raw_condition_variable = ConditionVariable.new if @write_port
99
+ @write_connection_callback = nil
100
+ @read_connection_callback = nil
101
+ @raw_logger_pair = nil
102
+ @raw_logging_enabled = false
103
+ @connection_mutex = Mutex.new
104
+ @listen_address = "0.0.0.0"
105
+ @auto_system_meta = false
106
+
40
107
  @read_allowed = false unless ConfigParser.handle_nil(read_port)
41
108
  @write_allowed = false unless ConfigParser.handle_nil(write_port)
42
109
  @write_raw_allowed = false unless ConfigParser.handle_nil(write_port)
110
+
111
+ @connected = false
43
112
  end
44
113
 
45
- # (see TcpipServer#connect)
114
+ # Create the read and write port listen threads. Incoming connections will
115
+ # spawn separate threads to process the reads and writes.
46
116
  def connect
47
- @tcpip_server.raw_logger_pair = @raw_logger_pair
48
- @tcpip_server.connect
117
+ @cancel_threads = false
118
+ @read_queue.clear if @read_queue
119
+ if @write_port == @read_port # One socket
120
+ start_listen_thread(@read_port, true, true)
121
+ else
122
+ start_listen_thread(@write_port, true, false) if @write_port
123
+ start_listen_thread(@read_port, false, true) if @read_port
124
+ end
125
+
126
+ if @write_port
127
+ @write_thread = Thread.new do
128
+ begin
129
+ loop do
130
+ write_thread_body()
131
+ break if @cancel_threads
132
+ end
133
+ rescue Exception => err
134
+ shutdown_interfaces(@write_interface_infos)
135
+ Logger.instance.error("Tcpip server write thread unexpectedly died")
136
+ Logger.instance.error(err.formatted)
137
+ end
138
+ end
139
+ @write_raw_thread = Thread.new do
140
+ begin
141
+ loop do
142
+ write_raw_thread_body()
143
+ break if @cancel_threads
144
+ end
145
+ rescue Exception => err
146
+ shutdown_interfaces(@write_interface_infos)
147
+ Logger.instance.error("Tcpip server write raw thread unexpectedly died")
148
+ Logger.instance.error(err.formatted)
149
+ end
150
+ end
151
+ else
152
+ @write_thread = nil
153
+ @write_raw_thread = nil
154
+ end
155
+ @connected = true
49
156
  end
50
157
 
51
- # (see TcpipServer#connected?)
158
+ # @return [Boolean] Whether the server is listening for connections
52
159
  def connected?
53
- @tcpip_server.connected?
160
+ @connected
54
161
  end
55
162
 
56
- # (see TcpipServer#disconnect)
163
+ # Shutdowns the listener threads for both the read and write ports as well
164
+ # as any client connections. As a part of shutting down client connections,
165
+ # the {Protocol#disconnect} method is called.
57
166
  def disconnect
58
- @tcpip_server.disconnect
59
- end
60
-
61
- # (see TcpipServer#read)
62
- def read
63
- # Normal case will not be trying to read if not connected so don't bother checking
64
- packet = @tcpip_server.read
65
- @read_count += 1 if packet
66
- packet
67
- end
68
-
69
- # If the server has connections, the packet is written to all the connected
70
- # clients.
71
- #
72
- # @param packet [Packet]
73
- def write(packet)
74
- if connected?()
167
+ @cancel_threads = true
168
+ @read_queue << nil if @read_queue
169
+ @listen_pipes.each do |pipe|
75
170
  begin
76
- @tcpip_server.write(packet)
77
- @write_count += 1
78
- rescue Exception => err
79
- Logger.instance.error("Error writing to interface : #{@name}")
80
- disconnect()
81
- raise err
171
+ pipe.write('.')
172
+ rescue Exception
173
+ # Oh well
82
174
  end
83
- else
84
- raise "Interface not connected for write : #{@name}"
85
175
  end
86
- end
176
+ @listen_pipes.clear
87
177
 
88
- # If the server has connections, the data is written to all the connected
89
- # clients.
90
- #
91
- # @param data [String] Raw binary data
92
- def write_raw(data)
93
- if connected?()
178
+ # Shutdown listen thread(s)
179
+ @listen_threads.each { |listen_thread| Cosmos.kill_thread(self, listen_thread) }
180
+ @listen_threads.clear
181
+
182
+ # Shutdown listen socket(s)
183
+ @listen_sockets.each do |listen_socket|
94
184
  begin
95
- @tcpip_server.write_raw(data)
96
- @write_count += 1
97
- rescue Exception => err
98
- Logger.instance.error("Error writing raw data to interface : #{@name}")
99
- disconnect()
100
- raise err
185
+ Cosmos.close_socket(listen_socket)
186
+ rescue IOError
187
+ # Ok may have been closed by the thread
101
188
  end
102
- else
103
- raise "Interface not connected for write_raw : #{@name}"
104
189
  end
105
- end
190
+ @listen_sockets.clear
191
+
192
+ # This will unblock read threads
193
+ shutdown_interfaces(@read_interface_infos)
106
194
 
107
- # (see TcpipServer#bytes_read)
108
- def bytes_read
109
- @tcpip_server.bytes_read
195
+ @read_threads.each { |thread| Cosmos.kill_thread(self, thread) }
196
+ @read_threads.clear
197
+ if @write_thread
198
+ Cosmos.kill_thread(self, @write_thread)
199
+ @write_thread = nil
200
+ end
201
+ if @write_raw_thread
202
+ Cosmos.kill_thread(self, @write_raw_thread)
203
+ @write_raw_thread = nil
204
+ end
205
+
206
+ shutdown_interfaces(@write_interface_infos)
207
+ @connected = false
110
208
  end
111
209
 
112
- # (see TcpipServer#bytes_read)
113
- def bytes_read=(bytes_read)
114
- @tcpip_server.bytes_read = bytes_read
210
+ # Gracefully kill all the threads
211
+ def graceful_kill
212
+ # This method is just here to prevent warnings
115
213
  end
116
214
 
117
- # (see TcpipServer#bytes_written)
118
- def bytes_written
119
- @tcpip_server.bytes_written
215
+ # @return [Packet] Latest packet read from any of the connected clients.
216
+ # Note this method blocks until data is available.
217
+ def read
218
+ raise "Interface not connected for read: #{@name}" unless connected? && read_allowed?
219
+ packet = @read_queue.pop
220
+ return nil unless packet
221
+ @read_count += 1
222
+ packet
120
223
  end
121
224
 
122
- # (see TcpipServer#bytes_written)
123
- def bytes_written=(bytes_written)
124
- @tcpip_server.bytes_written = bytes_written
225
+ # @param packet [Packet] Packet to write to all clients connected to the
226
+ # write port.
227
+ def write(packet)
228
+ raise "Interface not connected for write: #{@name}" unless connected? && write_allowed?
229
+ @write_count += 1
230
+ @write_queue << packet
231
+ @write_condition_variable.broadcast
125
232
  end
126
233
 
127
- # Number of clients connected to the TCP/IP server
128
- def num_clients
129
- @tcpip_server.num_clients
234
+ # @param data [String] Data to write to all clients connected to the
235
+ # write port.
236
+ def write_raw(data)
237
+ raise "Interface not connected for write_raw: #{@name}" unless connected? && write_raw_allowed?
238
+ @write_raw_queue << data
239
+ @write_raw_condition_variable.broadcast
240
+ return data
130
241
  end
131
242
 
132
- # Number of packets buffered in the read queue
243
+ # @return [Integer] The number of packets waiting on the read queue
133
244
  def read_queue_size
134
- @tcpip_server.read_queue_size
245
+ @read_queue ? @read_queue.size : 0
135
246
  end
136
247
 
137
- # Number of packets buffered in the write queue
248
+ # @return [Integer] The number of packets waiting on the write queue
138
249
  def write_queue_size
139
- @tcpip_server.write_queue_size
250
+ @write_queue ? @write_queue.size : 0
251
+ end
252
+
253
+ # @return [Integer] The number of connected clients
254
+ def num_clients
255
+ interfaces = []
256
+ @write_interface_infos.each {|wii| interfaces << wii.interface}
257
+ @read_interface_infos.each {|rii| interfaces << rii.interface}
258
+ interfaces.uniq.length
140
259
  end
141
260
 
142
261
  # Start raw logging for this interface
143
262
  def start_raw_logging
144
- @tcpip_server.start_raw_logging
263
+ @raw_logging_enabled = true
264
+ change_raw_logging(:start)
145
265
  end
146
266
 
147
267
  # Stop raw logging for this interface
148
268
  def stop_raw_logging
149
- @tcpip_server.stop_raw_logging
269
+ @raw_logging_enabled = false
270
+ change_raw_logging(:stop)
150
271
  end
151
272
 
152
273
  # Supported Options
153
274
  # LISTEN_ADDRESS - Ip address of the interface to accept connections on - Default: 0.0.0.0
275
+ # AUTO_SYSTEM_META - Automatically send SYSTEM META on connect - Default false
154
276
  # (see Interface#set_option)
155
277
  def set_option(option_name, option_values)
156
278
  super(option_name, option_values)
157
- if option_name.upcase == 'LISTEN_ADDRESS'
158
- @tcpip_server.listen_address = option_values[0]
279
+ case option_name.upcase
280
+ when 'LISTEN_ADDRESS'
281
+ @listen_address = option_values[0]
282
+ when 'AUTO_SYSTEM_META'
283
+ @auto_system_meta = ConfigParser.handle_true_false(option_values[0])
284
+ end
285
+ end
286
+
287
+ protected
288
+
289
+ def shutdown_interfaces(interface_infos)
290
+ @connection_mutex.synchronize do
291
+ interface_infos.each do |interface_info|
292
+ interface_info.interface.disconnect
293
+ interface_info.interface.raw_logger_pair.stop if interface_info.interface.raw_logger_pair
294
+ end
295
+ interface_infos.clear
296
+ end
297
+ end
298
+
299
+ def change_raw_logging(method)
300
+ if @raw_logger_pair
301
+ @write_interface_infos.each do |interface_info|
302
+ interface_info.interface.raw_logger_pair.send(method) if interface_info.interface.raw_logger_pair
303
+ end
304
+ @read_interface_infos.each do |interface_info|
305
+ interface_info.interface.raw_logger_pair.send(method) if interface_info.interface.raw_logger_pair
306
+ end
307
+ end
308
+ end
309
+
310
+ def start_listen_thread(port, listen_write = false, listen_read = false)
311
+ # Create a socket to accept connections from clients
312
+ addr = Socket.pack_sockaddr_in(port, @listen_address)
313
+ if RUBY_ENGINE == 'ruby'
314
+ listen_socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
315
+ listen_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1) unless Kernel.is_windows?
316
+ begin
317
+ listen_socket.bind(addr)
318
+ rescue Errno::EADDRINUSE
319
+ raise "Error binding to port #{port}.\n" +
320
+ "Either another application is using this port\n" +
321
+ "or the operating system is being slow cleaning up.\n" +
322
+ "Make sure all sockets/streams are closed in all applications,\n" +
323
+ "wait 1 minute and try again."
324
+ end
325
+
326
+ listen_socket.listen(5)
327
+ else
328
+ listen_socket = ServerSocket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
329
+ listen_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1) unless Kernel.is_windows?
330
+ begin
331
+ listen_socket.bind(addr, 5)
332
+ rescue Errno::EADDRINUSE
333
+ raise "Error binding to port #{port}.\n" +
334
+ "Either another application is using this port\n" +
335
+ "or the operating system is being slow cleaning up.\n" +
336
+ "Make sure all sockets/streams are closed in all applications,\n" +
337
+ "wait 1 minute and try again."
338
+ end
339
+ end
340
+ @listen_sockets << listen_socket
341
+ @listen_threads << Thread.new do
342
+ begin
343
+ thread_reader, thread_writer = IO.pipe
344
+ @listen_pipes << thread_writer
345
+ loop do
346
+ listen_thread_body(listen_socket, listen_write, listen_read, thread_reader)
347
+ break if @cancel_threads
348
+ end
349
+ rescue => err
350
+ Logger.instance.error("Tcpip server listen thread unexpectedly died")
351
+ Logger.instance.error(err.formatted)
352
+ end
353
+ end
354
+ end
355
+
356
+ def listen_thread_body(listen_socket, listen_write, listen_read, thread_reader)
357
+ begin
358
+ socket, address = listen_socket.accept_nonblock
359
+ rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EINTR, Errno::EWOULDBLOCK
360
+ read_ready, _ = IO.select([listen_socket, thread_reader])
361
+ if read_ready && read_ready.include?(thread_reader)
362
+ return
363
+ else
364
+ retry
365
+ end
366
+ end
367
+
368
+ port, host_ip = Socket.unpack_sockaddr_in(address)
369
+ hostname = ''
370
+ hostname = Socket.lookup_hostname_from_ip(host_ip) if System.instance.use_dns
371
+ if System.instance.acl
372
+ addr = ["AF_INET", 10, "lc630", host_ip.to_s]
373
+ if not System.instance.acl.allow_addr?(addr)
374
+ # Reject connection
375
+ Cosmos.close_socket(socket)
376
+ Logger.instance.info "Tcpip server rejected connection from #{hostname}(#{host_ip}):#{port}"
377
+ return
378
+ end
379
+ end
380
+
381
+ # Configure TCP_NODELAY option
382
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
383
+
384
+ # Accept Connection
385
+ write_socket = nil
386
+ read_socket = nil
387
+ write_socket = socket if listen_write
388
+ read_socket = socket if listen_read
389
+ stream = TcpipSocketStream.new(write_socket, read_socket, @write_timeout, @read_timeout)
390
+
391
+ interface = StreamInterface.new
392
+ interface.target_names = @target_names
393
+ if @raw_logger_pair
394
+ interface.raw_logger_pair = @raw_logger_pair.clone
395
+ interface.raw_logger_pair.start if @raw_logging_enabled
396
+ end
397
+ @protocol_info.each do |protocol_class, protocol_args, read_write|
398
+ interface.add_protocol(protocol_class, protocol_args, read_write)
399
+ end
400
+ interface.stream = stream
401
+ interface.connect
402
+
403
+ if listen_write
404
+ if @auto_system_meta
405
+ meta_packet = System.telemetry.packet('SYSTEM', 'META').clone
406
+ interface.write(meta_packet)
407
+ end
408
+
409
+ @write_connection_callback.call(interface) if @write_connection_callback
410
+ @connection_mutex.synchronize do
411
+ @write_interface_infos << InterfaceInfo.new(interface, hostname, host_ip, port)
412
+ end
413
+ end
414
+ if listen_read
415
+ @read_connection_callback.call(interface) if @read_connection_callback
416
+ @connection_mutex.synchronize do
417
+ @read_interface_infos << InterfaceInfo.new(interface, hostname, host_ip, port)
418
+ end
419
+ start_read_thread(@read_interface_infos[-1])
420
+ end
421
+ Logger.instance.info "Tcpip server accepted connection from #{hostname}(#{host_ip}):#{port}"
422
+ end
423
+
424
+ def start_read_thread(interface_info)
425
+ @read_threads << Thread.new do
426
+ index_to_delete = nil
427
+ begin
428
+ begin
429
+ read_thread_body(interface_info.interface)
430
+ rescue Exception => err
431
+ Logger.instance.error "Tcpip server read thread unexpectedly died"
432
+ Logger.instance.error err.formatted
433
+ end
434
+ Logger.instance.info "Tcpip server lost read connection to #{interface_info.hostname}(#{interface_info.host_ip}):#{interface_info.port}"
435
+ @read_threads.delete(Thread.current)
436
+
437
+ index_to_delete = nil
438
+ @connection_mutex.synchronize do
439
+ begin
440
+ index = 0
441
+ @read_interface_infos.each do |read_interface_info|
442
+ if interface_info.interface == read_interface_info.interface
443
+ index_to_delete = index
444
+ read_interface_info.interface.disconnect
445
+ read_interface_info.interface.raw_logger_pair.stop if read_interface_info.interface.raw_logger_pair
446
+ break
447
+ end
448
+ index += 1
449
+ end
450
+ ensure
451
+ if index_to_delete
452
+ @read_interface_infos.delete_at(index_to_delete)
453
+ end
454
+ end
455
+ end
456
+ rescue Exception => err
457
+ Logger.instance.error "Tcpip server read thread unexpectedly died"
458
+ Logger.instance.error err.formatted
459
+ end
159
460
  end
160
461
  end
161
462
 
162
- end # class TcpipServerInterface
463
+ def write_thread_body
464
+ # Retrieve the next packet to be sent out to clients
465
+ # Handles disconnected clients even when packets aren't flowing
466
+ packet = nil
163
467
 
164
- end # module Cosmos
468
+ loop do
469
+ break if @cancel_threads
470
+ begin
471
+ packet = @write_queue.pop(true) # non_block to raise ThreadError
472
+ break
473
+ rescue ThreadError
474
+ check_for_dead_clients()
475
+ end
476
+ end
477
+
478
+ packet = write_thread_hook(packet)
479
+ write_to_clients(:write, packet) if packet
480
+ end
481
+
482
+ def write_raw_thread_body
483
+ # Retrieve the next data to be sent out to clients
484
+ data = nil
485
+
486
+ loop do
487
+ break if @cancel_threads
488
+ begin
489
+ data = @write_raw_queue.pop(true) # non_block to raise ThreadError
490
+ break
491
+ rescue ThreadError
492
+ # Sleep until we receive data or for 100ms
493
+ @write_raw_mutex.synchronize do
494
+ @write_raw_condition_variable.wait(@write_raw_mutex, 0.1)
495
+ end
496
+ end
497
+ end
498
+
499
+ data = write_raw_thread_hook(data)
500
+ write_to_clients(:write_raw, data) if data
501
+ end
502
+
503
+ def interface_disconnect(interface_info)
504
+ Logger.instance.info "Tcpip server lost write connection to "\
505
+ "#{interface_info.hostname}(#{interface_info.host_ip}):#{interface_info.port}"
506
+ interface_info.interface.disconnect
507
+ interface_info.interface.raw_logger_pair.stop if interface_info.interface.raw_logger_pair
508
+ end
509
+
510
+ def write_thread_hook(packet)
511
+ packet # By default just return the packet
512
+ end
513
+
514
+ def write_raw_thread_hook(data)
515
+ data # By default just return the data
516
+ end
517
+
518
+ def read_thread_body(interface)
519
+ thread_bytes_read = 0
520
+ loop do
521
+ packet = interface.read
522
+ interface_bytes_read = interface.bytes_read
523
+ if interface_bytes_read != thread_bytes_read
524
+ diff = interface_bytes_read - thread_bytes_read
525
+ @bytes_read += diff # This would be better if mutex protected, but not that important for telemetry
526
+ thread_bytes_read = interface_bytes_read
527
+ end
528
+ return if !packet || @cancel_threads
529
+ packet = read_thread_hook(packet) # Do work on received packet
530
+ @read_raw_data_time = interface.read_raw_data_time
531
+ @read_raw_data = interface.read_raw_data
532
+ @read_queue << packet.clone
533
+ end
534
+ end
535
+
536
+ # @return [Packet] Return the packet
537
+ def read_thread_hook(packet)
538
+ packet
539
+ end
540
+
541
+ def check_for_dead_clients
542
+ indexes_to_delete = []
543
+ index = 0
544
+
545
+ @connection_mutex.synchronize do
546
+ @write_interface_infos.each do |interface_info|
547
+ begin
548
+ if (@write_port != @read_port)
549
+ # Socket should return EWOULDBLOCK if it is still cleanly connected
550
+ interface_info.interface.stream.write_socket.recvfrom_nonblock(10)
551
+ elsif (!interface_info.interface.stream.write_socket.closed?)
552
+ # Let read thread detect disconnect
553
+ next
554
+ end
555
+ # Client has disconnected (or is invalidly sending data on the socket)
556
+ Logger.instance.info "Tcpip server lost write connection to #{interface_info.hostname}(#{interface_info.host_ip}):#{interface_info.port}"
557
+ interface_info.interface.disconnect
558
+ interface_info.interface.raw_logger_pair.stop if interface_info.interface.raw_logger_pair
559
+ indexes_to_delete.unshift(index) # Put later indexes at front of array
560
+ rescue Errno::ECONNRESET, Errno::ECONNABORTED, IOError
561
+ # Client has disconnected
562
+ Logger.instance.info "Tcpip server lost write connection to #{interface_info.hostname}(#{interface_info.host_ip}):#{interface_info.port}"
563
+ interface_info.interface.disconnect
564
+ interface_info.interface.raw_logger_pair.stop if interface_info.interface.raw_logger_pair
565
+ indexes_to_delete.unshift(index) # Put later indexes at front of array
566
+ rescue Errno::EWOULDBLOCK
567
+ # Client is still cleanly connected as far as we can tell without writing to the socket
568
+ ensure
569
+ index += 1
570
+ end
571
+ end
572
+
573
+ # Delete any dead sockets
574
+ indexes_to_delete.each do |index_to_delete|
575
+ @write_interface_infos.delete_at(index_to_delete)
576
+ end
577
+ end # connection_mutex.synchronize
578
+
579
+ # Sleep until we receive a packet or for 100ms
580
+ @write_mutex.synchronize do
581
+ @write_condition_variable.wait(@write_mutex, 0.1)
582
+ end
583
+ end
584
+
585
+ def write_to_clients(method, packet_or_data)
586
+ @connection_mutex.synchronize do
587
+ # Send data to each client - On error drop the client
588
+ indexes_to_delete = []
589
+ index = 0
590
+ @write_interface_infos.each do |interface_info|
591
+ need_disconnect = false
592
+ begin
593
+ interface_bytes_written = interface_info.interface.bytes_written
594
+ interface_info.interface.send(method, packet_or_data)
595
+ diff = interface_info.interface.bytes_written - interface_bytes_written
596
+ @written_raw_data_time = interface_info.interface.written_raw_data_time
597
+ @written_raw_data = interface_info.interface.written_raw_data
598
+ @bytes_written += diff
599
+ rescue Errno::EPIPE, Errno::ECONNABORTED, IOError, Errno::ECONNRESET
600
+ # Client has normally disconnected
601
+ need_disconnect = true
602
+ rescue Exception => err
603
+ if err.message != "Stream not connected for write_raw"
604
+ Logger.instance.error "Error sending to client: #{err.class} #{err.message}"
605
+ end
606
+ need_disconnect = true
607
+ end
608
+
609
+ if need_disconnect
610
+ Logger.instance.info "Tcpip server lost write connection to #{interface_info.hostname}(#{interface_info.host_ip}):#{interface_info.port}"
611
+ interface_info.interface.disconnect
612
+ interface_info.interface.raw_logger_pair.stop if interface_info.interface.raw_logger_pair
613
+ indexes_to_delete.unshift(index) # Put later indexes at front of array
614
+ end
615
+ index += 1
616
+ end
617
+
618
+ # Delete any dead sockets
619
+ indexes_to_delete.each do |index_to_delete|
620
+ @write_interface_infos.delete_at(index_to_delete)
621
+ end
622
+ end # connection_mutex.synchronize
623
+ end
624
+ end
625
+ end