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.
Files changed (307) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +18 -0
  3. data/Guardfile +35 -0
  4. data/LICENSE.txt +727 -0
  5. data/README.md +37 -0
  6. data/Rakefile +131 -0
  7. data/bin/cstol_converter +1178 -0
  8. data/bin/openc3cli +531 -0
  9. data/bin/rubysloc +139 -0
  10. data/data/config/_array_params.yaml +23 -0
  11. data/data/config/_id_items.yaml +24 -0
  12. data/data/config/_id_params.yaml +58 -0
  13. data/data/config/_interfaces.yaml +214 -0
  14. data/data/config/_interfaces.yaml.err +1017 -0
  15. data/data/config/_items.yaml +20 -0
  16. data/data/config/_params.yaml +60 -0
  17. data/data/config/cmd_tlm_server.yaml +136 -0
  18. data/data/config/command.yaml +44 -0
  19. data/data/config/command_modifiers.yaml +160 -0
  20. data/data/config/command_telemetry.yaml +3 -0
  21. data/data/config/interface_modifiers.yaml +104 -0
  22. data/data/config/item_modifiers.yaml +221 -0
  23. data/data/config/microservice.yaml +78 -0
  24. data/data/config/param_item_modifiers.yaml +52 -0
  25. data/data/config/parameter_modifiers.yaml +200 -0
  26. data/data/config/plugins.yaml +80 -0
  27. data/data/config/protocols.yaml +290 -0
  28. data/data/config/screen.yaml +147 -0
  29. data/data/config/table_manager.yaml +89 -0
  30. data/data/config/table_parameter_modifiers.yaml +9 -0
  31. data/data/config/target.yaml +142 -0
  32. data/data/config/target_config.yaml +94 -0
  33. data/data/config/telemetry.yaml +87 -0
  34. data/data/config/telemetry_modifiers.yaml +159 -0
  35. data/data/config/tool.yaml +63 -0
  36. data/data/config/unknown.yaml +3 -0
  37. data/data/config/widgets.yaml +1505 -0
  38. data/ext/mkrf_conf.rb +49 -0
  39. data/ext/openc3/ext/array/array.c +122 -0
  40. data/ext/openc3/ext/array/extconf.rb +13 -0
  41. data/ext/openc3/ext/buffered_file/buffered_file.c +198 -0
  42. data/ext/openc3/ext/buffered_file/extconf.rb +13 -0
  43. data/ext/openc3/ext/config_parser/config_parser.c +280 -0
  44. data/ext/openc3/ext/config_parser/extconf.rb +13 -0
  45. data/ext/openc3/ext/crc/crc.c +351 -0
  46. data/ext/openc3/ext/crc/extconf.rb +13 -0
  47. data/ext/openc3/ext/openc3_io/extconf.rb +13 -0
  48. data/ext/openc3/ext/openc3_io/openc3_io.c +158 -0
  49. data/ext/openc3/ext/packet/extconf.rb +13 -0
  50. data/ext/openc3/ext/packet/packet.c +318 -0
  51. data/ext/openc3/ext/platform/extconf.rb +13 -0
  52. data/ext/openc3/ext/platform/platform.c +134 -0
  53. data/ext/openc3/ext/polynomial_conversion/extconf.rb +13 -0
  54. data/ext/openc3/ext/polynomial_conversion/polynomial_conversion.c +79 -0
  55. data/ext/openc3/ext/string/extconf.rb +13 -0
  56. data/ext/openc3/ext/string/string.c +63 -0
  57. data/ext/openc3/ext/structure/structure.c +1719 -0
  58. data/ext/openc3/ext/tabbed_plots_config/extconf.rb +13 -0
  59. data/ext/openc3/ext/tabbed_plots_config/tabbed_plots_config.c +62 -0
  60. data/ext/openc3/ext/telemetry/extconf.rb +13 -0
  61. data/ext/openc3/ext/telemetry/telemetry.c +336 -0
  62. data/lib/cosmos.rb +20 -0
  63. data/lib/cosmosc2.rb +20 -0
  64. data/lib/openc3/api/api.rb +39 -0
  65. data/lib/openc3/api/authorized_api.rb +30 -0
  66. data/lib/openc3/api/cmd_api.rb +451 -0
  67. data/lib/openc3/api/config_api.rb +58 -0
  68. data/lib/openc3/api/interface_api.rb +117 -0
  69. data/lib/openc3/api/limits_api.rb +375 -0
  70. data/lib/openc3/api/router_api.rb +117 -0
  71. data/lib/openc3/api/settings_api.rb +70 -0
  72. data/lib/openc3/api/target_api.rb +78 -0
  73. data/lib/openc3/api/tlm_api.rb +455 -0
  74. data/lib/openc3/bridge/bridge.rb +54 -0
  75. data/lib/openc3/bridge/bridge_config.rb +167 -0
  76. data/lib/openc3/bridge/bridge_interface_thread.rb +42 -0
  77. data/lib/openc3/bridge/bridge_router_thread.rb +42 -0
  78. data/lib/openc3/ccsds/ccsds_packet.rb +68 -0
  79. data/lib/openc3/ccsds/ccsds_parser.rb +148 -0
  80. data/lib/openc3/config/config_parser.rb +549 -0
  81. data/lib/openc3/config/meta_config_parser.rb +74 -0
  82. data/lib/openc3/conversions/conversion.rb +70 -0
  83. data/lib/openc3/conversions/generic_conversion.rb +83 -0
  84. data/lib/openc3/conversions/packet_time_formatted_conversion.rb +43 -0
  85. data/lib/openc3/conversions/packet_time_seconds_conversion.rb +43 -0
  86. data/lib/openc3/conversions/polynomial_conversion.rb +87 -0
  87. data/lib/openc3/conversions/processor_conversion.rb +70 -0
  88. data/lib/openc3/conversions/received_count_conversion.rb +38 -0
  89. data/lib/openc3/conversions/received_time_formatted_conversion.rb +42 -0
  90. data/lib/openc3/conversions/received_time_seconds_conversion.rb +42 -0
  91. data/lib/openc3/conversions/segmented_polynomial_conversion.rb +171 -0
  92. data/lib/openc3/conversions/unix_time_conversion.rb +68 -0
  93. data/lib/openc3/conversions/unix_time_formatted_conversion.rb +49 -0
  94. data/lib/openc3/conversions/unix_time_seconds_conversion.rb +49 -0
  95. data/lib/openc3/conversions.rb +34 -0
  96. data/lib/openc3/core_ext/array.rb +416 -0
  97. data/lib/openc3/core_ext/binding.rb +29 -0
  98. data/lib/openc3/core_ext/class.rb +72 -0
  99. data/lib/openc3/core_ext/exception.rb +61 -0
  100. data/lib/openc3/core_ext/file.rb +83 -0
  101. data/lib/openc3/core_ext/hash.rb +37 -0
  102. data/lib/openc3/core_ext/io.rb +134 -0
  103. data/lib/openc3/core_ext/kernel.rb +42 -0
  104. data/lib/openc3/core_ext/math.rb +128 -0
  105. data/lib/openc3/core_ext/matrix.rb +156 -0
  106. data/lib/openc3/core_ext/objectspace.rb +36 -0
  107. data/lib/openc3/core_ext/openc3_io.rb +57 -0
  108. data/lib/openc3/core_ext/range.rb +27 -0
  109. data/lib/openc3/core_ext/socket.rb +38 -0
  110. data/lib/openc3/core_ext/string.rb +389 -0
  111. data/lib/openc3/core_ext/stringio.rb +33 -0
  112. data/lib/openc3/core_ext/time.rb +508 -0
  113. data/lib/openc3/core_ext.rb +36 -0
  114. data/lib/openc3/interfaces/interface.rb +498 -0
  115. data/lib/openc3/interfaces/linc_interface.rb +475 -0
  116. data/lib/openc3/interfaces/protocols/burst_protocol.rb +192 -0
  117. data/lib/openc3/interfaces/protocols/crc_protocol.rb +193 -0
  118. data/lib/openc3/interfaces/protocols/fixed_protocol.rb +155 -0
  119. data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +56 -0
  120. data/lib/openc3/interfaces/protocols/length_protocol.rb +165 -0
  121. data/lib/openc3/interfaces/protocols/override_protocol.rb +60 -0
  122. data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +206 -0
  123. data/lib/openc3/interfaces/protocols/protocol.rb +82 -0
  124. data/lib/openc3/interfaces/protocols/template_protocol.rb +261 -0
  125. data/lib/openc3/interfaces/protocols/terminated_protocol.rb +93 -0
  126. data/lib/openc3/interfaces/serial_interface.rb +94 -0
  127. data/lib/openc3/interfaces/simulated_target_interface.rb +168 -0
  128. data/lib/openc3/interfaces/stream_interface.rb +81 -0
  129. data/lib/openc3/interfaces/tcpip_client_interface.rb +69 -0
  130. data/lib/openc3/interfaces/tcpip_server_interface.rb +629 -0
  131. data/lib/openc3/interfaces/udp_interface.rb +169 -0
  132. data/lib/openc3/interfaces.rb +44 -0
  133. data/lib/openc3/io/buffered_file.rb +109 -0
  134. data/lib/openc3/io/io_multiplexer.rb +80 -0
  135. data/lib/openc3/io/json_api_object.rb +208 -0
  136. data/lib/openc3/io/json_drb.rb +335 -0
  137. data/lib/openc3/io/json_drb_object.rb +114 -0
  138. data/lib/openc3/io/json_drb_rack.rb +84 -0
  139. data/lib/openc3/io/json_rpc.rb +420 -0
  140. data/lib/openc3/io/openc3_snmp.rb +58 -0
  141. data/lib/openc3/io/posix_serial_driver.rb +156 -0
  142. data/lib/openc3/io/raw_logger.rb +167 -0
  143. data/lib/openc3/io/raw_logger_pair.rb +77 -0
  144. data/lib/openc3/io/serial_driver.rb +105 -0
  145. data/lib/openc3/io/stderr.rb +43 -0
  146. data/lib/openc3/io/stdout.rb +43 -0
  147. data/lib/openc3/io/udp_sockets.rb +194 -0
  148. data/lib/openc3/io/win32_serial_driver.rb +196 -0
  149. data/lib/openc3/logs/log_writer.rb +302 -0
  150. data/lib/openc3/logs/packet_log_constants.rb +62 -0
  151. data/lib/openc3/logs/packet_log_reader.rb +345 -0
  152. data/lib/openc3/logs/packet_log_writer.rb +299 -0
  153. data/lib/openc3/logs/text_log_writer.rb +68 -0
  154. data/lib/openc3/logs.rb +25 -0
  155. data/lib/openc3/microservices/cleanup_microservice.rb +68 -0
  156. data/lib/openc3/microservices/decom_microservice.rb +136 -0
  157. data/lib/openc3/microservices/interface_microservice.rb +532 -0
  158. data/lib/openc3/microservices/log_microservice.rb +108 -0
  159. data/lib/openc3/microservices/microservice.rb +204 -0
  160. data/lib/openc3/microservices/plugin_microservice.rb +43 -0
  161. data/lib/openc3/microservices/reaction_microservice.rb +541 -0
  162. data/lib/openc3/microservices/reducer_microservice.rb +313 -0
  163. data/lib/openc3/microservices/router_microservice.rb +44 -0
  164. data/lib/openc3/microservices/text_log_microservice.rb +84 -0
  165. data/lib/openc3/microservices/timeline_microservice.rb +363 -0
  166. data/lib/openc3/microservices/trigger_group_microservice.rb +638 -0
  167. data/lib/openc3/models/activity_model.rb +319 -0
  168. data/lib/openc3/models/auth_model.rb +65 -0
  169. data/lib/openc3/models/cvt_model.rb +185 -0
  170. data/lib/openc3/models/environment_model.rb +58 -0
  171. data/lib/openc3/models/gem_model.rb +137 -0
  172. data/lib/openc3/models/info_model.rb +31 -0
  173. data/lib/openc3/models/interface_model.rb +281 -0
  174. data/lib/openc3/models/interface_status_model.rb +117 -0
  175. data/lib/openc3/models/metadata_model.rb +139 -0
  176. data/lib/openc3/models/metric_model.rb +59 -0
  177. data/lib/openc3/models/microservice_model.rb +206 -0
  178. data/lib/openc3/models/microservice_status_model.rb +74 -0
  179. data/lib/openc3/models/model.rb +204 -0
  180. data/lib/openc3/models/note_model.rb +122 -0
  181. data/lib/openc3/models/notification_model.rb +40 -0
  182. data/lib/openc3/models/ping_model.rb +35 -0
  183. data/lib/openc3/models/plugin_model.rb +292 -0
  184. data/lib/openc3/models/process_status_model.rb +76 -0
  185. data/lib/openc3/models/reaction_model.rb +322 -0
  186. data/lib/openc3/models/reducer_model.rb +65 -0
  187. data/lib/openc3/models/router_model.rb +35 -0
  188. data/lib/openc3/models/router_status_model.rb +27 -0
  189. data/lib/openc3/models/scope_model.rb +153 -0
  190. data/lib/openc3/models/settings_model.rb +55 -0
  191. data/lib/openc3/models/sorted_model.rb +167 -0
  192. data/lib/openc3/models/target_model.rb +759 -0
  193. data/lib/openc3/models/timeline_model.rb +154 -0
  194. data/lib/openc3/models/tool_config_model.rb +38 -0
  195. data/lib/openc3/models/tool_model.rb +262 -0
  196. data/lib/openc3/models/trigger_group_model.rb +186 -0
  197. data/lib/openc3/models/trigger_model.rb +330 -0
  198. data/lib/openc3/models/widget_model.rb +138 -0
  199. data/lib/openc3/operators/microservice_operator.rb +128 -0
  200. data/lib/openc3/operators/operator.rb +277 -0
  201. data/lib/openc3/packets/binary_accessor.rb +1207 -0
  202. data/lib/openc3/packets/commands.rb +373 -0
  203. data/lib/openc3/packets/json_packet.rb +134 -0
  204. data/lib/openc3/packets/limits.rb +271 -0
  205. data/lib/openc3/packets/limits_response.rb +53 -0
  206. data/lib/openc3/packets/packet.rb +1168 -0
  207. data/lib/openc3/packets/packet_config.rb +625 -0
  208. data/lib/openc3/packets/packet_item.rb +586 -0
  209. data/lib/openc3/packets/packet_item_limits.rb +162 -0
  210. data/lib/openc3/packets/parsers/format_string_parser.rb +65 -0
  211. data/lib/openc3/packets/parsers/limits_parser.rb +159 -0
  212. data/lib/openc3/packets/parsers/limits_response_parser.rb +61 -0
  213. data/lib/openc3/packets/parsers/packet_item_parser.rb +272 -0
  214. data/lib/openc3/packets/parsers/packet_parser.rb +134 -0
  215. data/lib/openc3/packets/parsers/processor_parser.rb +73 -0
  216. data/lib/openc3/packets/parsers/state_parser.rb +127 -0
  217. data/lib/openc3/packets/parsers/xtce_converter.rb +442 -0
  218. data/lib/openc3/packets/parsers/xtce_parser.rb +722 -0
  219. data/lib/openc3/packets/structure.rb +553 -0
  220. data/lib/openc3/packets/structure_item.rb +365 -0
  221. data/lib/openc3/packets/telemetry.rb +487 -0
  222. data/lib/openc3/processors/processor.rb +86 -0
  223. data/lib/openc3/processors/statistics_processor.rb +82 -0
  224. data/lib/openc3/processors/watermark_processor.rb +58 -0
  225. data/lib/openc3/processors.rb +24 -0
  226. data/lib/openc3/script/api_shared.rb +828 -0
  227. data/lib/openc3/script/calendar.rb +89 -0
  228. data/lib/openc3/script/commands.rb +227 -0
  229. data/lib/openc3/script/exceptions.rb +29 -0
  230. data/lib/openc3/script/extract.rb +161 -0
  231. data/lib/openc3/script/limits.rb +60 -0
  232. data/lib/openc3/script/script.rb +299 -0
  233. data/lib/openc3/script/script_runner.rb +238 -0
  234. data/lib/openc3/script/storage.rb +146 -0
  235. data/lib/openc3/script/suite.rb +542 -0
  236. data/lib/openc3/script/suite_results.rb +196 -0
  237. data/lib/openc3/script/suite_runner.rb +217 -0
  238. data/lib/openc3/script.rb +21 -0
  239. data/lib/openc3/streams/serial_stream.rb +167 -0
  240. data/lib/openc3/streams/stream.rb +63 -0
  241. data/lib/openc3/streams/tcpip_client_stream.rb +116 -0
  242. data/lib/openc3/streams/tcpip_socket_stream.rb +195 -0
  243. data/lib/openc3/system/system.rb +127 -0
  244. data/lib/openc3/system/system_config.rb +411 -0
  245. data/lib/openc3/system/target.rb +269 -0
  246. data/lib/openc3/system.rb +24 -0
  247. data/lib/openc3/tools/cmd_tlm_server/api.rb +20 -0
  248. data/lib/openc3/tools/cmd_tlm_server/cmd_tlm_server_config.rb +320 -0
  249. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +294 -0
  250. data/lib/openc3/tools/table_manager/table.rb +77 -0
  251. data/lib/openc3/tools/table_manager/table_config.rb +273 -0
  252. data/lib/openc3/tools/table_manager/table_item.rb +90 -0
  253. data/lib/openc3/tools/table_manager/table_item_parser.rb +66 -0
  254. data/lib/openc3/tools/table_manager/table_manager_core.rb +333 -0
  255. data/lib/openc3/tools/table_manager/table_parser.rb +93 -0
  256. data/lib/openc3/tools/test_runner/test.rb +67 -0
  257. data/lib/openc3/top_level.rb +595 -0
  258. data/lib/openc3/topics/autonomic_topic.rb +52 -0
  259. data/lib/openc3/topics/calendar_topic.rb +44 -0
  260. data/lib/openc3/topics/command_decom_topic.rb +76 -0
  261. data/lib/openc3/topics/command_topic.rb +83 -0
  262. data/lib/openc3/topics/config_topic.rb +68 -0
  263. data/lib/openc3/topics/interface_topic.rb +73 -0
  264. data/lib/openc3/topics/limits_event_topic.rb +109 -0
  265. data/lib/openc3/topics/notifications_topic.rb +28 -0
  266. data/lib/openc3/topics/router_topic.rb +85 -0
  267. data/lib/openc3/topics/telemetry_decom_topic.rb +54 -0
  268. data/lib/openc3/topics/telemetry_topic.rb +36 -0
  269. data/lib/openc3/topics/timeline_topic.rb +45 -0
  270. data/lib/openc3/topics/topic.rb +53 -0
  271. data/lib/openc3/utilities/authentication.rb +141 -0
  272. data/lib/openc3/utilities/authorization.rb +51 -0
  273. data/lib/openc3/utilities/crc.rb +278 -0
  274. data/lib/openc3/utilities/csv.rb +153 -0
  275. data/lib/openc3/utilities/logger.rb +187 -0
  276. data/lib/openc3/utilities/message_log.rb +91 -0
  277. data/lib/openc3/utilities/metric.rb +141 -0
  278. data/lib/openc3/utilities/process_manager.rb +139 -0
  279. data/lib/openc3/utilities/quaternion.rb +257 -0
  280. data/lib/openc3/utilities/ruby_lex_utils.rb +568 -0
  281. data/lib/openc3/utilities/s3.rb +202 -0
  282. data/lib/openc3/utilities/s3_autoload.rb +9 -0
  283. data/lib/openc3/utilities/s3_file_cache.rb +274 -0
  284. data/lib/openc3/utilities/simulated_target.rb +117 -0
  285. data/lib/openc3/utilities/sleeper.rb +51 -0
  286. data/lib/openc3/utilities/store.rb +23 -0
  287. data/lib/openc3/utilities/store_autoload.rb +237 -0
  288. data/lib/openc3/utilities/zip.rb +21 -0
  289. data/lib/openc3/utilities.rb +35 -0
  290. data/lib/openc3/version.rb +14 -0
  291. data/lib/openc3/win32/excel.rb +132 -0
  292. data/lib/openc3/win32/win32.rb +402 -0
  293. data/lib/openc3/win32/win32_main.rb +333 -0
  294. data/lib/openc3.rb +49 -0
  295. data/tasks/gemfile_stats.rake +113 -0
  296. data/tasks/spec.rake +30 -0
  297. data/templates/plugin-template/README.md +15 -0
  298. data/templates/plugin-template/Rakefile +12 -0
  299. data/templates/plugin-template/plugin.gemspec +23 -0
  300. data/templates/plugin-template/plugin.txt +9 -0
  301. data/templates/plugin-template/targets/TARGET/cmd_tlm/cmd.txt +8 -0
  302. data/templates/plugin-template/targets/TARGET/cmd_tlm/tlm.txt +8 -0
  303. data/templates/plugin-template/targets/TARGET/lib/target.rb +10 -0
  304. data/templates/plugin-template/targets/TARGET/procedures/procedure.rb +3 -0
  305. data/templates/plugin-template/targets/TARGET/screens/status.txt +9 -0
  306. data/templates/plugin-template/targets/TARGET/target.txt +5 -0
  307. 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