openc3 5.0.6
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 +7 -0
- data/Gemfile +18 -0
- data/Guardfile +35 -0
- data/LICENSE.txt +727 -0
- data/README.md +37 -0
- data/Rakefile +131 -0
- data/bin/cstol_converter +1178 -0
- data/bin/openc3cli +531 -0
- data/bin/rubysloc +139 -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 +214 -0
- data/data/config/_interfaces.yaml.err +1017 -0
- data/data/config/_items.yaml +20 -0
- data/data/config/_params.yaml +60 -0
- data/data/config/cmd_tlm_server.yaml +136 -0
- data/data/config/command.yaml +44 -0
- data/data/config/command_modifiers.yaml +160 -0
- data/data/config/command_telemetry.yaml +3 -0
- data/data/config/interface_modifiers.yaml +104 -0
- data/data/config/item_modifiers.yaml +221 -0
- data/data/config/microservice.yaml +78 -0
- data/data/config/param_item_modifiers.yaml +52 -0
- data/data/config/parameter_modifiers.yaml +200 -0
- data/data/config/plugins.yaml +80 -0
- data/data/config/protocols.yaml +290 -0
- data/data/config/screen.yaml +147 -0
- data/data/config/table_manager.yaml +89 -0
- data/data/config/table_parameter_modifiers.yaml +9 -0
- data/data/config/target.yaml +142 -0
- data/data/config/target_config.yaml +94 -0
- data/data/config/telemetry.yaml +87 -0
- data/data/config/telemetry_modifiers.yaml +159 -0
- data/data/config/tool.yaml +63 -0
- data/data/config/unknown.yaml +3 -0
- data/data/config/widgets.yaml +1505 -0
- data/ext/mkrf_conf.rb +49 -0
- data/ext/openc3/ext/array/array.c +122 -0
- data/ext/openc3/ext/array/extconf.rb +13 -0
- data/ext/openc3/ext/buffered_file/buffered_file.c +198 -0
- data/ext/openc3/ext/buffered_file/extconf.rb +13 -0
- data/ext/openc3/ext/config_parser/config_parser.c +280 -0
- data/ext/openc3/ext/config_parser/extconf.rb +13 -0
- data/ext/openc3/ext/crc/crc.c +351 -0
- data/ext/openc3/ext/crc/extconf.rb +13 -0
- data/ext/openc3/ext/openc3_io/extconf.rb +13 -0
- data/ext/openc3/ext/openc3_io/openc3_io.c +158 -0
- data/ext/openc3/ext/packet/extconf.rb +13 -0
- data/ext/openc3/ext/packet/packet.c +318 -0
- data/ext/openc3/ext/platform/extconf.rb +13 -0
- data/ext/openc3/ext/platform/platform.c +134 -0
- data/ext/openc3/ext/polynomial_conversion/extconf.rb +13 -0
- data/ext/openc3/ext/polynomial_conversion/polynomial_conversion.c +79 -0
- data/ext/openc3/ext/string/extconf.rb +13 -0
- data/ext/openc3/ext/string/string.c +63 -0
- data/ext/openc3/ext/structure/structure.c +1719 -0
- data/ext/openc3/ext/tabbed_plots_config/extconf.rb +13 -0
- data/ext/openc3/ext/tabbed_plots_config/tabbed_plots_config.c +62 -0
- data/ext/openc3/ext/telemetry/extconf.rb +13 -0
- data/ext/openc3/ext/telemetry/telemetry.c +336 -0
- data/lib/cosmos.rb +20 -0
- data/lib/cosmosc2.rb +20 -0
- data/lib/openc3/api/api.rb +39 -0
- data/lib/openc3/api/authorized_api.rb +30 -0
- data/lib/openc3/api/cmd_api.rb +451 -0
- data/lib/openc3/api/config_api.rb +58 -0
- data/lib/openc3/api/interface_api.rb +117 -0
- data/lib/openc3/api/limits_api.rb +375 -0
- data/lib/openc3/api/router_api.rb +117 -0
- data/lib/openc3/api/settings_api.rb +70 -0
- data/lib/openc3/api/target_api.rb +78 -0
- data/lib/openc3/api/tlm_api.rb +455 -0
- data/lib/openc3/bridge/bridge.rb +54 -0
- data/lib/openc3/bridge/bridge_config.rb +167 -0
- data/lib/openc3/bridge/bridge_interface_thread.rb +42 -0
- data/lib/openc3/bridge/bridge_router_thread.rb +42 -0
- data/lib/openc3/ccsds/ccsds_packet.rb +68 -0
- data/lib/openc3/ccsds/ccsds_parser.rb +148 -0
- data/lib/openc3/config/config_parser.rb +549 -0
- data/lib/openc3/config/meta_config_parser.rb +74 -0
- data/lib/openc3/conversions/conversion.rb +70 -0
- data/lib/openc3/conversions/generic_conversion.rb +83 -0
- data/lib/openc3/conversions/packet_time_formatted_conversion.rb +43 -0
- data/lib/openc3/conversions/packet_time_seconds_conversion.rb +43 -0
- data/lib/openc3/conversions/polynomial_conversion.rb +87 -0
- data/lib/openc3/conversions/processor_conversion.rb +70 -0
- data/lib/openc3/conversions/received_count_conversion.rb +38 -0
- data/lib/openc3/conversions/received_time_formatted_conversion.rb +42 -0
- data/lib/openc3/conversions/received_time_seconds_conversion.rb +42 -0
- data/lib/openc3/conversions/segmented_polynomial_conversion.rb +171 -0
- data/lib/openc3/conversions/unix_time_conversion.rb +68 -0
- data/lib/openc3/conversions/unix_time_formatted_conversion.rb +49 -0
- data/lib/openc3/conversions/unix_time_seconds_conversion.rb +49 -0
- data/lib/openc3/conversions.rb +34 -0
- data/lib/openc3/core_ext/array.rb +416 -0
- data/lib/openc3/core_ext/binding.rb +29 -0
- data/lib/openc3/core_ext/class.rb +72 -0
- data/lib/openc3/core_ext/exception.rb +61 -0
- data/lib/openc3/core_ext/file.rb +83 -0
- data/lib/openc3/core_ext/hash.rb +37 -0
- data/lib/openc3/core_ext/io.rb +134 -0
- data/lib/openc3/core_ext/kernel.rb +42 -0
- data/lib/openc3/core_ext/math.rb +128 -0
- data/lib/openc3/core_ext/matrix.rb +156 -0
- data/lib/openc3/core_ext/objectspace.rb +36 -0
- data/lib/openc3/core_ext/openc3_io.rb +57 -0
- data/lib/openc3/core_ext/range.rb +27 -0
- data/lib/openc3/core_ext/socket.rb +38 -0
- data/lib/openc3/core_ext/string.rb +389 -0
- data/lib/openc3/core_ext/stringio.rb +33 -0
- data/lib/openc3/core_ext/time.rb +508 -0
- data/lib/openc3/core_ext.rb +36 -0
- data/lib/openc3/interfaces/interface.rb +498 -0
- data/lib/openc3/interfaces/linc_interface.rb +475 -0
- data/lib/openc3/interfaces/protocols/burst_protocol.rb +192 -0
- data/lib/openc3/interfaces/protocols/crc_protocol.rb +193 -0
- data/lib/openc3/interfaces/protocols/fixed_protocol.rb +155 -0
- data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +56 -0
- data/lib/openc3/interfaces/protocols/length_protocol.rb +165 -0
- data/lib/openc3/interfaces/protocols/override_protocol.rb +60 -0
- data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +206 -0
- data/lib/openc3/interfaces/protocols/protocol.rb +82 -0
- data/lib/openc3/interfaces/protocols/template_protocol.rb +261 -0
- data/lib/openc3/interfaces/protocols/terminated_protocol.rb +93 -0
- data/lib/openc3/interfaces/serial_interface.rb +94 -0
- data/lib/openc3/interfaces/simulated_target_interface.rb +168 -0
- data/lib/openc3/interfaces/stream_interface.rb +81 -0
- data/lib/openc3/interfaces/tcpip_client_interface.rb +69 -0
- data/lib/openc3/interfaces/tcpip_server_interface.rb +629 -0
- data/lib/openc3/interfaces/udp_interface.rb +169 -0
- data/lib/openc3/interfaces.rb +44 -0
- data/lib/openc3/io/buffered_file.rb +109 -0
- data/lib/openc3/io/io_multiplexer.rb +80 -0
- data/lib/openc3/io/json_api_object.rb +208 -0
- data/lib/openc3/io/json_drb.rb +335 -0
- data/lib/openc3/io/json_drb_object.rb +114 -0
- data/lib/openc3/io/json_drb_rack.rb +84 -0
- data/lib/openc3/io/json_rpc.rb +420 -0
- data/lib/openc3/io/openc3_snmp.rb +58 -0
- data/lib/openc3/io/posix_serial_driver.rb +156 -0
- data/lib/openc3/io/raw_logger.rb +167 -0
- data/lib/openc3/io/raw_logger_pair.rb +77 -0
- data/lib/openc3/io/serial_driver.rb +105 -0
- data/lib/openc3/io/stderr.rb +43 -0
- data/lib/openc3/io/stdout.rb +43 -0
- data/lib/openc3/io/udp_sockets.rb +194 -0
- data/lib/openc3/io/win32_serial_driver.rb +196 -0
- data/lib/openc3/logs/log_writer.rb +302 -0
- data/lib/openc3/logs/packet_log_constants.rb +62 -0
- data/lib/openc3/logs/packet_log_reader.rb +345 -0
- data/lib/openc3/logs/packet_log_writer.rb +299 -0
- data/lib/openc3/logs/text_log_writer.rb +68 -0
- data/lib/openc3/logs.rb +25 -0
- data/lib/openc3/microservices/cleanup_microservice.rb +68 -0
- data/lib/openc3/microservices/decom_microservice.rb +136 -0
- data/lib/openc3/microservices/interface_microservice.rb +532 -0
- data/lib/openc3/microservices/log_microservice.rb +108 -0
- data/lib/openc3/microservices/microservice.rb +204 -0
- data/lib/openc3/microservices/plugin_microservice.rb +43 -0
- data/lib/openc3/microservices/reaction_microservice.rb +541 -0
- data/lib/openc3/microservices/reducer_microservice.rb +313 -0
- data/lib/openc3/microservices/router_microservice.rb +44 -0
- data/lib/openc3/microservices/text_log_microservice.rb +84 -0
- data/lib/openc3/microservices/timeline_microservice.rb +363 -0
- data/lib/openc3/microservices/trigger_group_microservice.rb +638 -0
- data/lib/openc3/models/activity_model.rb +319 -0
- data/lib/openc3/models/auth_model.rb +65 -0
- data/lib/openc3/models/cvt_model.rb +185 -0
- data/lib/openc3/models/environment_model.rb +58 -0
- data/lib/openc3/models/gem_model.rb +137 -0
- data/lib/openc3/models/info_model.rb +31 -0
- data/lib/openc3/models/interface_model.rb +281 -0
- data/lib/openc3/models/interface_status_model.rb +117 -0
- data/lib/openc3/models/metadata_model.rb +139 -0
- data/lib/openc3/models/metric_model.rb +59 -0
- data/lib/openc3/models/microservice_model.rb +206 -0
- data/lib/openc3/models/microservice_status_model.rb +74 -0
- data/lib/openc3/models/model.rb +204 -0
- data/lib/openc3/models/note_model.rb +122 -0
- data/lib/openc3/models/notification_model.rb +40 -0
- data/lib/openc3/models/ping_model.rb +35 -0
- data/lib/openc3/models/plugin_model.rb +292 -0
- data/lib/openc3/models/process_status_model.rb +76 -0
- data/lib/openc3/models/reaction_model.rb +322 -0
- data/lib/openc3/models/reducer_model.rb +65 -0
- data/lib/openc3/models/router_model.rb +35 -0
- data/lib/openc3/models/router_status_model.rb +27 -0
- data/lib/openc3/models/scope_model.rb +153 -0
- data/lib/openc3/models/settings_model.rb +55 -0
- data/lib/openc3/models/sorted_model.rb +167 -0
- data/lib/openc3/models/target_model.rb +759 -0
- data/lib/openc3/models/timeline_model.rb +154 -0
- data/lib/openc3/models/tool_config_model.rb +38 -0
- data/lib/openc3/models/tool_model.rb +262 -0
- data/lib/openc3/models/trigger_group_model.rb +186 -0
- data/lib/openc3/models/trigger_model.rb +330 -0
- data/lib/openc3/models/widget_model.rb +138 -0
- data/lib/openc3/operators/microservice_operator.rb +128 -0
- data/lib/openc3/operators/operator.rb +277 -0
- data/lib/openc3/packets/binary_accessor.rb +1207 -0
- data/lib/openc3/packets/commands.rb +373 -0
- data/lib/openc3/packets/json_packet.rb +134 -0
- data/lib/openc3/packets/limits.rb +271 -0
- data/lib/openc3/packets/limits_response.rb +53 -0
- data/lib/openc3/packets/packet.rb +1168 -0
- data/lib/openc3/packets/packet_config.rb +625 -0
- data/lib/openc3/packets/packet_item.rb +586 -0
- data/lib/openc3/packets/packet_item_limits.rb +162 -0
- data/lib/openc3/packets/parsers/format_string_parser.rb +65 -0
- data/lib/openc3/packets/parsers/limits_parser.rb +159 -0
- data/lib/openc3/packets/parsers/limits_response_parser.rb +61 -0
- data/lib/openc3/packets/parsers/packet_item_parser.rb +272 -0
- data/lib/openc3/packets/parsers/packet_parser.rb +134 -0
- data/lib/openc3/packets/parsers/processor_parser.rb +73 -0
- data/lib/openc3/packets/parsers/state_parser.rb +127 -0
- data/lib/openc3/packets/parsers/xtce_converter.rb +442 -0
- data/lib/openc3/packets/parsers/xtce_parser.rb +722 -0
- data/lib/openc3/packets/structure.rb +553 -0
- data/lib/openc3/packets/structure_item.rb +365 -0
- data/lib/openc3/packets/telemetry.rb +487 -0
- data/lib/openc3/processors/processor.rb +86 -0
- data/lib/openc3/processors/statistics_processor.rb +82 -0
- data/lib/openc3/processors/watermark_processor.rb +58 -0
- data/lib/openc3/processors.rb +24 -0
- data/lib/openc3/script/api_shared.rb +828 -0
- data/lib/openc3/script/calendar.rb +89 -0
- data/lib/openc3/script/commands.rb +227 -0
- data/lib/openc3/script/exceptions.rb +29 -0
- data/lib/openc3/script/extract.rb +161 -0
- data/lib/openc3/script/limits.rb +60 -0
- data/lib/openc3/script/script.rb +299 -0
- data/lib/openc3/script/script_runner.rb +238 -0
- data/lib/openc3/script/storage.rb +146 -0
- data/lib/openc3/script/suite.rb +542 -0
- data/lib/openc3/script/suite_results.rb +196 -0
- data/lib/openc3/script/suite_runner.rb +217 -0
- data/lib/openc3/script.rb +21 -0
- data/lib/openc3/streams/serial_stream.rb +167 -0
- data/lib/openc3/streams/stream.rb +63 -0
- data/lib/openc3/streams/tcpip_client_stream.rb +116 -0
- data/lib/openc3/streams/tcpip_socket_stream.rb +195 -0
- data/lib/openc3/system/system.rb +127 -0
- data/lib/openc3/system/system_config.rb +411 -0
- data/lib/openc3/system/target.rb +269 -0
- data/lib/openc3/system.rb +24 -0
- data/lib/openc3/tools/cmd_tlm_server/api.rb +20 -0
- data/lib/openc3/tools/cmd_tlm_server/cmd_tlm_server_config.rb +320 -0
- data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +294 -0
- data/lib/openc3/tools/table_manager/table.rb +77 -0
- data/lib/openc3/tools/table_manager/table_config.rb +273 -0
- data/lib/openc3/tools/table_manager/table_item.rb +90 -0
- data/lib/openc3/tools/table_manager/table_item_parser.rb +66 -0
- data/lib/openc3/tools/table_manager/table_manager_core.rb +333 -0
- data/lib/openc3/tools/table_manager/table_parser.rb +93 -0
- data/lib/openc3/tools/test_runner/test.rb +67 -0
- data/lib/openc3/top_level.rb +595 -0
- data/lib/openc3/topics/autonomic_topic.rb +52 -0
- data/lib/openc3/topics/calendar_topic.rb +44 -0
- data/lib/openc3/topics/command_decom_topic.rb +76 -0
- data/lib/openc3/topics/command_topic.rb +83 -0
- data/lib/openc3/topics/config_topic.rb +68 -0
- data/lib/openc3/topics/interface_topic.rb +73 -0
- data/lib/openc3/topics/limits_event_topic.rb +109 -0
- data/lib/openc3/topics/notifications_topic.rb +28 -0
- data/lib/openc3/topics/router_topic.rb +85 -0
- data/lib/openc3/topics/telemetry_decom_topic.rb +54 -0
- data/lib/openc3/topics/telemetry_topic.rb +36 -0
- data/lib/openc3/topics/timeline_topic.rb +45 -0
- data/lib/openc3/topics/topic.rb +53 -0
- data/lib/openc3/utilities/authentication.rb +141 -0
- data/lib/openc3/utilities/authorization.rb +51 -0
- data/lib/openc3/utilities/crc.rb +278 -0
- data/lib/openc3/utilities/csv.rb +153 -0
- data/lib/openc3/utilities/logger.rb +187 -0
- data/lib/openc3/utilities/message_log.rb +91 -0
- data/lib/openc3/utilities/metric.rb +141 -0
- data/lib/openc3/utilities/process_manager.rb +139 -0
- data/lib/openc3/utilities/quaternion.rb +257 -0
- data/lib/openc3/utilities/ruby_lex_utils.rb +568 -0
- data/lib/openc3/utilities/s3.rb +202 -0
- data/lib/openc3/utilities/s3_autoload.rb +9 -0
- data/lib/openc3/utilities/s3_file_cache.rb +274 -0
- data/lib/openc3/utilities/simulated_target.rb +117 -0
- data/lib/openc3/utilities/sleeper.rb +51 -0
- data/lib/openc3/utilities/store.rb +23 -0
- data/lib/openc3/utilities/store_autoload.rb +237 -0
- data/lib/openc3/utilities/zip.rb +21 -0
- data/lib/openc3/utilities.rb +35 -0
- data/lib/openc3/version.rb +14 -0
- data/lib/openc3/win32/excel.rb +132 -0
- data/lib/openc3/win32/win32.rb +402 -0
- data/lib/openc3/win32/win32_main.rb +333 -0
- data/lib/openc3.rb +49 -0
- data/tasks/gemfile_stats.rake +113 -0
- data/tasks/spec.rake +30 -0
- data/templates/plugin-template/README.md +15 -0
- data/templates/plugin-template/Rakefile +12 -0
- data/templates/plugin-template/plugin.gemspec +23 -0
- data/templates/plugin-template/plugin.txt +9 -0
- data/templates/plugin-template/targets/TARGET/cmd_tlm/cmd.txt +8 -0
- data/templates/plugin-template/targets/TARGET/cmd_tlm/tlm.txt +8 -0
- data/templates/plugin-template/targets/TARGET/lib/target.rb +10 -0
- data/templates/plugin-template/targets/TARGET/procedures/procedure.rb +3 -0
- data/templates/plugin-template/targets/TARGET/screens/status.txt +9 -0
- data/templates/plugin-template/targets/TARGET/target.txt +5 -0
- metadata +849 -0
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
3
|
+
# Copyright 2022 Ball Aerospace & Technologies Corp.
|
|
4
|
+
# All Rights Reserved.
|
|
5
|
+
#
|
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
|
7
|
+
# under the terms of the GNU Affero General Public License
|
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
|
10
|
+
#
|
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
# GNU Affero General Public License for more details.
|
|
15
|
+
|
|
16
|
+
# Modified by OpenC3, Inc.
|
|
17
|
+
# All changes Copyright 2022, OpenC3, Inc.
|
|
18
|
+
# All Rights Reserved
|
|
19
|
+
|
|
20
|
+
require 'socket'
|
|
21
|
+
require 'thread' # For Mutex
|
|
22
|
+
require 'timeout' # For Timeout::Error
|
|
23
|
+
require 'openc3/interfaces/stream_interface'
|
|
24
|
+
require 'openc3/streams/tcpip_socket_stream'
|
|
25
|
+
require 'openc3/config/config_parser'
|
|
26
|
+
|
|
27
|
+
module OpenC3
|
|
28
|
+
# TCP/IP Server which can both read and write on a single port or two
|
|
29
|
+
# independent ports. A listen thread is setup which waits for client
|
|
30
|
+
# connections. For each connection to the read port, a thread is spawned that
|
|
31
|
+
# calls the read method from the interface. This data is then
|
|
32
|
+
# available by calling the TcpipServer read method. For each connection to the
|
|
33
|
+
# write port, a thread is spawned that calls the write method from the
|
|
34
|
+
# interface when data is send to the TcpipServer via the write method.
|
|
35
|
+
class TcpipServerInterface < StreamInterface
|
|
36
|
+
# Data class which stores the interface and associated information
|
|
37
|
+
class InterfaceInfo
|
|
38
|
+
attr_reader :interface, :hostname, :host_ip, :port
|
|
39
|
+
|
|
40
|
+
def initialize(interface, hostname, host_ip, port)
|
|
41
|
+
@interface = interface
|
|
42
|
+
@hostname = hostname
|
|
43
|
+
@host_ip = host_ip
|
|
44
|
+
@port = port
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Callback method to call when a new client connects to the write port.
|
|
49
|
+
# This method will be called with the Interface as the only argument.
|
|
50
|
+
attr_accessor :write_connection_callback
|
|
51
|
+
# Callback method to call when a new client connects to the read port.
|
|
52
|
+
# This method will be called with the Interface as the only argument.
|
|
53
|
+
attr_accessor :read_connection_callback
|
|
54
|
+
# @return [RawLoggerPair] RawLoggerPair instance or nil
|
|
55
|
+
attr_accessor :raw_logger_pair
|
|
56
|
+
# @return [String] The ip address to bind to. Default to ANY (0.0.0.0)
|
|
57
|
+
attr_accessor :listen_address
|
|
58
|
+
# @return [boolean] Automatically send SYSTEM META on connect - Default false - Can be CMD/TLM
|
|
59
|
+
attr_accessor :auto_system_meta
|
|
60
|
+
|
|
61
|
+
# @param write_port [Integer] The server write port. Clients should connect
|
|
62
|
+
# and expect to receive data from this port.
|
|
63
|
+
# @param read_port [Integer] The server read port. Clients should connect
|
|
64
|
+
# and expect to send data to this port.
|
|
65
|
+
# @param write_timeout [Float|nil] The number of seconds to wait for the
|
|
66
|
+
# write to complete. Pass nil to block until the write is complete.
|
|
67
|
+
# @param read_timeout [Float|nil] The number of seconds to wait for the
|
|
68
|
+
# read to complete. Pass nil to block until the read is complete.
|
|
69
|
+
# @param protocol_type [String] The name of the stream to
|
|
70
|
+
# use for both the read and write ports. This name is combined with
|
|
71
|
+
# 'Protocol' to result in a OpenC3 Protocol class.
|
|
72
|
+
# @param protocol_args [Array] Arguments to pass to the Protocol
|
|
73
|
+
def initialize(write_port,
|
|
74
|
+
read_port,
|
|
75
|
+
write_timeout,
|
|
76
|
+
read_timeout,
|
|
77
|
+
protocol_type = nil,
|
|
78
|
+
*protocol_args)
|
|
79
|
+
super(protocol_type, protocol_args)
|
|
80
|
+
@write_port = ConfigParser.handle_nil(write_port)
|
|
81
|
+
@write_port = Integer(write_port) if @write_port
|
|
82
|
+
@read_port = ConfigParser.handle_nil(read_port)
|
|
83
|
+
@read_port = Integer(read_port) if @read_port
|
|
84
|
+
@write_timeout = ConfigParser.handle_nil(write_timeout)
|
|
85
|
+
@write_timeout = @write_timeout.to_f if @write_timeout
|
|
86
|
+
@read_timeout = ConfigParser.handle_nil(read_timeout)
|
|
87
|
+
@read_timeout = @read_timeout.to_f if @read_timeout
|
|
88
|
+
@listen_sockets = []
|
|
89
|
+
@listen_pipes = []
|
|
90
|
+
@listen_threads = []
|
|
91
|
+
@read_threads = []
|
|
92
|
+
@write_thread = nil
|
|
93
|
+
@write_raw_thread = nil
|
|
94
|
+
@write_interface_infos = []
|
|
95
|
+
@read_interface_infos = []
|
|
96
|
+
@write_queue = nil
|
|
97
|
+
@write_queue = Queue.new if @write_port
|
|
98
|
+
@write_raw_queue = nil
|
|
99
|
+
@write_raw_queue = Queue.new if @write_port
|
|
100
|
+
@read_queue = nil
|
|
101
|
+
@read_queue = Queue.new if @read_port
|
|
102
|
+
@write_condition_variable = nil
|
|
103
|
+
@write_condition_variable = ConditionVariable.new if @write_port
|
|
104
|
+
@write_raw_mutex = nil
|
|
105
|
+
@write_raw_mutex = Mutex.new if @write_port
|
|
106
|
+
@write_raw_condition_variable = nil
|
|
107
|
+
@write_raw_condition_variable = ConditionVariable.new if @write_port
|
|
108
|
+
@write_connection_callback = nil
|
|
109
|
+
@read_connection_callback = nil
|
|
110
|
+
@raw_logger_pair = nil
|
|
111
|
+
@raw_logging_enabled = false
|
|
112
|
+
@connection_mutex = Mutex.new
|
|
113
|
+
@listen_address = "0.0.0.0"
|
|
114
|
+
@auto_system_meta = false
|
|
115
|
+
|
|
116
|
+
@read_allowed = false unless ConfigParser.handle_nil(read_port)
|
|
117
|
+
@write_allowed = false unless ConfigParser.handle_nil(write_port)
|
|
118
|
+
@write_raw_allowed = false unless ConfigParser.handle_nil(write_port)
|
|
119
|
+
|
|
120
|
+
@connected = false
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Create the read and write port listen threads. Incoming connections will
|
|
124
|
+
# spawn separate threads to process the reads and writes.
|
|
125
|
+
def connect
|
|
126
|
+
@cancel_threads = false
|
|
127
|
+
@read_queue.clear if @read_queue
|
|
128
|
+
if @write_port == @read_port # One socket
|
|
129
|
+
start_listen_thread(@read_port, true, true)
|
|
130
|
+
else
|
|
131
|
+
start_listen_thread(@write_port, true, false) if @write_port
|
|
132
|
+
start_listen_thread(@read_port, false, true) if @read_port
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
if @write_port
|
|
136
|
+
@write_thread = Thread.new do
|
|
137
|
+
loop do
|
|
138
|
+
write_thread_body()
|
|
139
|
+
break if @cancel_threads
|
|
140
|
+
end
|
|
141
|
+
rescue Exception => err
|
|
142
|
+
shutdown_interfaces(@write_interface_infos)
|
|
143
|
+
Logger.error("#{@name}: Tcpip server write thread unexpectedly died")
|
|
144
|
+
Logger.error(err.formatted)
|
|
145
|
+
end
|
|
146
|
+
@write_raw_thread = Thread.new do
|
|
147
|
+
loop do
|
|
148
|
+
write_raw_thread_body()
|
|
149
|
+
break if @cancel_threads
|
|
150
|
+
end
|
|
151
|
+
rescue Exception => err
|
|
152
|
+
shutdown_interfaces(@write_interface_infos)
|
|
153
|
+
Logger.error("#{@name}: Tcpip server write raw thread unexpectedly died")
|
|
154
|
+
Logger.error(err.formatted)
|
|
155
|
+
end
|
|
156
|
+
else
|
|
157
|
+
@write_thread = nil
|
|
158
|
+
@write_raw_thread = nil
|
|
159
|
+
end
|
|
160
|
+
@connected = true
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# @return [Boolean] Whether the server is listening for connections
|
|
164
|
+
def connected?
|
|
165
|
+
@connected
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Shutdowns the listener threads for both the read and write ports as well
|
|
169
|
+
# as any client connections.
|
|
170
|
+
def disconnect
|
|
171
|
+
@cancel_threads = true
|
|
172
|
+
@read_queue << nil if @read_queue
|
|
173
|
+
@listen_pipes.each do |pipe|
|
|
174
|
+
pipe.write('.')
|
|
175
|
+
rescue Exception
|
|
176
|
+
# Oh well
|
|
177
|
+
end
|
|
178
|
+
@listen_pipes.clear
|
|
179
|
+
|
|
180
|
+
# Shutdown listen thread(s)
|
|
181
|
+
@listen_threads.each { |listen_thread| OpenC3.kill_thread(self, listen_thread) }
|
|
182
|
+
@listen_threads.clear
|
|
183
|
+
|
|
184
|
+
# Shutdown listen socket(s)
|
|
185
|
+
@listen_sockets.each do |listen_socket|
|
|
186
|
+
OpenC3.close_socket(listen_socket)
|
|
187
|
+
rescue IOError
|
|
188
|
+
# Ok may have been closed by the thread
|
|
189
|
+
end
|
|
190
|
+
@listen_sockets.clear
|
|
191
|
+
|
|
192
|
+
# This will unblock read threads
|
|
193
|
+
shutdown_interfaces(@read_interface_infos)
|
|
194
|
+
|
|
195
|
+
@read_threads.each { |thread| OpenC3.kill_thread(self, thread) }
|
|
196
|
+
@read_threads.clear
|
|
197
|
+
if @write_thread
|
|
198
|
+
OpenC3.kill_thread(self, @write_thread)
|
|
199
|
+
@write_thread = nil
|
|
200
|
+
end
|
|
201
|
+
if @write_raw_thread
|
|
202
|
+
OpenC3.kill_thread(self, @write_raw_thread)
|
|
203
|
+
@write_raw_thread = nil
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
shutdown_interfaces(@write_interface_infos)
|
|
207
|
+
@connected = false
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Gracefully kill all the threads
|
|
211
|
+
def graceful_kill
|
|
212
|
+
# This method is just here to prevent warnings
|
|
213
|
+
end
|
|
214
|
+
|
|
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?
|
|
219
|
+
raise "Interface not readable: #{@name}" unless read_allowed?
|
|
220
|
+
|
|
221
|
+
packet = @read_queue.pop
|
|
222
|
+
return nil unless packet
|
|
223
|
+
|
|
224
|
+
@read_count += 1
|
|
225
|
+
packet
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# @param packet [Packet] Packet to write to all clients connected to the
|
|
229
|
+
# write port.
|
|
230
|
+
def write(packet)
|
|
231
|
+
raise "Interface not connected for write: #{@name}" unless connected?
|
|
232
|
+
raise "Interface not writable: #{@name}" unless write_allowed?
|
|
233
|
+
|
|
234
|
+
@write_count += 1
|
|
235
|
+
@write_queue << packet.clone
|
|
236
|
+
@write_condition_variable.broadcast
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# @param data [String] Data to write to all clients connected to the
|
|
240
|
+
# write port.
|
|
241
|
+
def write_raw(data)
|
|
242
|
+
raise "Interface not connected for write_raw: #{@name}" unless connected?
|
|
243
|
+
raise "Interface not write-rawable: #{@name}" unless write_raw_allowed?
|
|
244
|
+
|
|
245
|
+
@write_raw_queue << data
|
|
246
|
+
@write_raw_condition_variable.broadcast
|
|
247
|
+
return data
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# @return [Integer] The number of packets waiting on the read queue
|
|
251
|
+
def read_queue_size
|
|
252
|
+
@read_queue ? @read_queue.size : 0
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# @return [Integer] The number of packets waiting on the write queue
|
|
256
|
+
def write_queue_size
|
|
257
|
+
@write_queue ? @write_queue.size : 0
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# @return [Integer] The number of connected clients
|
|
261
|
+
def num_clients
|
|
262
|
+
interfaces = []
|
|
263
|
+
@write_interface_infos.each { |wii| interfaces << wii.interface }
|
|
264
|
+
@read_interface_infos.each { |rii| interfaces << rii.interface }
|
|
265
|
+
interfaces.uniq.length
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Start raw logging for this interface
|
|
269
|
+
def start_raw_logging
|
|
270
|
+
@raw_logging_enabled = true
|
|
271
|
+
change_raw_logging(:start)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Stop raw logging for this interface
|
|
275
|
+
def stop_raw_logging
|
|
276
|
+
@raw_logging_enabled = false
|
|
277
|
+
change_raw_logging(:stop)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Supported Options
|
|
281
|
+
# LISTEN_ADDRESS - Ip address of the interface to accept connections on - Default: 0.0.0.0
|
|
282
|
+
# AUTO_SYSTEM_META - Automatically send SYSTEM META on connect - Default false
|
|
283
|
+
# (see Interface#set_option)
|
|
284
|
+
def set_option(option_name, option_values)
|
|
285
|
+
super(option_name, option_values)
|
|
286
|
+
case option_name.upcase
|
|
287
|
+
when 'LISTEN_ADDRESS'
|
|
288
|
+
@listen_address = option_values[0]
|
|
289
|
+
when 'AUTO_SYSTEM_META'
|
|
290
|
+
@auto_system_meta = ConfigParser.handle_true_false(option_values[0])
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
protected
|
|
295
|
+
|
|
296
|
+
def shutdown_interfaces(interface_infos)
|
|
297
|
+
@connection_mutex.synchronize do
|
|
298
|
+
interface_infos.each do |interface_info|
|
|
299
|
+
interface_info.interface.disconnect
|
|
300
|
+
interface_info.interface.raw_logger_pair.stop if interface_info.interface.raw_logger_pair
|
|
301
|
+
end
|
|
302
|
+
interface_infos.clear
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def change_raw_logging(method)
|
|
307
|
+
if @raw_logger_pair
|
|
308
|
+
@write_interface_infos.each do |interface_info|
|
|
309
|
+
interface_info.interface.raw_logger_pair.public_send(method) if interface_info.interface.raw_logger_pair
|
|
310
|
+
end
|
|
311
|
+
@read_interface_infos.each do |interface_info|
|
|
312
|
+
interface_info.interface.raw_logger_pair.public_send(method) if interface_info.interface.raw_logger_pair
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def start_listen_thread(port, listen_write = false, listen_read = false)
|
|
318
|
+
# Create a socket to accept connections from clients
|
|
319
|
+
addr = Socket.pack_sockaddr_in(port, @listen_address)
|
|
320
|
+
if RUBY_ENGINE == 'ruby'
|
|
321
|
+
listen_socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
|
322
|
+
listen_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1) unless Kernel.is_windows?
|
|
323
|
+
begin
|
|
324
|
+
listen_socket.bind(addr)
|
|
325
|
+
rescue Errno::EADDRINUSE
|
|
326
|
+
raise "Error binding to port #{port}.\n" +
|
|
327
|
+
"Either another application is using this port\n" +
|
|
328
|
+
"or the operating system is being slow cleaning up.\n" +
|
|
329
|
+
"Make sure all sockets/streams are closed in all applications,\n" +
|
|
330
|
+
"wait 1 minute and try again."
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
listen_socket.listen(5)
|
|
334
|
+
else
|
|
335
|
+
listen_socket = ServerSocket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
|
336
|
+
listen_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1) unless Kernel.is_windows?
|
|
337
|
+
begin
|
|
338
|
+
listen_socket.bind(addr, 5)
|
|
339
|
+
rescue Errno::EADDRINUSE
|
|
340
|
+
raise "Error binding to port #{port}.\n" +
|
|
341
|
+
"Either another application is using this port\n" +
|
|
342
|
+
"or the operating system is being slow cleaning up.\n" +
|
|
343
|
+
"Make sure all sockets/streams are closed in all applications,\n" +
|
|
344
|
+
"wait 1 minute and try again."
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
@listen_sockets << listen_socket
|
|
348
|
+
@listen_threads << Thread.new do
|
|
349
|
+
thread_reader, thread_writer = IO.pipe
|
|
350
|
+
@listen_pipes << thread_writer
|
|
351
|
+
loop do
|
|
352
|
+
listen_thread_body(listen_socket, listen_write, listen_read, thread_reader)
|
|
353
|
+
break if @cancel_threads
|
|
354
|
+
end
|
|
355
|
+
rescue => err
|
|
356
|
+
Logger.error("#{@name}: Tcpip server listen thread unexpectedly died")
|
|
357
|
+
Logger.error(err.formatted)
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def listen_thread_body(listen_socket, listen_write, listen_read, thread_reader)
|
|
362
|
+
begin
|
|
363
|
+
socket, address = listen_socket.accept_nonblock
|
|
364
|
+
rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EINTR, Errno::EWOULDBLOCK
|
|
365
|
+
read_ready, _ = IO.select([listen_socket, thread_reader])
|
|
366
|
+
if read_ready && read_ready.include?(thread_reader)
|
|
367
|
+
return
|
|
368
|
+
else
|
|
369
|
+
retry
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
port, host_ip = Socket.unpack_sockaddr_in(address)
|
|
374
|
+
hostname = ''
|
|
375
|
+
hostname = Socket.lookup_hostname_from_ip(host_ip)
|
|
376
|
+
# if System.instance.acl
|
|
377
|
+
# addr = ["AF_INET", 10, "lc630", host_ip.to_s]
|
|
378
|
+
# if not System.instance.acl.allow_addr?(addr)
|
|
379
|
+
# # Reject connection
|
|
380
|
+
# OpenC3.close_socket(socket)
|
|
381
|
+
# Logger.info "#{@name}: Tcpip server rejected connection from #{hostname}(#{host_ip}):#{port}"
|
|
382
|
+
# return
|
|
383
|
+
# end
|
|
384
|
+
# end
|
|
385
|
+
|
|
386
|
+
# Configure TCP_NODELAY option
|
|
387
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
388
|
+
|
|
389
|
+
# Accept Connection
|
|
390
|
+
write_socket = nil
|
|
391
|
+
read_socket = nil
|
|
392
|
+
write_socket = socket if listen_write
|
|
393
|
+
read_socket = socket if listen_read
|
|
394
|
+
stream = TcpipSocketStream.new(write_socket, read_socket, @write_timeout, @read_timeout)
|
|
395
|
+
|
|
396
|
+
interface = StreamInterface.new
|
|
397
|
+
interface.target_names = @target_names
|
|
398
|
+
if @raw_logger_pair
|
|
399
|
+
interface.raw_logger_pair = @raw_logger_pair.clone
|
|
400
|
+
interface.raw_logger_pair.start if @raw_logging_enabled
|
|
401
|
+
end
|
|
402
|
+
@protocol_info.each do |protocol_class, protocol_args, read_write|
|
|
403
|
+
interface.add_protocol(protocol_class, protocol_args, read_write)
|
|
404
|
+
end
|
|
405
|
+
interface.stream = stream
|
|
406
|
+
interface.connect
|
|
407
|
+
|
|
408
|
+
if listen_write
|
|
409
|
+
if @auto_system_meta
|
|
410
|
+
meta_packet = System.telemetry.packet('SYSTEM', 'META').clone
|
|
411
|
+
interface.write(meta_packet)
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
@write_connection_callback.call(interface) if @write_connection_callback
|
|
415
|
+
@connection_mutex.synchronize do
|
|
416
|
+
@write_interface_infos << InterfaceInfo.new(interface, hostname, host_ip, port)
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
if listen_read
|
|
420
|
+
@read_connection_callback.call(interface) if @read_connection_callback
|
|
421
|
+
@connection_mutex.synchronize do
|
|
422
|
+
@read_interface_infos << InterfaceInfo.new(interface, hostname, host_ip, port)
|
|
423
|
+
end
|
|
424
|
+
start_read_thread(@read_interface_infos[-1])
|
|
425
|
+
end
|
|
426
|
+
Logger.info "#{@name}: Tcpip server accepted connection from #{hostname}(#{host_ip}):#{port}"
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
def start_read_thread(interface_info)
|
|
430
|
+
@read_threads << Thread.new do
|
|
431
|
+
index_to_delete = nil
|
|
432
|
+
begin
|
|
433
|
+
begin
|
|
434
|
+
read_thread_body(interface_info.interface)
|
|
435
|
+
rescue Exception => err
|
|
436
|
+
Logger.error "#{@name}: Tcpip server read thread unexpectedly died"
|
|
437
|
+
Logger.error err.formatted
|
|
438
|
+
end
|
|
439
|
+
Logger.info "#{@name}: Tcpip server lost read connection to #{interface_info.hostname}(#{interface_info.host_ip}):#{interface_info.port}"
|
|
440
|
+
@read_threads.delete(Thread.current)
|
|
441
|
+
|
|
442
|
+
index_to_delete = nil
|
|
443
|
+
@connection_mutex.synchronize do
|
|
444
|
+
index = 0
|
|
445
|
+
@read_interface_infos.each do |read_interface_info|
|
|
446
|
+
if interface_info.interface == read_interface_info.interface
|
|
447
|
+
index_to_delete = index
|
|
448
|
+
read_interface_info.interface.disconnect
|
|
449
|
+
read_interface_info.interface.raw_logger_pair.stop if read_interface_info.interface.raw_logger_pair
|
|
450
|
+
break
|
|
451
|
+
end
|
|
452
|
+
index += 1
|
|
453
|
+
end
|
|
454
|
+
ensure
|
|
455
|
+
if index_to_delete
|
|
456
|
+
@read_interface_infos.delete_at(index_to_delete)
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
rescue Exception => err
|
|
460
|
+
Logger.error "#{@name}: Tcpip server read thread unexpectedly died"
|
|
461
|
+
Logger.error err.formatted
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
def write_thread_body
|
|
467
|
+
# Retrieve the next packet to be sent out to clients
|
|
468
|
+
# Handles disconnected clients even when packets aren't flowing
|
|
469
|
+
packet = nil
|
|
470
|
+
|
|
471
|
+
loop do
|
|
472
|
+
break if @cancel_threads
|
|
473
|
+
|
|
474
|
+
begin
|
|
475
|
+
packet = @write_queue.pop(true) # non_block to raise ThreadError
|
|
476
|
+
break
|
|
477
|
+
rescue ThreadError
|
|
478
|
+
check_for_dead_clients()
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
packet = write_thread_hook(packet)
|
|
483
|
+
write_to_clients(:write, packet) if packet
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def write_raw_thread_body
|
|
487
|
+
# Retrieve the next data to be sent out to clients
|
|
488
|
+
data = nil
|
|
489
|
+
|
|
490
|
+
loop do
|
|
491
|
+
break if @cancel_threads
|
|
492
|
+
|
|
493
|
+
begin
|
|
494
|
+
data = @write_raw_queue.pop(true) # non_block to raise ThreadError
|
|
495
|
+
break
|
|
496
|
+
rescue ThreadError
|
|
497
|
+
# Sleep until we receive data or for 100ms
|
|
498
|
+
@write_raw_mutex.synchronize do
|
|
499
|
+
@write_raw_condition_variable.wait(@write_raw_mutex, 0.1)
|
|
500
|
+
end
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
data = write_raw_thread_hook(data)
|
|
505
|
+
write_to_clients(:write_raw, data) if data
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
def interface_disconnect(interface_info)
|
|
509
|
+
Logger.info "#{@name}: Tcpip server lost write connection to "\
|
|
510
|
+
"#{interface_info.hostname}(#{interface_info.host_ip}):#{interface_info.port}"
|
|
511
|
+
interface_info.interface.disconnect
|
|
512
|
+
interface_info.interface.raw_logger_pair.stop if interface_info.interface.raw_logger_pair
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
def write_thread_hook(packet)
|
|
516
|
+
packet # By default just return the packet
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
def write_raw_thread_hook(data)
|
|
520
|
+
data # By default just return the data
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
def read_thread_body(interface)
|
|
524
|
+
thread_bytes_read = 0
|
|
525
|
+
loop do
|
|
526
|
+
packet = interface.read
|
|
527
|
+
interface_bytes_read = interface.bytes_read
|
|
528
|
+
if interface_bytes_read != thread_bytes_read
|
|
529
|
+
diff = interface_bytes_read - thread_bytes_read
|
|
530
|
+
@bytes_read += diff # This would be better if mutex protected, but not that important for telemetry
|
|
531
|
+
thread_bytes_read = interface_bytes_read
|
|
532
|
+
end
|
|
533
|
+
return if !packet || @cancel_threads
|
|
534
|
+
|
|
535
|
+
packet = read_thread_hook(packet) # Do work on received packet
|
|
536
|
+
@read_raw_data_time = interface.read_raw_data_time
|
|
537
|
+
@read_raw_data = interface.read_raw_data
|
|
538
|
+
@read_queue << packet.clone
|
|
539
|
+
end
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
# @return [Packet] Return the packet
|
|
543
|
+
def read_thread_hook(packet)
|
|
544
|
+
packet
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
def check_for_dead_clients
|
|
548
|
+
indexes_to_delete = []
|
|
549
|
+
index = 0
|
|
550
|
+
|
|
551
|
+
@connection_mutex.synchronize do
|
|
552
|
+
@write_interface_infos.each do |interface_info|
|
|
553
|
+
if @write_port != @read_port
|
|
554
|
+
# Socket should return EWOULDBLOCK if it is still cleanly connected
|
|
555
|
+
interface_info.interface.stream.write_socket.recvfrom_nonblock(10)
|
|
556
|
+
elsif !interface_info.interface.stream.write_socket.closed?
|
|
557
|
+
# Let read thread detect disconnect
|
|
558
|
+
next
|
|
559
|
+
end
|
|
560
|
+
# Client has disconnected (or is invalidly sending data on the socket)
|
|
561
|
+
Logger.info "#{@name}: Tcpip server lost write connection to #{interface_info.hostname}(#{interface_info.host_ip}):#{interface_info.port}"
|
|
562
|
+
interface_info.interface.disconnect
|
|
563
|
+
interface_info.interface.raw_logger_pair.stop if interface_info.interface.raw_logger_pair
|
|
564
|
+
indexes_to_delete.unshift(index) # Put later indexes at front of array
|
|
565
|
+
rescue Errno::ECONNRESET, Errno::ECONNABORTED, IOError
|
|
566
|
+
# Client has disconnected
|
|
567
|
+
Logger.info "#{@name}: Tcpip server lost write connection to #{interface_info.hostname}(#{interface_info.host_ip}):#{interface_info.port}"
|
|
568
|
+
interface_info.interface.disconnect
|
|
569
|
+
interface_info.interface.raw_logger_pair.stop if interface_info.interface.raw_logger_pair
|
|
570
|
+
indexes_to_delete.unshift(index) # Put later indexes at front of array
|
|
571
|
+
rescue Errno::EWOULDBLOCK
|
|
572
|
+
# Client is still cleanly connected as far as we can tell without writing to the socket
|
|
573
|
+
ensure
|
|
574
|
+
index += 1
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
# Delete any dead sockets
|
|
578
|
+
indexes_to_delete.each do |index_to_delete|
|
|
579
|
+
@write_interface_infos.delete_at(index_to_delete)
|
|
580
|
+
end
|
|
581
|
+
end # connection_mutex.synchronize
|
|
582
|
+
|
|
583
|
+
# Sleep until we receive a packet or for 100ms
|
|
584
|
+
@write_mutex.synchronize do
|
|
585
|
+
@write_condition_variable.wait(@write_mutex, 0.1)
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
def write_to_clients(method, packet_or_data)
|
|
590
|
+
@connection_mutex.synchronize do
|
|
591
|
+
# Send data to each client - On error drop the client
|
|
592
|
+
indexes_to_delete = []
|
|
593
|
+
index = 0
|
|
594
|
+
@write_interface_infos.each do |interface_info|
|
|
595
|
+
need_disconnect = false
|
|
596
|
+
begin
|
|
597
|
+
interface_bytes_written = interface_info.interface.bytes_written
|
|
598
|
+
interface_info.interface.public_send(method, packet_or_data)
|
|
599
|
+
diff = interface_info.interface.bytes_written - interface_bytes_written
|
|
600
|
+
@written_raw_data_time = interface_info.interface.written_raw_data_time
|
|
601
|
+
@written_raw_data = interface_info.interface.written_raw_data
|
|
602
|
+
@bytes_written += diff
|
|
603
|
+
rescue Errno::EPIPE, Errno::ECONNABORTED, IOError, Errno::ECONNRESET
|
|
604
|
+
# Client has normally disconnected
|
|
605
|
+
need_disconnect = true
|
|
606
|
+
rescue Exception => err
|
|
607
|
+
if err.message != "Stream not connected for write_raw"
|
|
608
|
+
Logger.error "#{@name}: Error sending to client: #{err.class} #{err.message}"
|
|
609
|
+
end
|
|
610
|
+
need_disconnect = true
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
if need_disconnect
|
|
614
|
+
Logger.info "#{@name}: Tcpip server lost write connection to #{interface_info.hostname}(#{interface_info.host_ip}):#{interface_info.port}"
|
|
615
|
+
interface_info.interface.disconnect
|
|
616
|
+
interface_info.interface.raw_logger_pair.stop if interface_info.interface.raw_logger_pair
|
|
617
|
+
indexes_to_delete.unshift(index) # Put later indexes at front of array
|
|
618
|
+
end
|
|
619
|
+
index += 1
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
# Delete any dead sockets
|
|
623
|
+
indexes_to_delete.each do |index_to_delete|
|
|
624
|
+
@write_interface_infos.delete_at(index_to_delete)
|
|
625
|
+
end
|
|
626
|
+
end # connection_mutex.synchronize
|
|
627
|
+
end
|
|
628
|
+
end
|
|
629
|
+
end
|