openc3 5.0.6

Sign up to get free protection for your applications and to get access to all the features.
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,541 @@
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/reaction_model'
22
+ require 'openc3/models/notification_model'
23
+ require 'openc3/models/trigger_model'
24
+ require 'openc3/topics/autonomic_topic'
25
+ require 'openc3/utilities/authentication'
26
+
27
+ require 'openc3/script'
28
+
29
+ module OpenC3
30
+
31
+ # This should remain a thread safe implamentation. This is the in memory
32
+ # cache that should mirror the database. This will update two hash
33
+ # variables and will track triggers to lookup what triggers link to what
34
+ # reactions.
35
+ class ReactionBase
36
+
37
+ def initialize(scope:)
38
+ @scope = scope
39
+ @reactions_mutex = Mutex.new
40
+ @reactions = Hash.new
41
+ @lookup_mutex = Mutex.new
42
+ @lookup = Hash.new
43
+ end
44
+
45
+ # RETURNS an Array of active and not snoozed reactions
46
+ def get_snoozed
47
+ data = nil
48
+ @reactions_mutex.synchronize do
49
+ data = Marshal.load( Marshal.dump(@reactions) )
50
+ end
51
+ ret = Array.new
52
+ return ret unless data
53
+ data.each do | _name, r_hash |
54
+ data = Marshal.load( Marshal.dump(r_hash) )
55
+ reaction = ReactionModel.from_json(data, name: data['name'], scope: data['scope'])
56
+ ret << reaction if reaction.active && reaction.snoozed_until
57
+ end
58
+ return ret
59
+ end
60
+
61
+ # RETURNS an Array of active and not snoozed reactions
62
+ def get_reactions(trigger_name:)
63
+ array_value = nil
64
+ @lookup_mutex.synchronize do
65
+ array_value = Marshal.load( Marshal.dump(@lookup[trigger_name]) )
66
+ end
67
+ ret = Array.new
68
+ return ret unless array_value
69
+ array_value.each do | name |
70
+ @reactions_mutex.synchronize do
71
+ data = Marshal.load( Marshal.dump(@reactions[name]) )
72
+ reaction = ReactionModel.from_json(data, name: data['name'], scope: data['scope'])
73
+ ret << reaction if reaction.active && reaction.snoozed_until.nil?
74
+ end
75
+ end
76
+ return ret
77
+ end
78
+
79
+ # Update the memeory database with a HASH of reactions from the external
80
+ # database
81
+ def setup(reactions:)
82
+ @reactions_mutex.synchronize do
83
+ @reactions = Marshal.load( Marshal.dump(reactions) )
84
+ end
85
+ @lookup_mutex.synchronize do
86
+ @lookup = Hash.new
87
+ reactions.each do | reaction_name, reaction |
88
+ reaction['triggers'].each do | trigger |
89
+ trigger_name = trigger['name']
90
+ if @lookup[trigger_name].nil?
91
+ @lookup[trigger_name] = [reaction_name]
92
+ else
93
+ @lookup[trigger_name] << reaction_name
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ # Pulls the latest reaction name from the in memory database to see
101
+ # if the reaction should be put to sleep.
102
+ def sleep(name:)
103
+ @reactions_mutex.synchronize do
104
+ data = Marshal.load( Marshal.dump(@reactions[name]) )
105
+ return unless data
106
+ reaction = ReactionModel.from_json(data, name: data['name'], scope: data['scope'])
107
+ if reaction.snoozed_until.nil? || Time.now.to_i >= reaction.snoozed_until
108
+ reaction.sleep()
109
+ end
110
+ @reactions[name] = reaction.as_json(:allow_nan => true)
111
+ end
112
+ end
113
+
114
+ # Pulls the latest reaction name from the in memory database to see
115
+ # if the reaction should be awaken.
116
+ def wake(name:)
117
+ @reactions_mutex.synchronize do
118
+ data = Marshal.load( Marshal.dump(@reactions[name]) )
119
+ return unless data
120
+ reaction = ReactionModel.from_json(data, name: data['name'], scope: data['scope'])
121
+ reaction.awaken()
122
+ @reactions[name] = reaction.as_json(:allow_nan => true)
123
+ end
124
+ end
125
+
126
+ # Add a reaction to the in memory database
127
+ def add(reaction:)
128
+ reaction_name = reaction['name']
129
+ @reactions_mutex.synchronize do
130
+ @reactions[reaction_name] = reaction
131
+ end
132
+ reaction['triggers'].each do | trigger |
133
+ trigger_name = trigger['name']
134
+ @lookup_mutex.synchronize do
135
+ if @lookup[trigger_name].nil?
136
+ @lookup[trigger_name] = [reaction_name]
137
+ else
138
+ @lookup[trigger_name] << reaction_name
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ # Updates a reaction to the in memory database. This current does not
145
+ # update the lookup Hash for the triggers.
146
+ def update(reaction:)
147
+ reaction_name = reaction['name']
148
+ @reactions_mutex.synchronize do
149
+ @reactions[reaction_name] = reaction
150
+ end
151
+ end
152
+
153
+ # Removes a reaction to the in memory database.
154
+ def remove(reaction:)
155
+ reaction_name = reaction['name']
156
+ @reactions_mutex.synchronize do
157
+ @reactions.delete(reaction_name)
158
+ end
159
+ reaction['triggers'].each do | trigger |
160
+ trigger_name = trigger['name']
161
+ @lookup_mutex.synchronize do
162
+ @lookup[trigger_name].delete(reaction_name)
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ # This should remain a thread safe implamentation.
169
+ class QueueBase
170
+
171
+ attr_reader :queue
172
+
173
+ def initialize(scope:)
174
+ @queue = Queue.new
175
+ end
176
+
177
+ def enqueue(kind:, data:)
178
+ @queue << [kind, data]
179
+ end
180
+ end
181
+
182
+ # This should remain a thread safe implamentation.
183
+ class SnoozeBase
184
+
185
+ def initialize(scope:)
186
+ # store the round robin watch
187
+ @watch_mutex = Mutex.new
188
+ @watch_size = 25
189
+ @watch_queue = Array.new(@watch_size)
190
+ @watch_index = 0
191
+ end
192
+
193
+ def not_queued?(reaction:)
194
+ key = "#{reaction.name}__#{reaction.snoozed_until}"
195
+ @watch_mutex.synchronize do
196
+ return false if @watch_queue.index(key)
197
+ @watch_queue[@watch_index] = key
198
+ @watch_index = @watch_index + 1 >= @watch_size ? 0 : @watch_index + 1
199
+ return true
200
+ end
201
+ end
202
+ end
203
+
204
+ # Shared between the monitor thread and the manager thread to
205
+ # share the resources.
206
+ class ReactionShare
207
+
208
+ attr_reader :reaction_base, :queue_base, :snooze_base
209
+
210
+ def initialize(scope:)
211
+ @reaction_base = ReactionBase.new(scope: scope)
212
+ @queue_base = QueueBase.new(scope: scope)
213
+ @snooze_base = SnoozeBase.new(scope: scope)
214
+ end
215
+
216
+ end
217
+
218
+ # The Reaction worker is a very simple thread pool worker. Once the manager
219
+ # queues a trigger to evaluate against the reactions. The worker will check
220
+ # the reactions to see if it needs to fire any reactions.
221
+ class ReactionWorker
222
+ REACTION_METRIC_NAME = 'reaction_duration_seconds'.freeze
223
+
224
+ attr_reader :name, :scope, :share
225
+
226
+ def initialize(name:, scope:, share:, ident:)
227
+ @name = name
228
+ @scope = scope
229
+ @share = share
230
+ @ident = ident
231
+ @metric_output_time = 0
232
+ @metric = Metric.new(microservice: @name, scope: @scope)
233
+ @authentication = generate_auth()
234
+ end
235
+
236
+ # generate the auth object
237
+ def generate_auth
238
+ if ENV['OPENC3_API_USER'].nil? || ENV['OPENC3_API_CLIENT'].nil?
239
+ return OpenC3Authentication.new()
240
+ else
241
+ return OpenC3KeycloakAuthentication.new(ENV['OPENC3_KEYCLOAK_URL'])
242
+ end
243
+ end
244
+
245
+ def reaction(data:)
246
+ return ReactionModel.from_json(data, name: data['name'], scope: data['scope'])
247
+ end
248
+
249
+ def run
250
+ Logger.info "ReactionWorker-#{@ident} running"
251
+ loop do
252
+ begin
253
+ kind, data = @share.queue_base.queue.pop
254
+ break if kind.nil? || data.nil?
255
+ case kind
256
+ when 'reaction'
257
+ run_reaction(reaction: reaction(data: data))
258
+ when 'trigger'
259
+ process_enabled_trigger(data: data)
260
+ end
261
+ current_time = Time.now.to_i
262
+ if @metric_output_time < current_time
263
+ @metric.output
264
+ @metric_output_time = current_time + 120
265
+ end
266
+ rescue StandardError => e
267
+ Logger.error "ReactionWorker-#{@ident} failed to evaluate kind: #{kind} data: #{data}\n#{e.formatted}"
268
+ end
269
+ end
270
+ Logger.info "ReactionWorker-#{@ident} exiting"
271
+ end
272
+
273
+ def process_enabled_trigger(data:)
274
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
275
+ @share.reaction_base.get_reactions(trigger_name: data['name']).each do | reaction |
276
+ run_reaction(reaction: reaction)
277
+ end
278
+ diff = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start # seconds as a float
279
+ metric_labels = { 'type' => 'trigger', 'thread' => "worker-#{@ident}" }
280
+ @metric.add_sample(name: REACTION_METRIC_NAME, value: diff, labels: metric_labels)
281
+ end
282
+
283
+ def run_reaction(reaction:)
284
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
285
+ reaction.actions.each do |action|
286
+ run_action(reaction: reaction, action: action)
287
+ end
288
+ @share.reaction_base.sleep(name: reaction.name)
289
+ diff = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start # seconds as a float
290
+ metric_labels = { 'type' => 'reaction', 'thread' => "worker-#{@ident}" }
291
+ @metric.add_sample(name: REACTION_METRIC_NAME, value: diff, labels: metric_labels)
292
+ end
293
+
294
+ def run_action(reaction:, action:)
295
+ case action['type']
296
+ when 'command'
297
+ run_command(reaction: reaction, action: action)
298
+ when 'script'
299
+ run_script(reaction: reaction, action: action)
300
+ end
301
+ end
302
+
303
+ def run_command(reaction:, action:)
304
+ Logger.debug "ReactionWorker-#{@ident} running reaction #{reaction.name}, command: '#{action['value']}' "
305
+ begin
306
+ cmd_no_hazardous_check(action['value'], scope: @scope)
307
+ Logger.info "ReactionWorker-#{@ident} #{reaction.name} command action complete, #{action['value']}"
308
+ rescue StandardError => e
309
+ Logger.error "ReactionWorker-#{@ident} #{reaction.name} command action failed, #{action}\n#{e.message}"
310
+ end
311
+ end
312
+
313
+ def run_script(reaction:, action:)
314
+ Logger.debug "ReactionWorker-#{@ident} running reaction #{reaction.name}, script: '#{action['value']}'"
315
+ begin
316
+ request = Net::HTTP::Post.new(
317
+ "/script-api/scripts/#{action['value']}/run?scope=#{@scope}",
318
+ 'Content-Type' => 'application/json',
319
+ 'Authorization' => @authentication.token()
320
+ )
321
+ request.body = JSON.generate({
322
+ 'scope' => @scope,
323
+ 'environment' => action['environment'],
324
+ 'reaction' => reaction.name,
325
+ 'id' => Time.now.to_i
326
+ })
327
+ hostname = ENV['OPENC3_SCRIPT_HOSTNAME'] || 'openc3-script-runner-api'
328
+ response = Net::HTTP.new(hostname, 2902).request(request)
329
+ raise "failed to call #{hostname}, for script: #{action['value']}, response code: #{response.code}" if response.code != '200'
330
+
331
+ Logger.info "ReactionWorker-#{@ident} #{reaction.name} script action complete, #{action['value']} => #{response.body}"
332
+ rescue StandardError => e
333
+ Logger.error "ReactionWorker-#{@ident} #{reaction.name} script action failed, #{action}\n#{e.message}"
334
+ end
335
+ end
336
+ end
337
+
338
+ # The reaction snooze manager starts a thread pool and keeps track of when a
339
+ # reaction is activated and to evalute triggers when the snooze is complete.
340
+ class ReactionSnoozeManager
341
+ SNOOZE_METRIC_NAME = 'snooze_manager_duration_seconds'.freeze
342
+
343
+ attr_reader :name, :scope, :share, :thread_pool
344
+
345
+ def initialize(name:, scope:, share:)
346
+ @name = name
347
+ @scope = scope
348
+ @share = share
349
+ @worker_count = 3
350
+ @thread_pool = nil
351
+ @cancel_thread = false
352
+ @metric = Metric.new(microservice: @name, scope: @scope)
353
+ @metric_output_time = 0
354
+ end
355
+
356
+ def generate_thread_pool()
357
+ thread_pool = []
358
+ @worker_count.times do | i |
359
+ worker = ReactionWorker.new(name: @name, scope: @scope, share: @share, ident: i)
360
+ thread_pool << Thread.new { worker.run }
361
+ end
362
+ return thread_pool
363
+ end
364
+
365
+ def run
366
+ Logger.info "ReactionSnoozeManager running"
367
+ @thread_pool = generate_thread_pool()
368
+ loop do
369
+ begin
370
+ current_time = Time.now.to_i
371
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
372
+ manage_snoozed_reactions(current_time: current_time)
373
+ diff = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start # seconds as a float
374
+ metric_labels = { 'type' => 'snooze', 'thread' => "manager" }
375
+ @metric.add_sample(name: SNOOZE_METRIC_NAME, value: diff, labels: metric_labels)
376
+ if @metric_output_time < current_time
377
+ @metric.output
378
+ @metric_output_time = current_time + 120
379
+ end
380
+ rescue StandardError => e
381
+ Logger.error "ReactionSnoozeManager failed to snooze reactions.\n#{e.formatted}"
382
+ end
383
+ break if @cancel_thread
384
+ sleep(1)
385
+ break if @cancel_thread
386
+ end
387
+ Logger.info "ReactionSnoozeManager exiting"
388
+ end
389
+
390
+ def active_triggers(reaction:)
391
+ reaction.triggers.each do | trigger |
392
+ t = TriggerModel.get(name: trigger['name'], group: trigger['group'], scope: @scope)
393
+ return true if t && t.state
394
+ end
395
+ return false
396
+ end
397
+
398
+ def manage_snoozed_reactions(current_time:)
399
+ @share.reaction_base.get_snoozed.each do | reaction |
400
+ time_difference = reaction.snoozed_until - current_time
401
+ if time_difference <= 0 && @share.snooze_base.not_queued?(reaction: reaction)
402
+ Logger.info "#{reaction.name} current: #{current_time}, vs #{reaction.snoozed_until}, #{time_difference}"
403
+ unless reaction.review
404
+ Logger.debug "#{reaction.name} review set to false, setting snoozed_until back to nil"
405
+ @share.reaction_base.wake(name: reaction.name)
406
+ next
407
+ end
408
+ if active_triggers(reaction: reaction)
409
+ @share.queue_base.enqueue(kind: 'reaction', data: reaction.as_json(:allow_nan => true))
410
+ else
411
+ @share.reaction_base.wake(name: reaction.name)
412
+ end
413
+ end
414
+ end
415
+ end
416
+
417
+ def shutdown
418
+ @cancel_thread = true
419
+ @worker_count.times do | i |
420
+ @share.queue_base.enqueue(kind: nil, data: nil)
421
+ end
422
+ end
423
+ end
424
+
425
+ # The reaction microservice starts a manager then gets the
426
+ # reactions and triggers from redis. It then monitors the
427
+ # AutonomicTopic for changes.
428
+ class ReactionMicroservice < Microservice
429
+ ACTION_METRIC_NAME = 'reactions_duration_seconds'.freeze
430
+
431
+ attr_reader :name, :scope, :share, :manager, :manager_thread
432
+
433
+ def initialize(*args)
434
+ super(*args)
435
+ @share = ReactionShare.new(scope: @scope)
436
+ @manager = ReactionSnoozeManager.new(name: @name, scope: @scope, share: @share)
437
+ @manager_thread = nil
438
+ @read_topic = true
439
+ end
440
+
441
+ def run
442
+ Logger.info "ReactionMicroservice running"
443
+ @manager_thread = Thread.new { @manager.run }
444
+ loop do
445
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
446
+ reactions = ReactionModel.all(scope: @scope)
447
+ @share.reaction_base.setup(reactions: reactions)
448
+ diff = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start # seconds as a float
449
+ @metric.add_sample(name: ACTION_METRIC_NAME, value: diff, labels: { 'thread' => 'microservice' })
450
+ break if @cancel_thread
451
+
452
+ block_for_updates()
453
+ break if @cancel_thread
454
+ end
455
+ Logger.info "ReactionMicroservice exiting"
456
+ end
457
+
458
+ def topic_lookup_functions
459
+ return {
460
+ 'group' => {
461
+ 'created' => :no_op,
462
+ 'updated' => :no_op,
463
+ 'deleted' => :no_op,
464
+ },
465
+ 'trigger' => {
466
+ 'created' => :no_op,
467
+ 'updated' => :no_op,
468
+ 'deleted' => :no_op,
469
+ 'enabled' => :trigger_enabled_event,
470
+ 'disabled' => :no_op,
471
+ 'activated' => :no_op,
472
+ 'deactivated' => :no_op,
473
+ },
474
+ 'reaction' => {
475
+ 'created' => :reaction_created_event,
476
+ 'updated' => :refresh_event,
477
+ 'deleted' => :reaction_deleted_event,
478
+ 'sleep' => :no_op,
479
+ 'awaken' => :no_op,
480
+ 'activated' => :reaction_updated_event,
481
+ 'deactivated' => :reaction_updated_event,
482
+ }
483
+ }
484
+ end
485
+
486
+ def block_for_updates
487
+ @read_topic = true
488
+ while @read_topic
489
+ begin
490
+ AutonomicTopic.read_topics(@topics) do |_topic, _msg_id, msg_hash, _redis|
491
+ Logger.debug "ReactionMicroservice block_for_updates: #{msg_hash.to_s}"
492
+ public_send(topic_lookup_functions[msg_hash['type']][msg_hash['kind']], msg_hash)
493
+ end
494
+ rescue StandardError => e
495
+ Logger.error "ReactionMicroservice failed to read topics #{@topics}\n#{e.formatted}"
496
+ end
497
+ end
498
+ end
499
+
500
+ def no_op(data)
501
+ Logger.debug "ReactionMicroservice web socket event: #{data}"
502
+ end
503
+
504
+ def refresh_event(data)
505
+ Logger.debug "ReactionMicroservice web socket schedule refresh: #{data}"
506
+ @read_topic = false
507
+ end
508
+
509
+ #
510
+ def trigger_enabled_event(msg_hash)
511
+ Logger.debug "ReactionMicroservice trigger event msg_hash: #{msg_hash}"
512
+ @share.queue_base.enqueue(kind: 'trigger', data: JSON.parse(msg_hash['data'], :allow_nan => true, :create_additions => true))
513
+ end
514
+
515
+ # Add the reaction to the shared data.
516
+ def reaction_created_event(msg_hash)
517
+ Logger.debug "ReactionMicroservice reaction created msg_hash: #{msg_hash}"
518
+ @share.reaction_base.add(reaction: JSON.parse(msg_hash['data'], :allow_nan => true, :create_additions => true))
519
+ end
520
+
521
+ # Update the reaction to the shared data.
522
+ def reaction_updated_event(msg_hash)
523
+ Logger.debug "ReactionMicroservice reaction updated msg_hash: #{msg_hash}"
524
+ @share.reaction_base.update(reaction: JSON.parse(msg_hash['data'], :allow_nan => true, :create_additions => true))
525
+ end
526
+
527
+ # Remove the reaction from the shared data
528
+ def reaction_deleted_event(msg_hash)
529
+ Logger.debug "ReactionMicroservice reaction deleted msg_hash: #{msg_hash}"
530
+ @share.reaction_base.remove(reaction: JSON.parse(msg_hash['data'], :allow_nan => true, :create_additions => true))
531
+ end
532
+
533
+ def shutdown
534
+ @read_topic = false
535
+ @manager.shutdown()
536
+ super
537
+ end
538
+ end
539
+ end
540
+
541
+ OpenC3::ReactionMicroservice.run if __FILE__ == $0