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,532 @@
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/microservices/microservice'
21
+ require 'openc3/models/interface_model'
22
+ require 'openc3/models/router_model'
23
+ require 'openc3/models/interface_status_model'
24
+ require 'openc3/models/router_status_model'
25
+ require 'openc3/topics/telemetry_topic'
26
+ require 'openc3/topics/command_topic'
27
+ require 'openc3/topics/command_decom_topic'
28
+ require 'openc3/topics/interface_topic'
29
+ require 'openc3/topics/router_topic'
30
+
31
+ module OpenC3
32
+ class InterfaceCmdHandlerThread
33
+ def initialize(interface, tlm, scope:)
34
+ @interface = interface
35
+ @tlm = tlm
36
+ @scope = scope
37
+ end
38
+
39
+ def start
40
+ @thread = Thread.new do
41
+ run()
42
+ rescue Exception => err
43
+ Logger.error "#{@interface.name}: Command handler thread died: #{err.formatted}"
44
+ raise err
45
+ end
46
+ end
47
+
48
+ def stop
49
+ OpenC3.kill_thread(self, @thread)
50
+ end
51
+
52
+ def graceful_kill
53
+ InterfaceTopic.shutdown(@interface, scope: @scope)
54
+ end
55
+
56
+ def run
57
+ InterfaceTopic.receive_commands(@interface, scope: @scope) do |topic, msg_hash|
58
+ # Check for a raw write to the interface
59
+ if topic =~ /CMD}INTERFACE/
60
+ if msg_hash['shutdown']
61
+ Logger.info "#{@interface.name}: Shutdown requested"
62
+ return
63
+ end
64
+ if msg_hash['connect']
65
+ Logger.info "#{@interface.name}: Connect requested"
66
+ @tlm.attempting()
67
+ next 'SUCCESS'
68
+ end
69
+ if msg_hash['disconnect']
70
+ Logger.info "#{@interface.name}: Disconnect requested"
71
+ @tlm.disconnect(false)
72
+ next 'SUCCESS'
73
+ end
74
+ if msg_hash['raw']
75
+ Logger.info "#{@interface.name}: Write raw"
76
+ # A raw interface write results in an UNKNOWN packet
77
+ command = System.commands.packet('UNKNOWN', 'UNKNOWN')
78
+ command.received_count += 1
79
+ command = command.clone
80
+ command.buffer = msg_hash['raw']
81
+ command.received_time = Time.now
82
+ CommandTopic.write_packet(command, scope: @scope)
83
+ @interface.write_raw(msg_hash['raw'])
84
+ next 'SUCCESS'
85
+ end
86
+ if msg_hash.key?('log_raw')
87
+ if msg_hash['log_raw'] == 'true'
88
+ Logger.info "#{@interface.name}: Enable raw logging"
89
+ @interface.start_raw_logging
90
+ else
91
+ Logger.info "#{@interface.name}: Disable raw logging"
92
+ @interface.stop_raw_logging
93
+ end
94
+ next 'SUCCESS'
95
+ end
96
+ end
97
+
98
+ target_name = msg_hash['target_name']
99
+ cmd_name = msg_hash['cmd_name']
100
+ cmd_params = nil
101
+ cmd_buffer = nil
102
+ hazardous_check = nil
103
+ if msg_hash['cmd_params']
104
+ cmd_params = JSON.parse(msg_hash['cmd_params'], :allow_nan => true, :create_additions => true)
105
+ range_check = ConfigParser.handle_true_false(msg_hash['range_check'])
106
+ raw = ConfigParser.handle_true_false(msg_hash['raw'])
107
+ hazardous_check = ConfigParser.handle_true_false(msg_hash['hazardous_check'])
108
+ elsif msg_hash['cmd_buffer']
109
+ cmd_buffer = msg_hash['cmd_buffer']
110
+ end
111
+
112
+ begin
113
+ begin
114
+ if cmd_params
115
+ command = System.commands.build_cmd(target_name, cmd_name, cmd_params, range_check, raw)
116
+ elsif cmd_buffer
117
+ if target_name
118
+ command = System.commands.identify(cmd_buffer, [target_name])
119
+ else
120
+ command = System.commands.identify(cmd_buffer, @target_names)
121
+ end
122
+ unless command
123
+ command = System.commands.packet('UNKNOWN', 'UNKNOWN')
124
+ command.received_count += 1
125
+ command = command.clone
126
+ command.buffer = cmd_buffer
127
+ end
128
+ else
129
+ raise "Invalid command received:\n #{msg_hash}"
130
+ end
131
+ command.received_time = Time.now
132
+ rescue => e
133
+ Logger.error "#{@interface.name}: #{msg_hash}"
134
+ Logger.error "#{@interface.name}: #{e.formatted}"
135
+ next e.message
136
+ end
137
+
138
+ if hazardous_check
139
+ hazardous, hazardous_description = System.commands.cmd_pkt_hazardous?(command)
140
+ # Return back the error, description, and the formatted command
141
+ # This allows the error handler to simply re-send the command
142
+ next "HazardousError\n#{hazardous_description}\n#{System.commands.format(command)}" if hazardous
143
+ end
144
+
145
+ begin
146
+ @interface.write(command)
147
+ CommandTopic.write_packet(command, scope: @scope)
148
+ CommandDecomTopic.write_packet(command, scope: @scope)
149
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
150
+ next 'SUCCESS'
151
+ rescue => e
152
+ Logger.error "#{@interface.name}: #{e.formatted}"
153
+ next e.message
154
+ end
155
+ rescue => e
156
+ Logger.error "#{@interface.name}: #{e.formatted}"
157
+ next e.message
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ class RouterTlmHandlerThread
164
+ def initialize(router, tlm, scope:)
165
+ @router = router
166
+ @tlm = tlm
167
+ @scope = scope
168
+ end
169
+
170
+ def start
171
+ @thread = Thread.new do
172
+ run()
173
+ rescue Exception => err
174
+ Logger.error "#{@router.name}: Telemetry handler thread died: #{err.formatted}"
175
+ raise err
176
+ end
177
+ end
178
+
179
+ def stop
180
+ OpenC3.kill_thread(self, @thread)
181
+ end
182
+
183
+ def graceful_kill
184
+ RouterTopic.shutdown(@router, scope: @scope)
185
+ end
186
+
187
+ def run
188
+ RouterTopic.receive_telemetry(@router, scope: @scope) do |topic, msg_hash|
189
+ # Check for commands to the router itself
190
+ if /CMD}ROUTER/.match?(topic)
191
+ if msg_hash['shutdown']
192
+ Logger.info "#{@router.name}: Shutdown requested"
193
+ return
194
+ end
195
+ if msg_hash['connect']
196
+ Logger.info "#{@router.name}: Connect requested"
197
+ @tlm.attempting()
198
+ end
199
+ if msg_hash['disconnect']
200
+ Logger.info "#{@router.name}: Disconnect requested"
201
+ @tlm.disconnect(false)
202
+ end
203
+ if msg_hash.key?('log_raw')
204
+ if msg_hash['log_raw'] == 'true'
205
+ Logger.info "#{@router.name}: Enable raw logging"
206
+ @router.start_raw_logging
207
+ else
208
+ Logger.info "#{@router.name}: Disable raw logging"
209
+ @router.stop_raw_logging
210
+ end
211
+ end
212
+ next 'SUCCESS'
213
+ end
214
+
215
+ if @router.connected?
216
+ target_name = msg_hash["target_name"]
217
+ packet_name = msg_hash["packet_name"]
218
+
219
+ packet = System.telemetry.packet(target_name, packet_name)
220
+ packet.stored = ConfigParser.handle_true_false(msg_hash["stored"])
221
+ packet.received_time = Time.from_nsec_from_epoch(msg_hash["time"].to_i)
222
+ packet.received_count = msg_hash["received_count"].to_i
223
+ packet.buffer = msg_hash["buffer"]
224
+
225
+ begin
226
+ @router.write(packet)
227
+ RouterStatusModel.set(@router.as_json(:allow_nan => true), scope: @scope)
228
+ next 'SUCCESS'
229
+ rescue => e
230
+ Logger.error "#{@router.name}: #{e.formatted}"
231
+ next e.message
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
237
+
238
+ class InterfaceMicroservice < Microservice
239
+ UNKNOWN_BYTES_TO_PRINT = 16
240
+
241
+ def initialize(name)
242
+ super(name)
243
+ @interface_or_router = self.class.name.to_s.split("Microservice")[0].upcase.split("::")[-1]
244
+ @scope = name.split("__")[0]
245
+ interface_name = name.split("__")[2]
246
+ if @interface_or_router == 'INTERFACE'
247
+ @interface = InterfaceModel.get_model(name: interface_name, scope: @scope).build
248
+ else
249
+ @interface = RouterModel.get_model(name: interface_name, scope: @scope).build
250
+ end
251
+ @interface.name = interface_name
252
+ # Map the interface to the interface's targets
253
+ @interface.target_names do |target_name|
254
+ target = System.targets[target_name]
255
+ target.interface = @interface
256
+ end
257
+ if @interface.connect_on_startup
258
+ @interface.state = 'ATTEMPTING'
259
+ else
260
+ @interface.state = 'DISCONNECTED'
261
+ end
262
+ if @interface_or_router == 'INTERFACE'
263
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
264
+ else
265
+ RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
266
+ end
267
+
268
+ @interface_thread_sleeper = Sleeper.new
269
+ @cancel_thread = false
270
+ @connection_failed_messages = []
271
+ @connection_lost_messages = []
272
+ @mutex = Mutex.new
273
+ if @interface_or_router == 'INTERFACE'
274
+ @handler_thread = InterfaceCmdHandlerThread.new(@interface, self, scope: @scope)
275
+ else
276
+ @handler_thread = RouterTlmHandlerThread.new(@interface, self, scope: @scope)
277
+ end
278
+ @handler_thread.start
279
+ end
280
+
281
+ # External method to be called by the InterfaceCmdHandlerThread to connect
282
+ # Thus we just set the state and allow the run method to handle the action
283
+ def attempting
284
+ @interface.state = 'ATTEMPTING'
285
+ if @interface_or_router == 'INTERFACE'
286
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
287
+ else
288
+ RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
289
+ end
290
+ end
291
+
292
+ def run
293
+ begin
294
+ if @interface.read_allowed?
295
+ Logger.info "#{@interface.name}: Starting packet reading"
296
+ else
297
+ Logger.info "#{@interface.name}: Starting connection maintenance"
298
+ end
299
+ while true
300
+ break if @cancel_thread
301
+
302
+ case @interface.state
303
+ when 'DISCONNECTED'
304
+ begin
305
+ # Just wait to see if we should connect later
306
+ @interface_thread_sleeper.sleep(1)
307
+ rescue Exception => err
308
+ break if @cancel_thread
309
+ end
310
+ when 'ATTEMPTING'
311
+ begin
312
+ @mutex.synchronize do
313
+ # We need to make sure connect is not called after stop() has been called
314
+ connect() unless @cancel_thread
315
+ end
316
+ rescue Exception => connect_error
317
+ handle_connection_failed(connect_error)
318
+ break if @cancel_thread
319
+ end
320
+ when 'CONNECTED'
321
+ if @interface.read_allowed?
322
+ begin
323
+ packet = @interface.read
324
+ if packet
325
+ handle_packet(packet)
326
+ @count += 1
327
+ else
328
+ Logger.info "#{@interface.name}: Internal disconnect requested (returned nil)"
329
+ handle_connection_lost()
330
+ break if @cancel_thread
331
+ end
332
+ rescue Exception => err
333
+ handle_connection_lost(err)
334
+ break if @cancel_thread
335
+ end
336
+ else
337
+ @interface_thread_sleeper.sleep(1)
338
+ handle_connection_lost() if !@interface.connected?
339
+ end
340
+ end
341
+ end
342
+ rescue Exception => error
343
+ Logger.error "#{@interface.name}: Packet reading thread died: #{error.formatted}"
344
+ OpenC3.handle_fatal_exception(error)
345
+ # Try to do clean disconnect because we're going down
346
+ disconnect(false)
347
+ end
348
+ if @interface_or_router == 'INTERFACE'
349
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
350
+ else
351
+ RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
352
+ end
353
+ Logger.info "#{@interface.name}: Stopped packet reading"
354
+ end
355
+
356
+ def handle_packet(packet)
357
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
358
+ packet.received_time = Time.now.sys unless packet.received_time
359
+
360
+ if packet.stored
361
+ # Stored telemetry does not update the current value table
362
+ identified_packet = System.telemetry.identify_and_define_packet(packet, @target_names)
363
+ else
364
+ # Identify and update packet
365
+ if packet.identified?
366
+ begin
367
+ # Preidentifed packet - place it into the current value table
368
+ identified_packet = System.telemetry.update!(packet.target_name,
369
+ packet.packet_name,
370
+ packet.buffer)
371
+ rescue RuntimeError
372
+ # Packet identified but we don't know about it
373
+ # Clear packet_name and target_name and try to identify
374
+ Logger.warn "#{@interface.name}: Received unknown identified telemetry: #{packet.target_name} #{packet.packet_name}"
375
+ packet.target_name = nil
376
+ packet.packet_name = nil
377
+ identified_packet = System.telemetry.identify!(packet.buffer,
378
+ @target_names)
379
+ end
380
+ else
381
+ # Packet needs to be identified
382
+ identified_packet = System.telemetry.identify!(packet.buffer,
383
+ @target_names)
384
+ end
385
+ end
386
+
387
+ if identified_packet
388
+ identified_packet.received_time = packet.received_time
389
+ identified_packet.stored = packet.stored
390
+ identified_packet.extra = packet.extra
391
+ packet = identified_packet
392
+ else
393
+ unknown_packet = System.telemetry.update!('UNKNOWN', 'UNKNOWN', packet.buffer)
394
+ unknown_packet.received_time = packet.received_time
395
+ unknown_packet.stored = packet.stored
396
+ unknown_packet.extra = packet.extra
397
+ packet = unknown_packet
398
+ json_hash = CvtModel.build_json_from_packet(packet)
399
+ CvtModel.set(json_hash, target_name: packet.target_name, packet_name: packet.packet_name, scope: scope)
400
+ num_bytes_to_print = [UNKNOWN_BYTES_TO_PRINT, packet.length].min
401
+ data = packet.buffer(false)[0..(num_bytes_to_print - 1)]
402
+ prefix = data.each_byte.map { | byte | sprintf("%02X", byte) }.join()
403
+ Logger.warn "#{@interface.name} #{packet.target_name} packet length: #{packet.length} starting with: #{prefix}"
404
+ end
405
+
406
+ # Write to stream
407
+ packet.received_count += 1
408
+ TelemetryTopic.write_packet(packet, scope: @scope)
409
+ end
410
+
411
+ def handle_connection_failed(connect_error)
412
+ @error = connect_error
413
+ Logger.error "#{@interface.name}: Connection Failed: #{connect_error.formatted(false, false)}"
414
+ case connect_error
415
+ when Interrupt
416
+ Logger.info "#{@interface.name}: Closing from signal"
417
+ @cancel_thread = true
418
+ when Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::ENOTSOCK, Errno::EHOSTUNREACH, IOError
419
+ # Do not write an exception file for these extremely common cases
420
+ else
421
+ if RuntimeError === connect_error and (connect_error.message =~ /canceled/ or connect_error.message =~ /timeout/)
422
+ # Do not write an exception file for these extremely common cases
423
+ else
424
+ Logger.error "#{@interface.name}: #{connect_error.formatted}"
425
+ unless @connection_failed_messages.include?(connect_error.message)
426
+ OpenC3.write_exception_file(connect_error)
427
+ @connection_failed_messages << connect_error.message
428
+ end
429
+ end
430
+ end
431
+ disconnect() # Ensure we do a clean disconnect
432
+ end
433
+
434
+ def handle_connection_lost(err = nil, reconnect: true)
435
+ if err
436
+ @error = err
437
+ Logger.info "#{@interface.name}: Connection Lost: #{err.formatted(false, false)}"
438
+ case err
439
+ when Interrupt
440
+ Logger.info "#{@interface.name}: Closing from signal"
441
+ @cancel_thread = true
442
+ when Errno::ECONNABORTED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EBADF, Errno::ENOTSOCK, IOError
443
+ # Do not write an exception file for these extremely common cases
444
+ else
445
+ Logger.error "#{@interface.name}: #{err.formatted}"
446
+ unless @connection_lost_messages.include?(err.message)
447
+ OpenC3.write_exception_file(err)
448
+ @connection_lost_messages << err.message
449
+ end
450
+ end
451
+ else
452
+ Logger.info "#{@interface.name}: Connection Lost"
453
+ end
454
+ disconnect(reconnect) # Ensure we do a clean disconnect
455
+ end
456
+
457
+ def connect
458
+ Logger.info "#{@interface.name}: Connecting ..."
459
+ @interface.connect
460
+ @interface.state = 'CONNECTED'
461
+ if @interface_or_router == 'INTERFACE'
462
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
463
+ else
464
+ RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
465
+ end
466
+ Logger.info "#{@interface.name}: Connection Success"
467
+ end
468
+
469
+ def disconnect(allow_reconnect = true)
470
+ return if @interface.state == 'DISCONNECTED' && !@interface.connected?
471
+
472
+ # Synchronize the calls to @interface.disconnect since it takes an unknown
473
+ # amount of time. If two calls to disconnect stack up, the if statement
474
+ # should avoid multiple calls to disconnect.
475
+ @mutex.synchronize do
476
+ begin
477
+ @interface.disconnect if @interface.connected?
478
+ rescue => e
479
+ Logger.error "Disconnect: #{@interface.name}: #{e.formatted}"
480
+ end
481
+ end
482
+
483
+ # If the interface is set to auto_reconnect then delay so the thread
484
+ # can come back around and allow the interface a chance to reconnect.
485
+ if allow_reconnect and @interface.auto_reconnect and @interface.state != 'DISCONNECTED'
486
+ attempting()
487
+ if !@cancel_thread
488
+ # Logger.debug "reconnect delay: #{@interface.reconnect_delay}"
489
+ @interface_thread_sleeper.sleep(@interface.reconnect_delay)
490
+ end
491
+ else
492
+ @interface.state = 'DISCONNECTED'
493
+ if @interface_or_router == 'INTERFACE'
494
+ InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
495
+ else
496
+ RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
497
+ end
498
+ end
499
+ end
500
+
501
+ # Disconnect from the interface and stop the thread
502
+ def stop
503
+ Logger.info "#{@interface.name}: stop requested"
504
+ @mutex.synchronize do
505
+ # Need to make sure that @cancel_thread is set and the interface disconnected within
506
+ # mutex to ensure that connect() is not called when we want to stop()
507
+ @cancel_thread = true
508
+ @handler_thread.stop
509
+ @interface_thread_sleeper.cancel
510
+ @interface.disconnect
511
+ if @interface_or_router == 'INTERFACE'
512
+ valid_interface = InterfaceStatusModel.get_model(name: @interface.name, scope: @scope)
513
+ else
514
+ valid_interface = RouterStatusModel.get_model(name: @interface.name, scope: @scope)
515
+ end
516
+ valid_interface.destroy if valid_interface
517
+ end
518
+ end
519
+
520
+ def shutdown(sig = nil)
521
+ Logger.info "#{@interface.name}: shutdown requested"
522
+ stop()
523
+ super()
524
+ end
525
+
526
+ def graceful_kill
527
+ # Just to avoid warning
528
+ end
529
+ end
530
+ end
531
+
532
+ OpenC3::InterfaceMicroservice.run if __FILE__ == $0
@@ -0,0 +1,108 @@
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/microservices/microservice'
21
+ require 'openc3/topics/topic'
22
+
23
+ module OpenC3
24
+ class LogMicroservice < Microservice
25
+ def initialize(name)
26
+ super(name)
27
+ @config['options'].each do |option|
28
+ case option[0].upcase
29
+ when 'RAW_OR_DECOM'
30
+ @raw_or_decom = option[1].intern
31
+ when 'CMD_OR_TLM'
32
+ @cmd_or_tlm = option[1].intern
33
+ when 'CYCLE_TIME' # Maximum time between log files
34
+ @cycle_time = option[1].to_i
35
+ when 'CYCLE_SIZE' # Maximum size of a log file
36
+ @cycle_size = option[1].to_i
37
+ else
38
+ Logger.error("Unknown option passed to microservice #{@name}: #{option}")
39
+ end
40
+ end
41
+
42
+ raise "Microservice #{@name} not fully configured" unless @raw_or_decom and @cmd_or_tlm
43
+
44
+ # These settings limit the log file to 10 minutes or 50MB of data, whichever comes first
45
+ @cycle_time = 600 unless @cycle_time # 10 minutes
46
+ @cycle_size = 50_000_000 unless @cycle_size # ~50 MB
47
+ end
48
+
49
+ def run
50
+ plws = setup_plws
51
+ while true
52
+ break if @cancel_thread
53
+
54
+ Topic.read_topics(@topics) do |topic, msg_id, msg_hash, redis|
55
+ break if @cancel_thread
56
+
57
+ log_data(plws, topic, msg_id, msg_hash, redis)
58
+ end
59
+ end
60
+ end
61
+
62
+ def setup_plws
63
+ plws = {}
64
+ @topics.each do |topic|
65
+ topic_split = topic.gsub(/{|}/, '').split("__") # Remove the redis hashtag curly braces
66
+ scope = topic_split[0]
67
+ target_name = topic_split[2]
68
+ packet_name = topic_split[3]
69
+ type = @raw_or_decom.to_s.downcase
70
+ remote_log_directory = "#{scope}/#{type}_logs/#{@cmd_or_tlm.to_s.downcase}/#{target_name}/#{packet_name}"
71
+ rt_label = "#{scope}__#{target_name}__#{packet_name}__rt__#{type}"
72
+ stored_label = "#{scope}__#{target_name}__#{packet_name}__stored__#{type}"
73
+ plws[topic] = {
74
+ :RT => PacketLogWriter.new(remote_log_directory, rt_label, true, @cycle_time, @cycle_size, redis_topic: topic),
75
+ :STORED => PacketLogWriter.new(remote_log_directory, stored_label, true, @cycle_time, @cycle_size, redis_topic: topic)
76
+ }
77
+ end
78
+ return plws
79
+ end
80
+
81
+ def log_data(plws, topic, msg_id, msg_hash, redis)
82
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
83
+ topic_split = topic.gsub(/{|}/, '').split("__") # Remove the redis hashtag curly braces
84
+ target_name = topic_split[2]
85
+ packet_name = topic_split[3]
86
+ rt_or_stored = ConfigParser.handle_true_false(msg_hash["stored"]) ? :STORED : :RT
87
+ packet_type = nil
88
+ data_key = nil
89
+ if @raw_or_decom == :RAW
90
+ packet_type = :RAW_PACKET
91
+ data_key = "buffer"
92
+ else # :DECOM
93
+ packet_type = :JSON_PACKET
94
+ data_key = "json_data"
95
+ end
96
+ plws[topic][rt_or_stored].write(packet_type, @cmd_or_tlm, target_name, packet_name, msg_hash["time"].to_i, rt_or_stored == :STORED, msg_hash[data_key], nil, msg_id)
97
+ @count += 1
98
+ diff = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start # seconds as a float
99
+ metric_labels = { "packet" => packet_name, "target" => target_name, "raw_or_decom" => @raw_or_decom.to_s, "cmd_or_tlm" => @cmd_or_tlm.to_s }
100
+ @metric.add_sample(name: "log_duration_seconds", value: diff, labels: metric_labels)
101
+ rescue => err
102
+ @error = err
103
+ Logger.error("#{@name} error: #{err.formatted}")
104
+ end
105
+ end
106
+ end
107
+
108
+ OpenC3::LogMicroservice.run if __FILE__ == $0