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.
- checksums.yaml +4 -4
- data/.rubocop.yml +23 -0
- data/.travis.yml +1 -0
- data/.yardopts +3 -0
- data/Gemfile +1 -1
- data/Manifest.txt +137 -52
- data/Rakefile +50 -44
- data/autohotkey/config/system/system.txt +0 -5
- data/autohotkey/config/targets/INST/cmd_tlm/inst_cmds.txt +6 -1
- data/autohotkey/config/targets/INST/screens/extra.txt +19 -0
- data/autohotkey/config/targets/INST/sequences/run_sequence.txt +1 -0
- data/autohotkey/config/targets/META/screens/data.txt +12 -0
- data/autohotkey/config/targets/SYSTEM/cmd_tlm/meta_cmd_tlm.txt +16 -0
- data/autohotkey/config/targets/{COSMOS/cmd_tlm/cosmos_server_cmds.txt → SYSTEM/cmd_tlm/system_cmds.txt} +8 -8
- data/autohotkey/config/targets/SYSTEM/cmd_tlm/system_tlm.txt +7 -0
- data/autohotkey/config/targets/SYSTEM/screens/limits_change.txt +14 -0
- data/autohotkey/config/targets/SYSTEM/screens/meta.txt +14 -0
- data/autohotkey/config/targets/SYSTEM/target.txt +12 -0
- data/autohotkey/config/tools/cmd_tlm_server/cmd_tlm_server.txt +2 -2
- data/autohotkey/config/tools/test_runner/test_runner2.txt +1 -1
- data/autohotkey/tools/CmdSequence +14 -0
- data/autohotkey/tools/CmdSequenceAHK +23 -0
- data/autohotkey/tools/CmdSequenceAHK2 +16 -0
- data/autohotkey/tools/cmd_extractor.ahk +2 -2
- data/autohotkey/tools/cmd_sender.ahk +4 -6
- data/autohotkey/tools/cmd_sequence.ahk +215 -0
- data/autohotkey/tools/cmd_sequence2.ahk +23 -0
- data/autohotkey/tools/data_viewer.ahk +2 -3
- data/autohotkey/tools/limits_monitor.ahk +9 -11
- data/autohotkey/tools/open_gl_builder.ahk +1 -2
- data/autohotkey/tools/packet_viewer.ahk +51 -35
- data/autohotkey/tools/replay.ahk +1 -2
- data/autohotkey/tools/script_runner.ahk +1 -2
- data/autohotkey/tools/script_runner2.ahk +1 -2
- data/autohotkey/tools/test_runner2.ahk +1 -5
- data/autohotkey/tools/test_runner3.ahk +1 -3
- data/autohotkey/tools/tlm_grapher.ahk +1 -3
- data/autohotkey/tools/tlm_grapher3.ahk +1 -2
- data/autohotkey/tools/tlm_viewer.ahk +8 -3
- data/autohotkey/tools/tlm_viewer2.ahk +2 -3
- data/autohotkey/tools/tlm_viewer5.ahk +1 -2
- data/cosmos.gemspec +26 -20
- data/data/cmd_sequence.png +0 -0
- data/data/config/_array_params.yaml +23 -0
- data/data/config/_id_items.yaml +24 -0
- data/data/config/_id_params.yaml +58 -0
- data/data/config/_interfaces.yaml +206 -0
- data/data/config/_items.yaml +20 -0
- data/data/config/_params.yaml +58 -0
- data/data/config/cmd_tlm_server.yaml +110 -0
- data/data/config/command.yaml +38 -0
- data/data/config/command_modifiers.yaml +127 -0
- data/data/config/command_telemetry.yaml +3 -0
- data/data/config/data_viewer.yaml +43 -0
- data/data/config/handbook_creator.yaml +23 -0
- data/data/config/housekeeping_params.yaml +71 -0
- data/data/config/interface_modifiers.yaml +44 -0
- data/data/config/item_modifiers.yaml +172 -0
- data/data/config/launcher.yaml +117 -0
- data/data/config/limits_monitor.yaml +53 -0
- data/data/config/linegraph_params.yaml +30 -0
- data/data/config/linegraph_plot.yaml +106 -0
- data/data/config/page_modifiers.yaml +128 -0
- data/data/config/param_item_modifiers.yaml +41 -0
- data/data/config/parameter_modifiers.yaml +144 -0
- data/data/config/protocols.yaml +257 -0
- data/data/config/screen.yaml +151 -0
- data/data/config/script_runner.yaml +15 -0
- data/data/config/system.yaml +153 -0
- data/data/config/table_manager.yaml +76 -0
- data/data/config/table_parameter_modifiers.yaml +9 -0
- data/data/config/target.yaml +71 -0
- data/data/config/telemetry.yaml +73 -0
- data/data/config/telemetry_modifiers.yaml +129 -0
- data/data/config/test_runner.yaml +118 -0
- data/data/config/tlm_extractor.yaml +109 -0
- data/data/config/tlm_grapher.yaml +78 -0
- data/data/config/tlm_viewer.yaml +107 -0
- data/data/config/unknown.yaml +3 -0
- data/data/config/widgets.yaml +1339 -0
- data/data/config/xy_params.yaml +50 -0
- data/data/config/xy_plot.yaml +12 -0
- data/data/config_editor.png +0 -0
- data/data/crc.txt +172 -161
- data/data/delete.png +0 -0
- data/demo/config/data/crc.txt +56 -36
- data/demo/config/data/meta_init.txt +1 -4
- data/demo/config/system/system.txt +15 -3
- data/demo/config/system/system2.txt +13 -3
- data/demo/config/targets/EXAMPLE/lib/example_interface.rb +2 -6
- data/demo/config/targets/EXAMPLE/target.txt +3 -1
- data/demo/config/targets/INST/cmd_tlm/inst_cmds.txt +1 -0
- data/demo/config/targets/INST/lib/inst_dump_component.rb +34 -0
- data/demo/config/targets/INST/screens/adcs.txt +39 -15
- data/demo/config/targets/INST/screens/commanding.txt +26 -19
- data/demo/config/targets/INST/screens/spacing_box.txt +44 -0
- data/demo/config/targets/INST/screens/spacing_grid.txt +78 -0
- data/demo/config/targets/INST/screens/tabs.txt +0 -2
- data/demo/config/targets/INST/sequences/sequence.tsv +3 -0
- data/demo/config/targets/INST/tables/EventAction.csv +9 -0
- data/demo/config/targets/INST/tables/EventAction.dat +0 -0
- data/demo/config/targets/INST/tables/McConfigTable.csv +20 -0
- data/demo/config/targets/INST/tables/McConfigTable.dat +0 -0
- data/demo/config/targets/INST/target.txt +4 -0
- data/demo/config/targets/INST/tools/data_viewer/data_viewer.txt +2 -0
- data/demo/config/targets/INST/tools/data_viewer/data_viewer2.txt +2 -0
- data/demo/config/targets/INST/tools/table_manager/EventAction_def.txt +6 -0
- data/demo/config/targets/INST/tools/table_manager/McConfigTable_def.txt +38 -0
- data/demo/config/targets/SYSTEM/cmd_tlm/limits_groups.txt +7 -3
- data/demo/config/targets/SYSTEM/cmd_tlm/meta_cmd_tlm.txt +16 -0
- data/demo/config/targets/{COSMOS/cmd_tlm/cosmos_server_cmds.txt → SYSTEM/cmd_tlm/system_cmds.txt} +8 -8
- data/demo/config/targets/SYSTEM/cmd_tlm/system_tlm.txt +7 -0
- data/demo/config/targets/{COSMOS → SYSTEM}/cmd_tlm_server.txt +2 -2
- data/demo/config/targets/SYSTEM/lib/limits_groups.rb +39 -0
- data/demo/config/targets/SYSTEM/screens/status.txt +1 -1
- data/demo/config/targets/SYSTEM/target.txt +12 -0
- data/demo/config/targets/TEMPLATED/cmd_tlm_server.txt +1 -1
- data/demo/config/targets/TEMPLATED/lib/templated_interface.rb +8 -5
- data/demo/config/targets/TEMPLATED/target.txt +2 -0
- data/demo/config/tools/cmd_tlm_server/cmd_tlm_server.txt +15 -6
- data/demo/config/tools/cmd_tlm_server/cmd_tlm_server2.txt +13 -5
- data/demo/config/tools/data_viewer/data_viewer.txt +9 -0
- data/demo/config/tools/launcher/launcher.txt +9 -6
- data/demo/config/tools/launcher/launcher2.txt +16 -13
- data/demo/config/tools/launcher/launcher_mini.txt +45 -0
- data/demo/config/tools/table_manager/MCConfigurationTable_fsw1_def.txt +12 -11
- data/demo/config/tools/table_manager/MCConfigurationTable_fsw2_def.txt +12 -11
- data/demo/config/tools/test_runner/test_runner.txt +1 -1
- data/demo/config/tools/tlm_viewer/tlm_viewer.txt +5 -5
- data/demo/lib/example_background_task.rb +9 -5
- data/demo/lib/example_target.rb +5 -15
- data/demo/lib/scpi_target.rb +4 -10
- data/demo/procedures/cosmos_api_test.rb +17 -0
- data/demo/tools/CmdSequence +16 -0
- data/demo/tools/CmdSequence.bat +9 -0
- data/demo/tools/ConfigEditor +16 -0
- data/demo/tools/ConfigEditor.bat +9 -0
- data/demo/tools/mac/CmdSequence.app/Contents/Info.plist +38 -0
- data/demo/tools/mac/CmdSequence.app/Contents/MacOS/CmdSequence.rb +16 -0
- data/demo/tools/mac/CmdSequence.app/Contents/MacOS/main.sh +10 -0
- data/demo/tools/mac/CmdSequence.app/Contents/MacOS/tool_launch.rb +38 -0
- data/demo/tools/mac/CmdSequence.app/Contents/Resources/appIcon.icns +0 -0
- data/ext/cosmos/ext/packet/packet.c +5 -5
- data/install/config/data/crc.txt +12 -8
- data/install/config/system/system.txt +13 -3
- data/install/config/targets/SYSTEM/cmd_tlm/meta_cmd_tlm.txt +14 -0
- data/install/config/targets/SYSTEM/target.txt +12 -0
- data/install/tools/CmdSequence +16 -0
- data/install/tools/CmdSequence.bat +9 -0
- data/install/tools/ConfigEditor +16 -0
- data/install/tools/ConfigEditor.bat +9 -0
- data/install/tools/mac/CmdSequence.app/Contents/Info.plist +38 -0
- data/install/tools/mac/CmdSequence.app/Contents/MacOS/CmdSequence.rb +16 -0
- data/install/tools/mac/CmdSequence.app/Contents/MacOS/main.sh +10 -0
- data/install/tools/mac/CmdSequence.app/Contents/MacOS/tool_launch.rb +38 -0
- data/install/tools/mac/CmdSequence.app/Contents/Resources/appIcon.icns +0 -0
- data/lib/cosmos.rb +1 -1
- data/lib/cosmos/config/config_parser.rb +147 -59
- data/lib/cosmos/config/meta_config_parser.rb +57 -0
- data/lib/cosmos/conversions/polynomial_conversion.rb +20 -4
- data/lib/cosmos/conversions/unix_time_conversion.rb +4 -4
- data/lib/cosmos/core_ext/array.rb +45 -5
- data/lib/cosmos/core_ext/cosmos_io.rb +31 -15
- data/lib/cosmos/core_ext/file.rb +2 -2
- data/lib/cosmos/core_ext/kernel.rb +1 -6
- data/lib/cosmos/core_ext/objectspace.rb +0 -2
- data/lib/cosmos/core_ext/string.rb +27 -4
- data/lib/cosmos/core_ext/time.rb +39 -10
- data/lib/cosmos/gui/choosers/combobox_chooser.rb +37 -26
- data/lib/cosmos/gui/choosers/file_chooser.rb +23 -6
- data/lib/cosmos/gui/choosers/float_chooser.rb +13 -11
- data/lib/cosmos/gui/choosers/integer_chooser.rb +13 -11
- data/lib/cosmos/gui/choosers/string_chooser.rb +18 -36
- data/lib/cosmos/gui/choosers/telemetry_chooser.rb +64 -64
- data/lib/cosmos/gui/choosers/value_chooser.rb +15 -15
- data/lib/cosmos/gui/dialogs/about_dialog.rb +18 -13
- data/lib/cosmos/gui/dialogs/calendar_dialog.rb +11 -3
- data/lib/cosmos/gui/dialogs/cmd_tlm_raw_dialog.rb +1 -1
- data/lib/cosmos/gui/dialogs/details_dialog.rb +1 -1
- data/lib/cosmos/gui/dialogs/exception_dialog.rb +7 -7
- data/lib/cosmos/gui/dialogs/find_replace_dialog.rb +20 -15
- data/lib/cosmos/gui/dialogs/interface_raw_dialog.rb +143 -0
- data/lib/cosmos/gui/dialogs/legal_dialog.rb +6 -5
- data/lib/cosmos/gui/dialogs/packet_log_dialog.rb +5 -2
- data/lib/cosmos/gui/dialogs/progress_dialog.rb +1 -1
- data/lib/cosmos/gui/dialogs/pry_dialog.rb +4 -4
- data/lib/cosmos/gui/dialogs/scroll_text_dialog.rb +3 -0
- data/lib/cosmos/gui/dialogs/set_tlm_dialog.rb +7 -6
- data/lib/cosmos/gui/dialogs/splash.rb +1 -1
- data/lib/cosmos/gui/dialogs/tlm_details_dialog.rb +1 -1
- data/lib/cosmos/gui/dialogs/tlm_graph_dialog.rb +114 -0
- data/lib/cosmos/gui/line_graph/line_graph.rb +9 -10
- data/lib/cosmos/gui/line_graph/line_graph_dialog.rb +7 -5
- data/lib/cosmos/gui/line_graph/line_graph_drawing.rb +3 -7
- data/lib/cosmos/gui/line_graph/line_graph_popups.rb +3 -8
- data/lib/cosmos/gui/line_graph/line_graph_scaling.rb +2 -7
- data/lib/cosmos/gui/line_graph/overview_graph.rb +6 -1
- data/lib/cosmos/gui/opengl/earth_model.rb +6 -3
- data/lib/cosmos/gui/opengl/gl_bounds.rb +11 -23
- data/lib/cosmos/gui/opengl/gl_light.rb +3 -4
- data/lib/cosmos/gui/opengl/gl_material.rb +3 -4
- data/lib/cosmos/gui/opengl/gl_scene.rb +10 -4
- data/lib/cosmos/gui/opengl/gl_shape.rb +6 -2
- data/lib/cosmos/gui/opengl/gl_viewer.rb +5 -5
- data/lib/cosmos/gui/opengl/gl_viewport.rb +11 -12
- data/lib/cosmos/gui/opengl/moon_model.rb +6 -3
- data/lib/cosmos/gui/opengl/stl_reader.rb +8 -9
- data/lib/cosmos/gui/opengl/stl_shape.rb +4 -5
- data/lib/cosmos/gui/opengl/texture_mapped_sphere.rb +7 -7
- data/lib/cosmos/gui/qt.rb +1 -1
- data/lib/cosmos/gui/qt_tool.rb +21 -10
- data/lib/cosmos/gui/text/completion.rb +23 -2
- data/lib/cosmos/gui/text/completion_text_edit.rb +38 -23
- data/lib/cosmos/gui/utilities/analyze_log.rb +1 -1
- data/lib/cosmos/gui/utilities/screenshot.rb +2 -2
- data/lib/cosmos/gui/widgets/full_text_search_line_edit.rb +11 -1
- data/lib/cosmos/gui/widgets/packet_log_frame.rb +19 -6
- data/lib/cosmos/interfaces.rb +10 -0
- data/lib/cosmos/interfaces/cmd_tlm_server_interface.rb +28 -47
- data/lib/cosmos/interfaces/interface.rb +240 -22
- data/lib/cosmos/interfaces/linc_interface.rb +3 -5
- data/lib/cosmos/interfaces/protocols/burst_protocol.rb +173 -0
- data/lib/cosmos/interfaces/protocols/crc_protocol.rb +141 -0
- data/lib/cosmos/{streams/fixed_stream_protocol.rb → interfaces/protocols/fixed_protocol.rb} +40 -37
- data/lib/cosmos/{streams/length_stream_protocol.rb → interfaces/protocols/length_protocol.rb} +55 -48
- data/lib/cosmos/interfaces/protocols/override_protocol.rb +52 -0
- data/lib/cosmos/interfaces/protocols/preidentified_protocol.rb +141 -0
- data/lib/cosmos/interfaces/protocols/protocol.rb +60 -0
- data/lib/cosmos/interfaces/protocols/template_protocol.rb +209 -0
- data/lib/cosmos/interfaces/protocols/terminated_protocol.rb +81 -0
- data/lib/cosmos/interfaces/serial_interface.rb +28 -23
- data/lib/cosmos/interfaces/simulated_target_interface.rb +27 -16
- data/lib/cosmos/interfaces/stream_interface.rb +36 -108
- data/lib/cosmos/interfaces/tcpip_client_interface.rb +21 -21
- data/lib/cosmos/interfaces/tcpip_server_interface.rb +555 -94
- data/lib/cosmos/interfaces/udp_interface.rb +51 -83
- data/lib/cosmos/io/buffered_file.rb +92 -2
- data/lib/cosmos/io/json_drb.rb +2 -2
- data/lib/cosmos/io/posix_serial_driver.rb +3 -1
- data/lib/cosmos/io/raw_logger.rb +3 -3
- data/lib/cosmos/io/serial_driver.rb +14 -5
- data/lib/cosmos/io/win32_serial_driver.rb +16 -4
- data/lib/cosmos/packet_logs.rb +0 -1
- data/lib/cosmos/packet_logs/packet_log_reader.rb +11 -1
- data/lib/cosmos/packet_logs/packet_log_writer.rb +31 -13
- data/lib/cosmos/packets/binary_accessor.rb +599 -32
- data/lib/cosmos/packets/commands.rb +48 -24
- data/lib/cosmos/packets/packet.rb +140 -54
- data/lib/cosmos/packets/packet_config.rb +0 -2
- data/lib/cosmos/packets/parsers/packet_item_parser.rb +10 -2
- data/lib/cosmos/packets/structure.rb +81 -33
- data/lib/cosmos/packets/structure_item.rb +45 -5
- data/lib/cosmos/packets/telemetry.rb +149 -55
- data/lib/cosmos/script/api_shared.rb +1000 -0
- data/lib/cosmos/script/commands.rb +2 -2
- data/lib/cosmos/script/extract.rb +19 -4
- data/lib/cosmos/script/limits.rb +2 -0
- data/lib/cosmos/script/script.rb +1 -1
- data/lib/cosmos/script/scripting.rb +4 -784
- data/lib/cosmos/script/telemetry.rb +44 -23
- data/lib/cosmos/script/tools.rb +15 -69
- data/lib/cosmos/streams/serial_stream.rb +12 -19
- data/lib/cosmos/streams/stream.rb +2 -11
- data/lib/cosmos/streams/tcpip_socket_stream.rb +3 -13
- data/lib/cosmos/system/system.rb +187 -31
- data/lib/cosmos/system/target.rb +11 -2
- data/lib/cosmos/tools/cmd_extractor/cmd_extractor.rb +12 -11
- data/lib/cosmos/tools/cmd_sender/{cmd_sender_item_delegate.rb → cmd_param_table_item_delegate.rb} +11 -10
- data/lib/cosmos/tools/cmd_sender/cmd_sender.rb +209 -164
- data/lib/cosmos/tools/cmd_sequence/cmd_sequence.rb +652 -0
- data/lib/cosmos/tools/cmd_sequence/sequence_item.rb +510 -0
- data/lib/cosmos/tools/cmd_sequence/sequence_list.rb +194 -0
- data/lib/cosmos/tools/cmd_tlm_server/api.rb +179 -5
- data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server.rb +31 -14
- data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_config.rb +23 -16
- data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_gui.rb +92 -20
- data/lib/cosmos/tools/cmd_tlm_server/commanding.rb +1 -1
- data/lib/cosmos/tools/cmd_tlm_server/gui/interfaces_tab.rb +17 -4
- data/lib/cosmos/tools/cmd_tlm_server/gui/targets_tab.rb +0 -5
- data/lib/cosmos/tools/cmd_tlm_server/interface_thread.rb +1 -2
- data/lib/cosmos/tools/cmd_tlm_server/interfaces.rb +4 -4
- data/lib/cosmos/tools/cmd_tlm_server/limits_groups_background_task.rb +121 -0
- data/lib/cosmos/tools/cmd_tlm_server/routers.rb +8 -4
- data/lib/cosmos/tools/config_editor/config_editor.rb +720 -0
- data/lib/cosmos/tools/config_editor/config_editor_frame.rb +675 -0
- data/lib/cosmos/tools/data_viewer/data_viewer.rb +44 -27
- data/lib/cosmos/tools/data_viewer/data_viewer_component.rb +8 -22
- data/lib/cosmos/tools/launcher/launcher.rb +29 -12
- data/lib/cosmos/tools/launcher/launcher_config.rb +1 -1
- data/lib/cosmos/tools/limits_monitor/limits_monitor.rb +153 -42
- data/lib/cosmos/tools/packet_viewer/packet_viewer.rb +44 -6
- data/lib/cosmos/tools/replay/replay.rb +36 -20
- data/lib/cosmos/tools/replay/replay_server.rb +1 -1
- data/lib/cosmos/tools/script_runner/script_runner_config.rb +1 -1
- data/lib/cosmos/tools/script_runner/script_runner_frame.rb +31 -21
- data/lib/cosmos/tools/table_manager/table_config.rb +9 -3
- data/lib/cosmos/tools/table_manager/table_manager.rb +27 -7
- data/lib/cosmos/tools/test_runner/results_writer.rb +6 -6
- data/lib/cosmos/tools/test_runner/test_runner.rb +4 -6
- data/lib/cosmos/tools/tlm_extractor/tlm_extractor.rb +4 -5
- data/lib/cosmos/tools/tlm_extractor/tlm_extractor_config.rb +1 -1
- data/lib/cosmos/tools/tlm_extractor/tlm_extractor_processor.rb +1 -1
- data/lib/cosmos/tools/tlm_grapher/data_object_adders/housekeeping_data_object_adder.rb +23 -6
- data/lib/cosmos/tools/tlm_grapher/data_object_editors/housekeeping_data_object_editor.rb +44 -3
- data/lib/cosmos/tools/tlm_grapher/data_objects/housekeeping_data_object.rb +20 -7
- data/lib/cosmos/tools/tlm_grapher/tabbed_plots/overview_tabbed_plots.rb +1 -1
- data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_config.rb +11 -4
- data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_plot_editor.rb +2 -2
- data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_realtime_thread.rb +1 -1
- data/lib/cosmos/tools/tlm_grapher/tlm_grapher.rb +16 -0
- data/lib/cosmos/tools/tlm_viewer/screen.rb +36 -32
- data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +59 -50
- data/lib/cosmos/tools/tlm_viewer/tlm_viewer_config.rb +2 -2
- data/lib/cosmos/tools/tlm_viewer/widgets.rb +1 -0
- data/lib/cosmos/tools/tlm_viewer/widgets/canvasvalue_widget.rb +1 -0
- data/lib/cosmos/tools/tlm_viewer/widgets/labelvalue_widget.rb +22 -4
- data/lib/cosmos/tools/tlm_viewer/widgets/matrixbycolumns_widget.rb +9 -0
- data/lib/cosmos/tools/tlm_viewer/widgets/spacer_widget.rb +55 -0
- data/lib/cosmos/tools/tlm_viewer/widgets/vertical_widget.rb +3 -2
- data/lib/cosmos/tools/tlm_viewer/widgets/verticalbox_widget.rb +3 -2
- data/lib/cosmos/tools/tlm_viewer/widgets/widget.rb +12 -12
- data/lib/cosmos/top_level.rb +34 -24
- data/lib/cosmos/utilities/crc.rb +108 -6
- data/lib/cosmos/utilities/csv.rb +68 -14
- data/lib/cosmos/utilities/logger.rb +2 -2
- data/lib/cosmos/utilities/low_fragmentation_array.rb +9 -1
- data/lib/cosmos/version.rb +6 -6
- data/lib/cosmos/win32/win32_main.rb +50 -46
- data/run_gui_tests.bat +3 -1
- data/spec/conversions/unix_time_formatted_conversion_spec.rb +2 -2
- data/spec/conversions/unix_time_seconds_conversion_spec.rb +2 -2
- data/spec/core_ext/file_spec.rb +1 -1
- data/spec/core_ext/objectspace_spec.rb +12 -9
- data/spec/core_ext/string_spec.rb +6 -0
- data/spec/core_ext/time_spec.rb +10 -0
- data/spec/gui/line_graph/line_clip_spec.rb +226 -224
- data/spec/gui/qt_spec.rb +81 -79
- data/spec/install/config/system/system.txt +0 -6
- data/spec/install/config/targets/INST/cmd_tlm/inst_cmd_linc.txt +5 -5
- data/spec/install/config/targets/INST/cmd_tlm/inst_tlm_linc.txt +8 -8
- data/spec/install/config/targets/SYSTEM/cmd_tlm/meta_cmd_tlm.txt +16 -0
- data/{install/config/targets/COSMOS/cmd_tlm/cosmos_server_cmds.txt → spec/install/config/targets/SYSTEM/cmd_tlm/system_cmds.txt} +8 -8
- data/spec/install/config/targets/SYSTEM/cmd_tlm/system_tlm.txt +7 -0
- data/spec/install/config/targets/{COSMOS → SYSTEM}/cmd_tlm_server.txt +2 -2
- data/spec/install/config/targets/SYSTEM/screens/status.txt +12 -0
- data/spec/install/config/targets/SYSTEM/target.txt +12 -0
- data/spec/interfaces/cmd_tlm_server_interface_spec.rb +9 -13
- data/spec/interfaces/interface_spec.rb +402 -18
- data/spec/interfaces/linc_interface_spec.rb +37 -39
- data/spec/interfaces/protocols/burst_protocol_spec.rb +300 -0
- data/spec/interfaces/protocols/crc_protocol_spec.rb +709 -0
- data/spec/interfaces/protocols/fixed_protocol_spec.rb +119 -0
- data/spec/interfaces/protocols/length_protocol_spec.rb +499 -0
- data/spec/interfaces/protocols/override_protocol_spec.rb +158 -0
- data/spec/interfaces/protocols/preidentified_protocol_spec.rb +149 -0
- data/spec/interfaces/protocols/template_protocol_spec.rb +218 -0
- data/spec/interfaces/protocols/terminated_protocol_spec.rb +174 -0
- data/spec/interfaces/serial_interface_spec.rb +35 -34
- data/spec/interfaces/simulated_target_interface_spec.rb +13 -13
- data/spec/interfaces/tcpip_client_interface_spec.rb +21 -16
- data/spec/interfaces/tcpip_server_interface_spec.rb +66 -69
- data/spec/interfaces/udp_interface_spec.rb +120 -55
- data/spec/io/serial_driver_spec.rb +41 -39
- data/spec/io/udp_sockets_spec.rb +13 -8
- data/spec/io/win32_serial_driver_spec.rb +62 -59
- data/spec/packet_logs/packet_log_reader_spec.rb +68 -47
- data/spec/packet_logs/packet_log_writer_spec.rb +7 -5
- data/spec/packets/commands_spec.rb +5 -5
- data/spec/packets/packet_spec.rb +2 -14
- data/spec/script/extract_spec.rb +21 -7
- data/spec/script/scripting_spec.rb +261 -6
- data/spec/script/telemetry_spec.rb +17 -9
- data/spec/spec_helper.rb +26 -10
- data/spec/streams/serial_stream_spec.rb +87 -82
- data/spec/streams/tcpip_client_stream_spec.rb +12 -4
- data/spec/streams/tcpip_socket_stream_spec.rb +5 -0
- data/spec/system/system_spec.rb +66 -50
- data/spec/system/target_spec.rb +33 -11
- data/spec/tools/cmd_tlm_server/api_spec.rb +5 -5
- data/spec/tools/cmd_tlm_server/background_tasks_spec.rb +75 -15
- data/spec/tools/cmd_tlm_server/cmd_tlm_server_config_spec.rb +125 -5
- data/spec/tools/cmd_tlm_server/cmd_tlm_server_spec.rb +244 -232
- data/spec/tools/cmd_tlm_server/commanding_spec.rb +18 -18
- data/spec/tools/cmd_tlm_server/interface_thread_spec.rb +124 -29
- data/spec/tools/cmd_tlm_server/interfaces_spec.rb +2 -2
- data/spec/tools/cmd_tlm_server/limits_groups_background_task_spec.rb +145 -0
- data/spec/tools/cmd_tlm_server/router_thread_spec.rb +50 -10
- data/spec/tools/table_manager/tablemanager_core_spec.rb +0 -1
- data/spec/top_level/top_level_spec.rb +39 -11
- data/spec/utilities/csv_spec.rb +62 -20
- data/tasks/gemfile_stats.rake +6 -3
- data/test/performance/config/system/system_packets.txt +0 -1
- data/test/performance/config/system/system_threads.txt +0 -1
- metadata +177 -92
- data/autohotkey/config/targets/COSMOS/cmd_tlm/cosmos_server_tlm.txt +0 -15
- data/autohotkey/config/targets/COSMOS/cmd_tlm_server.txt +0 -6
- data/autohotkey/config/targets/COSMOS/target.txt +0 -5
- data/autohotkey/userpath.txt +0 -1
- data/demo/config/targets/COSMOS/cmd_tlm/cosmos_server_tlm.txt +0 -15
- data/demo/config/targets/COSMOS/screens/limits_change.txt +0 -20
- data/demo/config/targets/COSMOS/screens/version.txt +0 -19
- data/demo/config/targets/COSMOS/target.txt +0 -11
- data/demo/config/targets/META/cmd_tlm/meta_cmd.txt +0 -10
- data/demo/config/targets/META/cmd_tlm/meta_tlm.txt +0 -13
- data/demo/userpath.txt +0 -1
- data/install/config/targets/COSMOS/cmd_tlm/cosmos_server_tlm.txt +0 -15
- data/install/config/targets/COSMOS/cmd_tlm_server.txt +0 -6
- data/install/config/targets/COSMOS/screens/limits_change.txt +0 -20
- data/install/config/targets/COSMOS/screens/version.txt +0 -19
- data/install/config/targets/COSMOS/target.txt +0 -9
- data/install/config/targets/SYSTEM/README.txt +0 -1
- data/install/userpath.txt +0 -1
- data/lib/cosmos/io/tcpip_server.rb +0 -571
- data/lib/cosmos/packet_logs/meta_packet_log_writer.rb +0 -107
- data/lib/cosmos/streams/burst_stream_protocol.rb +0 -25
- data/lib/cosmos/streams/preidentified_stream_protocol.rb +0 -118
- data/lib/cosmos/streams/stream_protocol.rb +0 -373
- data/lib/cosmos/streams/template_stream_protocol.rb +0 -140
- data/lib/cosmos/streams/terminated_stream_protocol.rb +0 -85
- data/spec/install/config/targets/COSMOS/cmd_tlm/cosmos_server_cmds.txt +0 -41
- data/spec/install/config/targets/COSMOS/cmd_tlm/cosmos_server_tlm.txt +0 -15
- data/spec/install/config/targets/COSMOS/screens/limits_change.txt +0 -20
- data/spec/install/config/targets/COSMOS/screens/version.txt +0 -19
- data/spec/install/config/targets/COSMOS/target.txt +0 -5
- data/spec/install/config/targets/META/cmd_tlm/meta_cmd.txt +0 -4
- data/spec/install/config/targets/META/cmd_tlm/meta_tlm.txt +0 -4
- data/spec/install/userpath.txt +0 -1
- data/spec/interfaces/stream_interface_spec.rb +0 -157
- data/spec/io/tcpip_server_spec.rb +0 -338
- data/spec/packet_logs/meta_packet_log_writer_spec.rb +0 -170
- data/spec/streams/burst_stream_protocol_spec.rb +0 -32
- data/spec/streams/fixed_stream_protocol_spec.rb +0 -113
- data/spec/streams/length_stream_protocol_spec.rb +0 -300
- data/spec/streams/preidentified_stream_protocol_spec.rb +0 -121
- data/spec/streams/stream_protocol_spec.rb +0 -346
- data/spec/streams/template_stream_protocol_spec.rb +0 -156
- data/spec/streams/terminated_stream_protocol_spec.rb +0 -127
- data/test/performance/userpath.txt +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# encoding: ascii-8bit
|
|
2
2
|
|
|
3
|
-
# Copyright
|
|
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
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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 {
|
|
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
|
-
|
|
55
|
-
|
|
55
|
+
@read_timeout
|
|
56
|
+
)
|
|
57
|
+
super()
|
|
56
58
|
end
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
end # module Cosmos
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# encoding: ascii-8bit
|
|
2
2
|
|
|
3
|
-
# Copyright
|
|
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 '
|
|
12
|
-
require '
|
|
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
|
-
#
|
|
17
|
-
|
|
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
|
-
#
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
|
|
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
|
-
|
|
30
|
-
*
|
|
31
|
-
super()
|
|
32
|
-
|
|
33
|
-
@
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@
|
|
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
|
-
#
|
|
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
|
-
@
|
|
48
|
-
@
|
|
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
|
-
#
|
|
158
|
+
# @return [Boolean] Whether the server is listening for connections
|
|
52
159
|
def connected?
|
|
53
|
-
@
|
|
160
|
+
@connected
|
|
54
161
|
end
|
|
55
162
|
|
|
56
|
-
#
|
|
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
|
-
@
|
|
59
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
176
|
+
@listen_pipes.clear
|
|
87
177
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
190
|
+
@listen_sockets.clear
|
|
191
|
+
|
|
192
|
+
# This will unblock read threads
|
|
193
|
+
shutdown_interfaces(@read_interface_infos)
|
|
106
194
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
@
|
|
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
|
-
#
|
|
113
|
-
def
|
|
114
|
-
|
|
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
|
-
#
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
#
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
#
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
#
|
|
243
|
+
# @return [Integer] The number of packets waiting on the read queue
|
|
133
244
|
def read_queue_size
|
|
134
|
-
@
|
|
245
|
+
@read_queue ? @read_queue.size : 0
|
|
135
246
|
end
|
|
136
247
|
|
|
137
|
-
#
|
|
248
|
+
# @return [Integer] The number of packets waiting on the write queue
|
|
138
249
|
def write_queue_size
|
|
139
|
-
@
|
|
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
|
-
@
|
|
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
|
-
@
|
|
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
|
-
|
|
158
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|