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,595 @@
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
+ # This file contains top level functions in the OpenC3 namespace
21
+
22
+ require 'thread'
23
+ require 'digest'
24
+ require 'open3'
25
+ require 'openc3/core_ext'
26
+ require 'openc3/version'
27
+ require 'openc3/utilities/logger'
28
+ require 'socket'
29
+ require 'pathname'
30
+
31
+ $openc3_chdir_mutex = Mutex.new
32
+
33
+ # If a hazardous command is sent through the {OpenC3::Api} this error is raised.
34
+ # {OpenC3::Script} rescues the error and prompts the user to continue.
35
+ class HazardousError < StandardError
36
+ attr_accessor :target_name
37
+ attr_accessor :cmd_name
38
+ attr_accessor :cmd_params
39
+ attr_accessor :hazardous_description
40
+ attr_accessor :formatted # formatted command for use in resending original
41
+
42
+ def to_s
43
+ string = "#{target_name} #{cmd_name} with #{cmd_params} is Hazardous"
44
+ string << "due to '#{hazardous_description}'" if hazardous_description
45
+ # Pass along the original formatted command so it can be resent
46
+ string << ".\n#{formatted}"
47
+ end
48
+ end
49
+
50
+ # OpenC3 is almost
51
+ # wholly contained within the OpenC3 module. OpenC3 also extends some of the
52
+ # core Ruby classes to add additional functionality.
53
+
54
+ module OpenC3
55
+ BASE_PWD = Dir.pwd
56
+
57
+ # FatalErrors cause an exit but are not as dangerous as other errors.
58
+ # They are used for known issues and thus we don't need a full error report.
59
+ class FatalError < StandardError; end
60
+
61
+ # Global mutex for the OpenC3 module
62
+ OPENC3_MUTEX = Mutex.new
63
+
64
+ # Path to OpenC3 Gem based on location of top_level.rb
65
+ PATH = File.expand_path(File.join(File.dirname(__FILE__), '../..'))
66
+ PATH.freeze
67
+
68
+ # Header to put on all marshal files created by OpenC3
69
+ OPENC3_MARSHAL_HEADER = "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE} patchlevel #{RUBY_PATCHLEVEL}) [#{RUBY_PLATFORM}] OpenC3 #{OPENC3_VERSION}"
70
+
71
+ # Disables the Ruby interpreter warnings such as when redefining a constant
72
+ def self.disable_warnings
73
+ saved_verbose = $VERBOSE
74
+ $VERBOSE = nil
75
+ yield
76
+ ensure
77
+ $VERBOSE = saved_verbose
78
+ end
79
+
80
+ # Adds a path to the global Ruby search path
81
+ #
82
+ # @param path [String] Directory path
83
+ def self.add_to_search_path(path, front = true)
84
+ path = File.expand_path(path)
85
+ $:.delete(path)
86
+ if front
87
+ $:.unshift(path)
88
+ else # Back
89
+ $: << path
90
+ end
91
+ end
92
+
93
+ # Creates a marshal file by serializing the given obj
94
+ #
95
+ # @param marshal_filename [String] Name of the marshal file to create
96
+ # @param obj [Object] The object to serialize to the file
97
+ def self.marshal_dump(marshal_filename, obj)
98
+ File.open(marshal_filename, 'wb') do |file|
99
+ file.write(OPENC3_MARSHAL_HEADER)
100
+ file.write(Marshal.dump(obj))
101
+ end
102
+ rescue Exception => exception
103
+ begin
104
+ File.delete(marshal_filename)
105
+ rescue Exception
106
+ # Oh well - we tried
107
+ end
108
+ if exception.class == TypeError and exception.message =~ /Thread::Mutex/
109
+ original_backtrace = exception.backtrace
110
+ exception = exception.exception("Mutex exists in a packet. Note: Packets must not be read during class initializers for Conversions, Limits Responses, etc.: #{exception}")
111
+ exception.set_backtrace(original_backtrace)
112
+ end
113
+ self.handle_fatal_exception(exception)
114
+ end
115
+
116
+ # Loads the marshal file back into a Ruby object
117
+ #
118
+ # @param marshal_filename [String] Name of the marshal file to load
119
+ def self.marshal_load(marshal_filename)
120
+ openc3_marshal_header = nil
121
+ data = nil
122
+ File.open(marshal_filename, 'rb') do |file|
123
+ openc3_marshal_header = file.read(OPENC3_MARSHAL_HEADER.length)
124
+ data = file.read
125
+ end
126
+ if openc3_marshal_header == OPENC3_MARSHAL_HEADER
127
+ return Marshal.load(data)
128
+ else
129
+ Logger.warn "Marshal load failed with invalid marshal file: #{marshal_filename}"
130
+ return nil
131
+ end
132
+ rescue Exception => exception
133
+ if File.exist?(marshal_filename)
134
+ Logger.error "Marshal load failed with exception: #{marshal_filename}\n#{exception.formatted}"
135
+ else
136
+ Logger.info "Marshal file does not exist: #{marshal_filename}"
137
+ end
138
+
139
+ # Try to delete the bad marshal file
140
+ begin
141
+ File.delete(marshal_filename)
142
+ rescue Exception
143
+ # Oh well - we tried
144
+ end
145
+ self.handle_fatal_exception(exception) if File.exist?(marshal_filename)
146
+ return nil
147
+ end
148
+
149
+ # Executes the command in a new Ruby Thread.
150
+ #
151
+ # @param command [String] The command to execute via the 'system' call
152
+ def self.run_process(command)
153
+ thread = nil
154
+ thread = Thread.new do
155
+ system(command)
156
+ end
157
+ # Wait for the thread and process to start
158
+ sleep 0.01 until !thread.status.nil?
159
+ sleep 0.1
160
+ thread
161
+ end
162
+
163
+ # Executes the command in a new Ruby Thread. Will print the output if the
164
+ # process produces any output
165
+ #
166
+ # @param command [String] The command to execute via the 'system' call
167
+ def self.run_process_check_output(command)
168
+ thread = nil
169
+ thread = Thread.new do
170
+ output, _ = Open3.capture2e(command)
171
+ if !output.empty?
172
+ # Ignore modalSession messages on Mac Mavericks
173
+ new_output = ''
174
+ output.each_line do |line|
175
+ new_output << line if !/modalSession/.match?(line)
176
+ end
177
+ output = new_output
178
+
179
+ if !output.empty?
180
+ Logger.error output
181
+ self.write_unexpected_file(output)
182
+ end
183
+ end
184
+ end
185
+ # Wait for the thread and process to start
186
+ sleep 0.01 until !thread.status.nil?
187
+ sleep 0.1
188
+ thread
189
+ end
190
+
191
+ # Runs a hash algorithm over one or more files and returns the Digest object.
192
+ # Handles windows/unix new line differences but changes in whitespace will
193
+ # change the hash sum.
194
+ #
195
+ # Usage:
196
+ # digest = OpenC3.hash_files(files, additional_data, hashing_algorithm)
197
+ # digest.digest # => the 16 bytes of digest
198
+ # digest.hexdigest # => the formatted string in hex
199
+ #
200
+ # @param filenames [Array<String>] List of files to read and calculate a hashing
201
+ # sum on
202
+ # @param additional_data [String] Additional data to add to the hashing sum
203
+ # @param hashing_algorithm [String] Hashing algorithm to use
204
+ # @return [Digest::<algorithm>] The hashing sum object
205
+ def self.hash_files(filenames, additional_data = nil, hashing_algorithm = 'SHA256')
206
+ digest = Digest.const_get(hashing_algorithm).public_send('new')
207
+
208
+ filenames.each do |filename|
209
+ next if File.directory?(filename)
210
+
211
+ # Read the file's data and add to the running hashing sum
212
+ digest << File.read(filename)
213
+ end
214
+ digest << additional_data if additional_data
215
+ digest
216
+ end
217
+
218
+ # Opens a timestamped log file for writing. The opened file is yielded back
219
+ # to the block.
220
+ #
221
+ # @param filename [String] String to append to the exception log filename.
222
+ # The filename will start with a date/time stamp.
223
+ # @param log_dir [String] By default this method will write to the OpenC3
224
+ # default log directory. By setting this parameter you can override the
225
+ # directory the log will be written to.
226
+ # @yieldparam file [File] The log file
227
+ # @return [String|nil] The fully pathed log filename or nil if there was
228
+ # an error creating the log file.
229
+ def self.create_log_file(filename, log_dir = nil)
230
+ log_file = nil
231
+ begin
232
+ # The following code goes inside a begin rescue because it reads the
233
+ # system.txt configuration file. If this has an error we won't be able
234
+ # to determine the log path but we still want to write the log.
235
+ log_dir = System.instance.paths['LOGS'] unless log_dir
236
+ # Make sure the log directory exists
237
+ raise unless File.exist?(log_dir)
238
+ rescue Exception
239
+ log_dir = nil # Reset log dir since it failed above
240
+ # First check for ./logs
241
+ log_dir = './logs' if File.exist?('./logs')
242
+ # Prefer ./outputs/logs if it exists
243
+ log_dir = './outputs/logs' if File.exist?('./outputs/logs')
244
+ # If all else fails just use the local directory
245
+ log_dir = '.' unless log_dir
246
+ end
247
+ log_file = File.join(log_dir,
248
+ File.build_timestamped_filename([filename]))
249
+ # Check for the log file existing. This could happen if this method gets
250
+ # called more than once in the same second.
251
+ if File.exist?(log_file)
252
+ sleep 1.01 # Sleep before rebuilding the timestamp to get something unique
253
+ log_file = File.join(log_dir,
254
+ File.build_timestamped_filename([filename]))
255
+ end
256
+ begin
257
+ OPENC3_MUTEX.synchronize do
258
+ file = File.open(log_file, 'w')
259
+ yield file
260
+ ensure
261
+ file.close unless file.closed?
262
+ File.chmod(0444, log_file) # Make file read only
263
+ end
264
+ rescue Exception
265
+ # Ensure we always return
266
+ end
267
+ log_file = File.expand_path(log_file)
268
+ return log_file
269
+ end
270
+
271
+ # Writes a log file with information about the current configuration
272
+ # including the Ruby version, OpenC3 version, whether you are on Windows, the
273
+ # OpenC3 path, and the Ruby path along with the exception that
274
+ # is passed in.
275
+ #
276
+ # @param [String] filename String to append to the exception log filename.
277
+ # The filename will start with a date/time stamp.
278
+ # @param [String] log_dir By default this method will write to the OpenC3
279
+ # default log directory. By setting this parameter you can override the
280
+ # directory the log will be written to.
281
+ # @return [String|nil] The fully pathed log filename or nil if there was
282
+ # an error creating the log file.
283
+ def self.write_exception_file(exception, filename = 'exception', log_dir = nil)
284
+ log_file = create_log_file(filename, log_dir) do |file|
285
+ file.puts "Exception:"
286
+ if exception
287
+ file.puts exception.formatted
288
+ file.puts
289
+ else
290
+ file.puts "No Exception Given"
291
+ file.puts caller.join("\n")
292
+ file.puts
293
+ end
294
+ file.puts "Caller Backtrace:"
295
+ file.puts caller().join("\n")
296
+ file.puts
297
+
298
+ file.puts "Ruby Version: ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE} patchlevel #{RUBY_PATCHLEVEL}) [#{RUBY_PLATFORM}]"
299
+ file.puts "Rubygems Version: #{Gem::VERSION}"
300
+ file.puts "OpenC3 Version: #{OpenC3::VERSION}"
301
+ file.puts "OpenC3::PATH: #{OpenC3::PATH}"
302
+ file.puts ""
303
+ file.puts "Environment:"
304
+ file.puts "RUBYOPT: #{ENV['RUBYOPT']}"
305
+ file.puts "RUBYLIB: #{ENV['RUBYLIB']}"
306
+ file.puts "GEM_PATH: #{ENV['GEM_PATH']}"
307
+ file.puts "GEMRC: #{ENV['GEMRC']}"
308
+ file.puts "RI_DEVKIT: #{ENV['RI_DEVKIT']}"
309
+ file.puts "GEM_HOME: #{ENV['GEM_HOME']}"
310
+ file.puts "PATH: #{ENV['PATH']}"
311
+ file.puts ""
312
+ file.puts "Ruby Path:\n #{$:.join("\n ")}\n\n"
313
+ file.puts "Gems:"
314
+ Gem.loaded_specs.values.map { |x| file.puts "#{x.name} #{x.version} #{x.platform}" }
315
+ file.puts ""
316
+ file.puts "All Threads Backtraces:"
317
+ Thread.list.each do |thread|
318
+ file.puts thread.backtrace.join("\n")
319
+ file.puts
320
+ end
321
+ file.puts ""
322
+ file.puts ""
323
+ ensure
324
+ file.close
325
+ end
326
+ return log_file
327
+ end
328
+
329
+ # Writes a log file with information about unexpected output
330
+ #
331
+ # @param [String] text The unexpected output text
332
+ # @param [String] filename String to append to the exception log filename.
333
+ # The filename will start with a date/time stamp.
334
+ # @param [String] log_dir By default this method will write to the OpenC3
335
+ # default log directory. By setting this parameter you can override the
336
+ # directory the log will be written to.
337
+ # @return [String|nil] The fully pathed log filename or nil if there was
338
+ # an error creating the log file.
339
+ def self.write_unexpected_file(text, filename = 'unexpected', log_dir = nil)
340
+ log_file = create_log_file(filename, log_dir) do |file|
341
+ file.puts "Unexpected Output:\n\n"
342
+ file.puts text
343
+ ensure
344
+ file.close
345
+ end
346
+ return log_file
347
+ end
348
+
349
+ # Catch fatal exceptions within the block
350
+ # This is intended to catch exceptions before the GUI is available
351
+ def self.catch_fatal_exception
352
+ yield
353
+ rescue Exception => error
354
+ unless error.class == SystemExit or error.class == Interrupt
355
+ Logger.level = Logger::FATAL
356
+ OpenC3.handle_fatal_exception(error, false)
357
+ end
358
+ end
359
+
360
+ # Write a message to the Logger, write an exception file, and popup a GUI
361
+ # window if try_gui. Finally 'exit 1' is called to end the calling program.
362
+ #
363
+ # @param error [Exception] The exception to handle
364
+ # @param try_gui [Boolean] Whether to try and create a GUI exception popup
365
+ def self.handle_fatal_exception(error, try_gui = true)
366
+ unless error.class == SystemExit or error.class == Interrupt
367
+ $openc3_fatal_exception = error
368
+ self.write_exception_file(error)
369
+ Logger.level = Logger::FATAL
370
+ Logger.fatal "Fatal Exception! Exiting..."
371
+ Logger.fatal error.formatted
372
+ if $stdout != STDOUT
373
+ $stdout = STDOUT
374
+ Logger.fatal "Fatal Exception! Exiting..."
375
+ Logger.fatal error.formatted
376
+ end
377
+ sleep 1 # Allow the messages to be printed and then crash
378
+ exit 1
379
+ else
380
+ exit 0
381
+ end
382
+ end
383
+
384
+ # CriticalErrors are errors that need to be brought to a user's attention but
385
+ # do not cause an exit. A good example is if the packet log writer fails and
386
+ # can no longer write the log file. Write a message to the Logger, write an
387
+ # exception file, and popup a GUI window if try_gui. Ensure the GUI only
388
+ # comes up once so this method can be called over and over by failing code.
389
+ #
390
+ # @param error [Exception] The exception to handle
391
+ # @param try_gui [Boolean] Whether to try and create a GUI exception popup
392
+ def self.handle_critical_exception(error, try_gui = true)
393
+ Logger.error "Critical Exception! #{error.formatted}"
394
+ self.write_exception_file(error)
395
+ end
396
+
397
+ # Creates a Ruby Thread to run the given block. Rescues any exceptions and
398
+ # retries the threads the given number of times before handling the thread
399
+ # death by calling {OpenC3.handle_fatal_exception}.
400
+ #
401
+ # @param name [String] Name of the thread
402
+ # @param retry_attempts [Integer] The number of times to allow the thread to
403
+ # restart before exiting
404
+ def self.safe_thread(name, retry_attempts = 0)
405
+ Thread.new do
406
+ retry_count = 0
407
+ begin
408
+ yield
409
+ rescue => error
410
+ Logger.error "#{name} thread unexpectedly died. Retries: #{retry_count} of #{retry_attempts}"
411
+ Logger.error error.formatted
412
+ retry_count += 1
413
+ if retry_count <= retry_attempts
414
+ self.write_exception_file(error)
415
+ retry
416
+ end
417
+ handle_fatal_exception(error)
418
+ end
419
+ end
420
+ end
421
+
422
+ # Require the class represented by the filename. This uses the standard Ruby
423
+ # convention of having a single class per file where the class name is camel
424
+ # cased and filename is lowercase with underscores.
425
+ #
426
+ # @param class_name_or_class_filename [String] The name of the class or the file which contains the
427
+ # Ruby class to require
428
+ # @param log_error [Boolean] Whether to log an error if we can't require the class
429
+ def self.require_class(class_name_or_class_filename, log_error = true)
430
+ if class_name_or_class_filename.downcase[-3..-1] == '.rb' or (class_name_or_class_filename[0] == class_name_or_class_filename[0].downcase)
431
+ class_filename = class_name_or_class_filename
432
+ class_name = class_filename.filename_to_class_name
433
+ else
434
+ class_name = class_name_or_class_filename
435
+ class_filename = class_name.class_name_to_filename
436
+ end
437
+ return class_name.to_class if class_name.to_class and defined? class_name.to_class
438
+
439
+ self.require_file(class_filename, log_error)
440
+ klass = class_name.to_class
441
+ raise "Ruby class #{class_name} not found" unless klass
442
+
443
+ klass
444
+ end
445
+
446
+ # Requires a file with a standard error message if it fails
447
+ #
448
+ # @param filename [String] The name of the file to require
449
+ # @param log_error [Boolean] Whether to log an error if we can't require the class
450
+ def self.require_file(filename, log_error = true)
451
+ require filename
452
+ rescue Exception => err
453
+ msg = "Unable to require #{filename} due to #{err.message}. "\
454
+ "Ensure #{filename} is in the OpenC3 lib directory."
455
+ Logger.error msg if log_error
456
+ raise $!, msg, $!.backtrace
457
+ end
458
+
459
+ # @param filename [String] Name of the file to open in the web browser
460
+ def self.open_in_web_browser(filename)
461
+ if filename
462
+ if Kernel.is_windows?
463
+ self.run_process("cmd /c \"start \"\" \"#{filename.gsub('/', '\\')}\"\"")
464
+ elsif Kernel.is_mac?
465
+ self.run_process("open -a Safari \"#{filename}\"")
466
+ else
467
+ which_firefox = `which firefox`.chomp
468
+ if which_firefox =~ /Command not found/i or which_firefox =~ /no .* in/i
469
+ raise "Firefox not found"
470
+ else
471
+ system_call = "#{which_firefox} \"#{filename}\""
472
+ end
473
+
474
+ self.run_process(system_call)
475
+ end
476
+ end
477
+ end
478
+
479
+ # Temporarily set the working directory during a block
480
+ # Working directory is global, so this can make other threads wait
481
+ # Ruby Dir.chdir with block always throws an error if multiple threads
482
+ # call Dir.chdir
483
+ def self.set_working_dir(working_dir, &block)
484
+ if $openc3_chdir_mutex.owned?
485
+ set_working_dir_internal(working_dir, &block)
486
+ else
487
+ $openc3_chdir_mutex.synchronize do
488
+ set_working_dir_internal(working_dir, &block)
489
+ end
490
+ end
491
+ end
492
+
493
+ # Private helper method
494
+ def self.set_working_dir_internal(working_dir)
495
+ current_dir = Dir.pwd
496
+ Dir.chdir(working_dir)
497
+ begin
498
+ yield
499
+ ensure
500
+ Dir.chdir(current_dir)
501
+ end
502
+ end
503
+
504
+ # Attempt to gracefully kill a thread
505
+ # @param owner Object that owns the thread and may have a graceful_kill method
506
+ # @param thread The thread to gracefully kill
507
+ # @param graceful_timeout Timeout in seconds to wait for it to die gracefully
508
+ # @param timeout_interval How often to poll for aliveness
509
+ # @param hard_timeout Timeout in seconds to wait for it to die ungracefully
510
+ def self.kill_thread(owner, thread, graceful_timeout = 1, timeout_interval = 0.01, hard_timeout = 1)
511
+ if thread
512
+ if owner and owner.respond_to? :graceful_kill
513
+ if Thread.current != thread
514
+ owner.graceful_kill
515
+ end_time = Time.now.sys + graceful_timeout
516
+ while thread.alive? && ((end_time - Time.now.sys) > 0)
517
+ sleep(timeout_interval)
518
+ end
519
+ else
520
+ Logger.warn "Threads cannot graceful_kill themselves"
521
+ end
522
+ elsif owner
523
+ Logger.info "Thread owner #{owner.class} does not support graceful_kill"
524
+ end
525
+ if thread.alive?
526
+ # If the thread dies after alive? but before backtrace, bt will be nil.
527
+ bt = thread.backtrace
528
+
529
+ # Graceful failed
530
+ msg = "Failed to gracefully kill thread:\n"
531
+ msg << " Caller Backtrace:\n #{caller().join("\n ")}\n"
532
+ msg << " \n Thread Backtrace:\n #{bt.join("\n ")}\n" if bt
533
+ msg << "\n"
534
+ Logger.warn msg
535
+ thread.kill
536
+ end_time = Time.now.sys + hard_timeout
537
+ while thread.alive? && ((end_time - Time.now.sys) > 0)
538
+ sleep(timeout_interval)
539
+ end
540
+ end
541
+ if thread.alive?
542
+ Logger.error "Failed to kill thread"
543
+ end
544
+ end
545
+ end
546
+
547
+ # Close a socket in a manner that ensures that any reads blocked in select
548
+ # will unblock across platforms
549
+ # @param socket The socket to close
550
+ def self.close_socket(socket)
551
+ if socket
552
+ # Calling shutdown and then sleep seems to be required
553
+ # to get select to reliably unblock on linux
554
+ begin
555
+ socket.shutdown(:RDWR)
556
+ sleep(0)
557
+ rescue Exception
558
+ # Oh well we tried
559
+ end
560
+ begin
561
+ socket.close unless socket.closed?
562
+ rescue Exception
563
+ # Oh well we tried
564
+ end
565
+ end
566
+ end
567
+ end
568
+
569
+ # The following code makes most older COSMOS 5 plugins still work with OpenC3
570
+ # New plugins should only use openc3 paths and module OpenC3
571
+ unless ENV['OPENC3_NO_COSMOS_COMPATIBILITY']
572
+ Cosmos = OpenC3
573
+ ENV['COSMOS_SCOPE'] = ENV['OPENC3_SCOPE']
574
+ module CosmosCompatibility
575
+ def require(*args)
576
+ filename = args[0]
577
+ if filename[0..6] == "cosmos/"
578
+ filename[0..6] = "openc3/"
579
+ end
580
+ args[0] = filename
581
+ super(*args)
582
+ end
583
+ def load(*args)
584
+ filename = args[0]
585
+ if filename[0..6] == "cosmos/"
586
+ filename[0..6] = "openc3/"
587
+ end
588
+ args[0] = filename
589
+ super(*args)
590
+ end
591
+ end
592
+ class Object
593
+ include CosmosCompatibility
594
+ end
595
+ end
@@ -0,0 +1,52 @@
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/topics/topic'
21
+
22
+ module OpenC3
23
+ class AutonomicTopic < Topic
24
+ PRIMARY_KEY = "__openc3_autonomic"
25
+
26
+ # Notify to the topic
27
+ #
28
+ # ```json
29
+ # {
30
+ # "kind" => "created",
31
+ # "type" => "trigger",
32
+ # "data" => {
33
+ # "name" => "foobar",
34
+ # "target": "INST",
35
+ # "packet": "ADCS",
36
+ # "left": {
37
+ # "type": "item",
38
+ # "item": "POSX",
39
+ # },
40
+ # "operation": ">",
41
+ # "right": {
42
+ # "type": "value",
43
+ # "value": 690000,
44
+ # }
45
+ # }
46
+ # }
47
+ # ```
48
+ def self.write_notification(notification, scope:)
49
+ Topic.write_topic("#{scope}#{PRIMARY_KEY}", notification, '*', 1000)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,44 @@
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/topics/topic'
21
+
22
+ module OpenC3
23
+ class CalendarTopic < Topic
24
+ PRIMARY_KEY = '__openc3_calendar'.freeze
25
+
26
+ # Write an activity to the topic
27
+ #
28
+ # ```json
29
+ # {
30
+ # "type" => "metadata",
31
+ # "kind" => "created",
32
+ # "metadata" => {
33
+ # "target" => "FOO",
34
+ # "start" => 1621875570,
35
+ # "color" => "#FF0000",
36
+ # "metadata" => {"test"=>"123456"}
37
+ # },
38
+ # }
39
+ # ```
40
+ def self.write_entry(entry, scope:)
41
+ Topic.write_topic("#{scope}#{PRIMARY_KEY}", entry, '*', 1000)
42
+ end
43
+ end
44
+ end