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,475 @@
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 'openc3/interfaces/tcpip_client_interface'
21
+ require 'uuidtools'
22
+
23
+ module OpenC3
24
+ # Interface for connecting to Ball Aerospace LINC Labview targets
25
+ class LincInterface < TcpipClientInterface
26
+ # The maximum number of asynchronous commands we can wait for at a time.
27
+ # We don't ever expect to get close to this but we need to limit it
28
+ # to ensure the Array doesn't grow out of control.
29
+ MAX_CONCURRENT_HANDSHAKES = 1000
30
+
31
+ def initialize(
32
+ hostname,
33
+ port,
34
+ handshake_enabled = true,
35
+ response_timeout = 5.0,
36
+ read_timeout = nil,
37
+ write_timeout = 5.0,
38
+ length_bitoffset = 0,
39
+ length_bitsize = 16,
40
+ length_value_offset = 4,
41
+ fieldname_guid = 'HDR_GUID',
42
+ endianness = 'BIG_ENDIAN',
43
+ fieldname_cmd_length = 'HDR_LENGTH'
44
+ )
45
+ # Initialize Super Class
46
+ super(hostname, port, port, write_timeout, read_timeout, 'LENGTH',
47
+ length_bitoffset, length_bitsize, length_value_offset, 1, endianness, 0, nil, nil)
48
+
49
+ # Configuration Settings
50
+ @handshake_enabled = ConfigParser.handle_true_false(handshake_enabled)
51
+ @handshake_enableds = nil
52
+ @response_timeout = response_timeout.to_f
53
+ @length_value_offset = Integer(length_value_offset)
54
+ @fieldname_guid = ConfigParser.handle_nil(fieldname_guid)
55
+ @fieldname_cmd_length = ConfigParser.handle_nil(fieldname_cmd_length)
56
+
57
+ # Other instance variables
58
+ @ignored_error_codes = {}
59
+ @handshake_cmds = []
60
+ @handshakes_mutex = Mutex.new
61
+
62
+ # Call this once now because the first time is slow
63
+ UUIDTools::UUID.random_create.raw
64
+ end # def initialize
65
+
66
+ def connect
67
+ # Packet definitions need to be retrieved here because @target_names is not filled in until after initialize
68
+ unless @handshake_enableds
69
+ @handshake_enableds = {}
70
+ @target_names.each do |target_name|
71
+ @handshake_enableds[target_name] = @handshake_enabled
72
+ @ignored_error_codes[target_name] = []
73
+ end
74
+ end
75
+ @handshake_packets = []
76
+ @error_packets = []
77
+ @error_ignore_commands = nil
78
+ @error_handle_commands = nil
79
+ @handshake_enable_commands = nil
80
+ @handshake_disable_commands = nil
81
+
82
+ @target_names.each do |target_name|
83
+ @handshake_packets << System.telemetry.packet(target_name, 'HANDSHAKE')
84
+ @error_packets << System.telemetry.packet(target_name, 'ERROR')
85
+
86
+ # Handle not defining the interface configuration commands (Targets may not want to support this functionality)
87
+ begin
88
+ command = System.commands.packet(target_name, 'OPENC3_ERROR_IGNORE')
89
+ @error_ignore_commands ||= []
90
+ @error_ignore_commands << command
91
+ rescue
92
+ end
93
+ begin
94
+ command = System.commands.packet(target_name, 'OPENC3_ERROR_HANDLE')
95
+ @error_handle_commands ||= []
96
+ @error_handle_commands << command
97
+ rescue
98
+ end
99
+ begin
100
+ command = System.commands.packet(target_name, 'OPENC3_HANDSHAKE_EN')
101
+ @handshake_enable_commands ||= []
102
+ @handshake_enable_commands << command
103
+ rescue
104
+ end
105
+ begin
106
+ command = System.commands.packet(target_name, 'OPENC3_HANDSHAKE_DS')
107
+ @handshake_disable_commands ||= []
108
+ @handshake_disable_commands << command
109
+ rescue
110
+ end
111
+ end
112
+
113
+ @handshakes_mutex.synchronize do
114
+ @handshake_cmds = []
115
+ end
116
+
117
+ # Actually connect
118
+ super()
119
+ end
120
+
121
+ def write(packet)
122
+ return if linc_interface_command(packet)
123
+ raise "Interface not connected" unless connected?()
124
+
125
+ # Add a GUID to the GUID field if its defined
126
+ # A GUID means it's an asychronous packet type.
127
+ if @fieldname_guid
128
+ guid = get_guid(packet)
129
+ else
130
+ # If @fieldname_guid is not defined (syncronous) we don't care what the
131
+ # GUID is because we're not trying to match it up with anything.
132
+ # As soon as we get a response we free the command.
133
+ guid = 0
134
+ end
135
+
136
+ # Fix the length field to handle the cases where a variable length packet
137
+ # is defined. OpenC3 does not do this automatically.
138
+ update_length_field(packet) if @fieldname_cmd_length
139
+
140
+ # Always take the mutex (even if we aren't handshaking)
141
+ # We do not want any incoming telemetry to be missed because
142
+ # it could be the handshake to this command.
143
+ @handshakes_mutex.synchronize do
144
+ super(packet) # Send the command
145
+ wait_for_response(packet, guid) if @handshake_enableds[packet.target_name]
146
+ end
147
+ end
148
+
149
+ def linc_interface_command(packet)
150
+ if @error_ignore_commands
151
+ @error_ignore_commands.each do |error_ignore_command|
152
+ if error_ignore_command.identify?(packet.buffer(false))
153
+ linc_cmd = error_ignore_command.clone
154
+ linc_cmd.buffer = packet.buffer
155
+ code = linc_cmd.read('CODE')
156
+ @ignored_error_codes[error_ignore_command.target_name] << code unless @ignored_error_codes[error_ignore_command.target_name].include? code
157
+ return true
158
+ end
159
+ end
160
+ end
161
+
162
+ if @error_handle_commands
163
+ @error_handle_commands.each do |error_handle_command|
164
+ if error_handle_command.identify?(packet.buffer(false))
165
+ linc_cmd = error_handle_command.clone
166
+ linc_cmd.buffer = packet.buffer
167
+ code = linc_cmd.read('CODE')
168
+ @ignored_error_codes[error_handle_command.target_name].delete(code) if @ignored_error_codes[error_handle_command.target_name].include? code
169
+ return true
170
+ end
171
+ end
172
+ end
173
+
174
+ if @handshake_enable_commands
175
+ @handshake_enable_commands.each do |handshake_enable_command|
176
+ if handshake_enable_command.identify?(packet.buffer(false))
177
+ @handshake_enabled = true
178
+ @handshake_enableds[handshake_enable_command.target_name] = true
179
+ return true
180
+ end
181
+ end
182
+ end
183
+
184
+ if @handshake_disable_commands
185
+ @handshake_disable_commands.each do |handshake_disable_command|
186
+ if handshake_disable_command.identify?(packet.buffer(false))
187
+ @handshake_enabled = false
188
+ @handshake_enableds[handshake_disable_command.target_name] = false
189
+ return true
190
+ end
191
+ end
192
+ end
193
+
194
+ return false
195
+ end
196
+
197
+ def get_guid(packet)
198
+ if not packet.read(@fieldname_guid) =~ /[\x01-\xFF]/
199
+ # The GUID has not been set already (it has all \x00 values), so make a new one.
200
+ # This enables a router GUI to make the GUIDs so it can process handshakes too.
201
+ guid = UUIDTools::UUID.random_create.raw
202
+ packet.write(@fieldname_guid, guid, :RAW)
203
+ else
204
+ guid = packet.read(@fieldname_guid)
205
+ end
206
+ return guid
207
+ end
208
+
209
+ def update_length_field(packet)
210
+ length = packet.length - @length_value_offset
211
+ packet.write(@fieldname_cmd_length, length, :RAW)
212
+ end
213
+
214
+ def wait_for_response(packet, guid)
215
+ # Check the number of commands waiting for handshakes. This is just for sanity
216
+ # If the number of commands waiting for handshakes is very large then it can't be real
217
+ # So raise an error. Something has gone horribly wrong.
218
+ if @handshake_cmds.length > MAX_CONCURRENT_HANDSHAKES
219
+ len = @handshake_cmds.length
220
+ @handshake_cmds = []
221
+ raise "The number of commands waiting for handshakes to #{len}. Clearing all commands!"
222
+ end
223
+
224
+ # Create a handshake command object and add it to the list of commands waiting
225
+ handshake_cmd = LincHandshakeCommand.new(@handshakes_mutex, guid)
226
+ @handshake_cmds.push(handshake_cmd)
227
+
228
+ # wait for that handshake. This releases the mutex so that the telemetry and other commands can start running again.
229
+ timed_out = handshake_cmd.wait_for_handshake(@response_timeout)
230
+ # We now have the mutex again. This interface is blocked for the rest of the command handling,
231
+ # which should be quick because it's just checking variables and logging.
232
+ # We want to hold the mutex during that so that the commands get logged in order of handshake from here.
233
+
234
+ if timed_out
235
+ # Clean this command out of the array of items that require handshakes.
236
+ @handshake_cmds.delete_if { |hsc| hsc == handshake_cmd }
237
+ raise "Timeout waiting for handshake from #{System.commands.format(packet, System.targets[packet.target_name].ignored_parameters)}"
238
+ end
239
+
240
+ process_handshake_results(handshake_cmd)
241
+ rescue Exception => err
242
+ # If anything goes wrong after successfully writing the packet to the LINC target
243
+ # ensure that the packet gets updated in the CVT and logged to the packet log writer.
244
+ # OpenC3 normally only does this if write returns successfully
245
+ if packet.identified?
246
+ command = System.commands.packet(packet.target_name, packet.packet_name)
247
+ else
248
+ command = System.commands.packet('UNKNOWN', 'UNKNOWN')
249
+ end
250
+ command.buffer = packet.buffer
251
+
252
+ @packet_log_writer_pairs.each do |packet_log_writer_pair|
253
+ packet_log_writer_pair.cmd_log_writer.write(packet)
254
+ end
255
+
256
+ raise err
257
+ end
258
+
259
+ def process_handshake_results(handshake_cmd)
260
+ status = handshake_cmd.handshake.handshake.read('STATUS')
261
+ code = handshake_cmd.handshake.handshake.read('CODE')
262
+ source = handshake_cmd.handshake.error_source
263
+
264
+ # Handle handshake warnings and errors
265
+ if status == "OK" and code != 0
266
+ unless @ignored_error_codes[handshake_cmd.handshake.handshake.target_name].include? code
267
+ Logger.warn "#{@name}: Warning sending command (#{code}): #{source}"
268
+ end
269
+ elsif status == "ERROR"
270
+ unless @ignored_error_codes[handshake_cmd.handshake.handshake.target_name].include? code
271
+ raise "Error sending command (#{code}): #{source}"
272
+ end
273
+ end
274
+ end
275
+
276
+ def read
277
+ packet = super()
278
+ if packet
279
+ @handshake_packets.each do |handshake_packet|
280
+ if handshake_packet.identify?(packet.buffer(false))
281
+ handshake_packet = handshake_packet.clone
282
+ handshake_packet.buffer = packet.buffer
283
+ linc_handshake = LincHandshake.new(handshake_packet, handshake_packet.target_name)
284
+
285
+ # Check for a local handshake
286
+ if handshake_packet.read('origin') == "LCL"
287
+ handle_local_handshake(linc_handshake)
288
+ else
289
+ handle_remote_handshake(linc_handshake) if @handshake_enableds[handshake_packet.target_name]
290
+ end # if handshake_packet.read('origin') == "LCL"
291
+ end # @handshake_packet.identify?(packet.buffer(false))
292
+ end
293
+ end # if packet
294
+
295
+ return packet
296
+ end
297
+
298
+ def handle_local_handshake(linc_handshake)
299
+ # Update the current value table for this command
300
+ command = System.commands.packet(linc_handshake.identified_command.target_name, linc_handshake.identified_command.packet_name)
301
+ command.received_time = linc_handshake.identified_command.received_time
302
+ command.buffer = linc_handshake.identified_command.buffer
303
+ command.received_count += 1
304
+
305
+ # Put a log of the command onto the server for the user to see
306
+ Logger.info("#{@name}: External Command: " + System.commands.format(linc_handshake.identified_command, System.targets[linc_handshake.identified_command.target_name].ignored_parameters))
307
+
308
+ # Log the command to the command log(s)
309
+ @packet_log_writer_pairs.each do |packet_log_writer_pair|
310
+ packet_log_writer_pair.cmd_log_writer.write(linc_handshake.identified_command)
311
+ end
312
+ end
313
+
314
+ def handle_remote_handshake(linc_handshake)
315
+ # This is a remote packet (sent from here).
316
+ # Add to the array of handshake packet responses
317
+ # The mutex is required by the command task due to the way it
318
+ # first looks up the handshake before removing it.
319
+ @handshakes_mutex.synchronize do
320
+ if @fieldname_guid
321
+ # A GUID means it's an asychronous packet type.
322
+ # So look at the list of incoming handshakes and pick off (deleting)
323
+ # the handshake from the list if it's for this command.
324
+ #
325
+ # The mutex is required because the telemetry task
326
+ # could enqueue a response between the index lookup and the slice
327
+ # function which would remove the wrong response. FAIL!
328
+
329
+ # Loop through all waiting commands to see if this handshake belongs to them
330
+ this_handshake_guid = linc_handshake.get_cmd_guid(@fieldname_guid)
331
+ handshake_cmd_index = @handshake_cmds.index { |hsc| hsc.get_cmd_guid == this_handshake_guid }
332
+
333
+ # If command was waiting (ie the loop above found one), then remove it from waiters and signal it
334
+ if handshake_cmd_index
335
+ handshake_cmd = @handshake_cmds.slice!(handshake_cmd_index)
336
+ handshake_cmd.got_your_handshake(linc_handshake)
337
+ else
338
+ # No command match found! Either it gave up and timed out or this wasn't originated from here.
339
+ # Ignore this typically. This case here for clarity.
340
+ end
341
+
342
+ else
343
+ # Synchronous version: just pop the array (pull the command off) and send it the handshake
344
+ handshake_cmd = @handshakes_cmds.pop
345
+ handshake_cmd.got_your_handshake(linc_handshake)
346
+ end # of handshaking type check
347
+ end
348
+ end
349
+ end # class LincInterface
350
+
351
+ # The LincHandshakeCommand class is used only by the LincInterface.
352
+ # It is the command with other required items that is passed to the telemetry
353
+ # thread so it can match it with the handshake.
354
+ class LincHandshakeCommand
355
+ attr_accessor :handshake
356
+
357
+ def initialize(handshakes_mutex, cmd_guid)
358
+ @cmd_guid = cmd_guid
359
+ @handshakes_mutex = handshakes_mutex
360
+ @resource = ConditionVariable.new
361
+ @handshake = nil
362
+ end
363
+
364
+ def wait_for_handshake(response_timeout)
365
+ timed_out = false
366
+
367
+ @resource.wait(@handshakes_mutex, response_timeout)
368
+ if @handshake
369
+ timed_out = false
370
+ else
371
+ Logger.warn "#{@name}: No handshake - must be timeout."
372
+ timed_out = true
373
+ end
374
+
375
+ return timed_out
376
+ end
377
+
378
+ def got_your_handshake(handshake)
379
+ @handshake = handshake
380
+ @resource.signal
381
+ end
382
+
383
+ def get_cmd_guid
384
+ return @cmd_guid
385
+ end
386
+ end # class LincHandshakeCommand
387
+
388
+ # The LincHandshake class is used only by the LincInterface. It processes the handshake and
389
+ # helps with finding the information regarding the internal command.
390
+ class LincHandshake
391
+ attr_accessor :handshake
392
+ attr_accessor :identified_command
393
+ attr_accessor :error_source
394
+
395
+ def initialize(handshake, interface_target_name)
396
+ @handshake = handshake
397
+
398
+ # Interpret the command field of the handshake packet
399
+ # Where DATA is defined as:
400
+ # 1 byte target name length
401
+ # X byte target name
402
+ # 1 byte packet name length
403
+ # X byte packet name
404
+ # 4 byte packet data length
405
+ # X byte packet data
406
+ # 4 byte error source length
407
+ # X byte error source
408
+ data = handshake.read('DATA')
409
+ raise "Data field too short for target name length" if data.length == 0
410
+
411
+ # Get target name length
412
+ target_name_length = data[0..0].unpack('C')[0]
413
+ raise "Invalid target name length" if target_name_length == 0
414
+
415
+ data = data[1..-1]
416
+
417
+ # get target name
418
+ raise "Data field too short for target name" if data.length < target_name_length
419
+
420
+ # target_name = data[0..(target_name_length - 1)] # Unused
421
+ data = data[target_name_length..-1]
422
+
423
+ # get packet name length
424
+ raise "Data field too short for packet name length" if data.length == 0
425
+
426
+ packet_name_length = data[0..0].unpack('C')[0]
427
+ raise "Invalid packet name length" if packet_name_length == 0
428
+
429
+ data = data[1..-1]
430
+
431
+ # get packet name
432
+ raise "Data field too short for packet name" if data.length < packet_name_length
433
+
434
+ packet_name = data[0..(packet_name_length - 1)]
435
+ data = data[packet_name_length..-1]
436
+
437
+ # get packet data length
438
+ raise "Data field too short for packet data length" if data.length < 4
439
+
440
+ packet_data_length = data[0..3].unpack('N')[0]
441
+ raise "Invalid data length" if packet_data_length == 0
442
+
443
+ data = data[4..-1]
444
+
445
+ # get packet data
446
+ raise "Data field too short for packet data" if data.length < packet_data_length
447
+
448
+ packet_data = data[0..(packet_data_length - 1)]
449
+ data = data[packet_data_length..-1]
450
+
451
+ # get error source length
452
+ raise "Data field too short for error source length" if data.length < 4
453
+
454
+ error_source_length = data[0..3].unpack('N')[0]
455
+ # note it is OK to have a 0 source length
456
+ data = data[4..-1]
457
+
458
+ # get error source - store on object
459
+ if error_source_length > 0
460
+ @error_source = data[0..(error_source_length - 1)]
461
+ else
462
+ @error_source = ''
463
+ end
464
+
465
+ # make packet - store on object as a defined packet of type command that this handshakes
466
+ @identified_command = System.commands.packet(interface_target_name, packet_name).clone
467
+ @identified_command.buffer = packet_data
468
+ @identified_command.received_time = Time.at(handshake.read('TIME_SECONDS'), handshake.read('TIME_MICROSECONDS')).sys
469
+ end
470
+
471
+ def get_cmd_guid(fieldname_guid)
472
+ return @identified_command.read(fieldname_guid)
473
+ end
474
+ end
475
+ end
@@ -0,0 +1,192 @@
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 'openc3/config/config_parser'
21
+ require 'openc3/interfaces/protocols/protocol'
22
+ require 'thread'
23
+
24
+ module OpenC3
25
+ # Reads all data available on the interface and creates a packet
26
+ # with that data.
27
+ class BurstProtocol < Protocol
28
+ # @param discard_leading_bytes [Integer] The number of bytes to discard
29
+ # from the binary data after reading. Note that this is often
30
+ # used to remove a sync pattern from the final packet data.
31
+ # @param sync_pattern [String] String representing a hex number ("0x1234")
32
+ # that will be searched for in the raw data. Bytes encountered before
33
+ # this pattern is found are discarded.
34
+ # @param fill_fields [Boolean] Fill any required fields when writing packets
35
+ # @param allow_empty_data [true/false/nil] See Protocol#initialize
36
+ def initialize(discard_leading_bytes = 0, sync_pattern = nil, fill_fields = false, allow_empty_data = nil)
37
+ super(allow_empty_data)
38
+ @discard_leading_bytes = discard_leading_bytes.to_i
39
+ @sync_pattern = ConfigParser.handle_nil(sync_pattern)
40
+ @sync_pattern = @sync_pattern.hex_to_byte_string if @sync_pattern
41
+ @fill_fields = ConfigParser.handle_true_false(fill_fields)
42
+ end
43
+
44
+ def reset
45
+ super()
46
+ @data = ''
47
+ @data.force_encoding('ASCII-8BIT')
48
+ @sync_state = :SEARCHING
49
+ end
50
+
51
+ # Reads from the interface. It can look for a sync pattern before
52
+ # creating a Packet. It can discard a set number of bytes at the beginning
53
+ # before creating the Packet.
54
+ #
55
+ # @return [String|nil] Data for a packet consisting of the bytes read
56
+ def read_data(data)
57
+ @data << data
58
+
59
+ control = handle_sync_pattern()
60
+ return control if control and data.length > 0
61
+
62
+ # Reduce the data to a single packet
63
+ packet_data = reduce_to_single_packet()
64
+
65
+ # Potentially allow blank string to be sent to other protocols if no packet is ready in this one
66
+ if Symbol === packet_data
67
+ if (data.length <= 0) and packet_data == :STOP
68
+ return super(data)
69
+ else
70
+ return packet_data
71
+ end
72
+ end
73
+
74
+ @sync_state = :SEARCHING
75
+
76
+ # Discard leading bytes if necessary
77
+ packet_data.replace(packet_data[@discard_leading_bytes..-1]) if @discard_leading_bytes > 0
78
+ packet_data
79
+ end
80
+
81
+ # Called to perform modifications on a command packet before it is sent
82
+ #
83
+ # @param packet [Packet] Original packet
84
+ # @return [Packet] Potentially modified packet
85
+ def write_packet(packet)
86
+ # If we're filling the sync pattern and the sync pattern is part of the
87
+ # packet (since we're not discarding any leading bytes) then we have to
88
+ # fill the sync pattern in the actual packet so do it here.
89
+ if @fill_fields && @sync_pattern && @discard_leading_bytes == 0
90
+ # Directly write the packet buffer and fill in the sync pattern
91
+ BinaryAccessor.write(@sync_pattern, 0, @sync_pattern.length * 8, :BLOCK,
92
+ packet.buffer(false), :BIG_ENDIAN, :ERROR)
93
+ end
94
+ packet
95
+ end
96
+
97
+ # Called to perform modifications on write data before sending it to the interface
98
+ #
99
+ # @param data [String] Raw packet data
100
+ # @return [String] Potentially modified packet data
101
+ def write_data(data)
102
+ # If we're filling the sync pattern and discarding the leading bytes
103
+ # during a read then we need to put them back during a write.
104
+ # If we're discarding the bytes then by definition they can't be part
105
+ # of the packet so we just modify the data.
106
+ if @fill_fields && @discard_leading_bytes > 0
107
+ data = ("\x00" * @discard_leading_bytes) << data
108
+ if @sync_pattern
109
+ BinaryAccessor.write(@sync_pattern, 0, @sync_pattern.length * 8, :BLOCK,
110
+ data, :BIG_ENDIAN, :ERROR)
111
+ end
112
+ end
113
+ super(data)
114
+ end
115
+
116
+ # @return [Boolean] control code (nil, :STOP)
117
+ def handle_sync_pattern
118
+ if @sync_pattern and @sync_state == :SEARCHING
119
+ loop do
120
+ # Make sure we have some data to look for a sync word in
121
+ return :STOP if @data.length < @sync_pattern.length
122
+
123
+ # Find the beginning of the sync pattern
124
+ sync_index = @data.index(@sync_pattern.getbyte(0).chr)
125
+ if sync_index
126
+ # Make sure we have enough data for the whole sync pattern past this index
127
+ return :STOP if @data.length < (sync_index + @sync_pattern.length)
128
+
129
+ # Check for the rest of the sync pattern
130
+ found = true
131
+ index = sync_index
132
+ @sync_pattern.each_byte do |byte|
133
+ if @data.getbyte(index) != byte
134
+ found = false
135
+ break
136
+ end
137
+ index += 1
138
+ end
139
+
140
+ if found
141
+ if sync_index != 0
142
+ discard_length = @data[0..(sync_index - 1)].length
143
+ log_discard(discard_length, true)
144
+ # Delete Data Before Sync Pattern
145
+ @data.replace(@data[sync_index..-1])
146
+ end
147
+ @sync_state = :FOUND
148
+ return nil
149
+
150
+ else # not found
151
+ log_discard(@data[0..sync_index].length, false)
152
+ # Delete Data Before and including first character of suspected sync Pattern
153
+ @data.replace(@data[(sync_index + 1)..-1])
154
+ next
155
+ end # if found
156
+
157
+ else # sync_index = nil
158
+ log_discard(@data.length, false)
159
+ @data.replace('')
160
+ return :STOP
161
+ end # unless sync_index.nil?
162
+ end # end loop
163
+ end # if @sync_pattern
164
+ nil
165
+ end
166
+
167
+ def log_discard(length, found)
168
+ Logger.error("#{@interface ? @interface.name : ""}: Sync #{'not ' unless found}found. Discarding #{length} bytes of data.")
169
+ if @data.length >= 0
170
+ Logger.error(sprintf("Starting: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
171
+ @data.length >= 1 ? @data.getbyte(0) : 0,
172
+ @data.length >= 2 ? @data.getbyte(1) : 0,
173
+ @data.length >= 3 ? @data.getbyte(2) : 0,
174
+ @data.length >= 4 ? @data.getbyte(3) : 0,
175
+ @data.length >= 5 ? @data.getbyte(4) : 0,
176
+ @data.length >= 6 ? @data.getbyte(5) : 0))
177
+ end
178
+ end
179
+
180
+ def reduce_to_single_packet
181
+ if @data.length <= 0
182
+ # Need some data
183
+ return :STOP
184
+ end
185
+
186
+ # Reduce to packet data and clear data for next packet
187
+ packet_data = @data.clone
188
+ @data.replace('')
189
+ packet_data
190
+ end
191
+ end
192
+ end