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,330 @@
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/models/model'
21
+ require 'openc3/models/microservice_model'
22
+ require 'openc3/models/target_model'
23
+ require 'openc3/topics/autonomic_topic'
24
+
25
+ module OpenC3
26
+ class TriggerError < StandardError; end
27
+
28
+ class TriggerInputError < TriggerError; end
29
+
30
+ # INPUT:
31
+ # {
32
+ # "group": "someGroup",
33
+ # "left": {
34
+ # "type": "item",
35
+ # "target": "INST",
36
+ # "packet": "ADCS",
37
+ # "item": "POSX",
38
+ # },
39
+ # "operator": ">",
40
+ # "right": {
41
+ # "type": "value",
42
+ # "value": 690000,
43
+ # }
44
+ # }
45
+ class TriggerModel < Model
46
+ PRIMARY_KEY = '__TRIGGERS__'.freeze
47
+ ITEM_TYPE = 'item'.freeze
48
+ LIMIT_TYPE = 'limit'.freeze
49
+ FLOAT_TYPE = 'float'.freeze
50
+ STRING_TYPE = 'string'.freeze
51
+ TRIGGER_TYPE = 'trigger'.freeze
52
+
53
+ def self.create_mini_id
54
+ time = (Time.now.to_f * 10_000_000).to_i
55
+ jitter = rand(10_000_000)
56
+ key = "#{jitter}#{time}".to_i.to_s(36)
57
+ return "TV0-#{key}"
58
+ end
59
+
60
+ # @return [TriggerModel] Return the object with the name at
61
+ def self.get(name:, group:, scope:)
62
+ json = super("#{scope}#{PRIMARY_KEY}#{group}", name: name)
63
+ unless json.nil?
64
+ self.from_json(json, name: name, scope: scope)
65
+ end
66
+ end
67
+
68
+ # @return [Array<Hash>] All the Key, Values stored under the name key
69
+ def self.all(group:, scope:)
70
+ super("#{scope}#{PRIMARY_KEY}#{group}")
71
+ end
72
+
73
+ # @return [Array<String>] All the uuids stored under the name key
74
+ def self.names(group:, scope:)
75
+ super("#{scope}#{PRIMARY_KEY}#{group}")
76
+ end
77
+
78
+ # Check dependents before delete.
79
+ def self.delete(name:, group:, scope:)
80
+ model = self.get(name: name, group: group, scope: scope)
81
+ if model.nil?
82
+ raise TriggerInputError.new "invalid operation group: #{group} trigger: #{name} not found"
83
+ end
84
+ unless model.dependents.empty?
85
+ raise TriggerError.new "failed to delete #{name} dependents: #{model.dependents}"
86
+ end
87
+ model.roots.each do | trigger |
88
+ trigger_model = self.get(name: trigger, group: group, scope: scope)
89
+ trigger_model.update_dependents(dependent: name, remove: true)
90
+ trigger_model.update()
91
+ end
92
+ Store.hdel("#{scope}#{PRIMARY_KEY}#{group}", name)
93
+ model.notify(kind: 'deleted')
94
+ end
95
+
96
+ def validate_operand(operand:)
97
+ unless operand.is_a?(Hash)
98
+ raise TriggerInputError.new "invalid operand: #{operand}"
99
+ end
100
+ operand_types = [ITEM_TYPE, LIMIT_TYPE, FLOAT_TYPE, STRING_TYPE, TRIGGER_TYPE]
101
+ unless operand_types.include?(operand['type'])
102
+ raise TriggerInputError.new "invalid operand type: #{operand['type']} must be of type: #{operand_types}"
103
+ end
104
+ if operand[operand['type']].nil?
105
+ raise TriggerInputError.new "invalid operand must contain type: #{operand}"
106
+ end
107
+ case operand['type']
108
+ when ITEM_TYPE
109
+ if operand['target'].nil? || operand['packet'].nil? || operand['raw'].nil?
110
+ raise TriggerInputError.new "invalid operand must contain target, packet, item, and raw: #{operand}"
111
+ end
112
+ when TRIGGER_TYPE
113
+ @roots << operand[operand['type']]
114
+ end
115
+ return operand
116
+ end
117
+
118
+ def validate_operator(operator:)
119
+ unless operator.is_a?(String)
120
+ raise TriggerInputError.new "invalid operator: #{operator}"
121
+ end
122
+ operators = ['>', '<', '>=', '<=']
123
+ match_operators = ['==', '!=']
124
+ trigger_operators = ['AND', 'OR']
125
+ if @roots.empty? && operators.include?(operator)
126
+ return operator
127
+ elsif @roots.empty? && match_operators.include?(operator)
128
+ return operator
129
+ elsif @roots.size() == 2 && trigger_operators.include?(operator)
130
+ return operator
131
+ elsif operators.include?(operator)
132
+ raise TriggerInputError.new "invalid operator pair: '#{operator}' must be of type: #{trigger_operators}"
133
+ else
134
+ raise TriggerInputError.new "invalid operator: '#{operator}' must be of type: #{operators}"
135
+ end
136
+ end
137
+
138
+ def validate_description(description:)
139
+ if description.nil?
140
+ left_type = @left['type']
141
+ right_type = @right['type']
142
+ return "#{@left[left_type]} #{@operator} #{@right[right_type]}"
143
+ end
144
+ unless description.is_a?(String)
145
+ raise TriggerInputError.new "invalid description: #{description}"
146
+ end
147
+ return description
148
+ end
149
+
150
+ attr_reader :name, :scope, :state, :group, :active, :left, :operator, :right, :dependents, :roots
151
+
152
+ #
153
+ def initialize(
154
+ name:,
155
+ scope:,
156
+ group:,
157
+ left:,
158
+ operator:,
159
+ right:,
160
+ state: false,
161
+ active: true,
162
+ description: nil,
163
+ dependents: nil,
164
+ updated_at: nil
165
+ )
166
+ if name.nil? || scope.nil? || group.nil? || left.nil? || operator.nil? || right.nil?
167
+ raise TriggerInputError.new "#{name}, #{scope}, #{group}, #{left}, #{operator}, or #{right} must not be nil"
168
+ end
169
+ super("#{scope}#{PRIMARY_KEY}#{group}", name: name, scope: scope)
170
+ @roots = []
171
+ @group = group
172
+ @state = state
173
+ @active = active
174
+ @left = validate_operand(operand: left)
175
+ @right = validate_operand(operand: right)
176
+ @operator = validate_operator(operator: operator)
177
+ @description = validate_description(description: description)
178
+ @dependents = dependents
179
+ @updated_at = updated_at
180
+ end
181
+
182
+ def verify_triggers
183
+ unless @group.is_a?(String)
184
+ raise TriggerInputError.new "invalid group: #{@group}"
185
+ end
186
+ selected_group = OpenC3::TriggerGroupModel.get(name: @group, scope: @scope)
187
+ if selected_group.nil?
188
+ raise TriggerGroupInputError.new "failed to find group: #{@group}"
189
+ end
190
+ @dependents = [] if @dependents.nil?
191
+ @roots.each do | trigger |
192
+ model = TriggerModel.get(name: trigger, group: @group, scope: @scope)
193
+ if model.nil?
194
+ raise TriggerInputError.new "failed to find dependent trigger: #{trigger}"
195
+ end
196
+ if model.group != @group
197
+ raise TriggerInputError.new "failed group dependent trigger: #{trigger}"
198
+ end
199
+ unless model.dependents.include?(@name)
200
+ model.update_dependents(dependent: @name)
201
+ model.update()
202
+ end
203
+ end
204
+ end
205
+
206
+ def create
207
+ unless Store.hget(@primary_key, @name).nil?
208
+ raise TriggerInputError.new "exsisting Trigger found: #{@name}"
209
+ end
210
+ verify_triggers()
211
+ @updated_at = Time.now.to_nsec_from_epoch
212
+ Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
213
+ notify(kind: 'created')
214
+ end
215
+
216
+ def update
217
+ verify_triggers()
218
+ @updated_at = Time.now.to_nsec_from_epoch
219
+ Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
220
+ notify(kind: 'updated')
221
+ end
222
+
223
+ def enable
224
+ @state = true
225
+ @updated_at = Time.now.to_nsec_from_epoch
226
+ Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
227
+ notify(kind: 'enabled')
228
+ end
229
+
230
+ def disable
231
+ @state = false
232
+ @updated_at = Time.now.to_nsec_from_epoch
233
+ Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
234
+ notify(kind: 'disabled')
235
+ end
236
+
237
+ def activate
238
+ @active = true
239
+ @updated_at = Time.now.to_nsec_from_epoch
240
+ Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
241
+ notify(kind: 'activated')
242
+ end
243
+
244
+ def deactivate
245
+ @active = false
246
+ @state = false
247
+ @updated_at = Time.now.to_nsec_from_epoch
248
+ Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
249
+ notify(kind: 'deactivated')
250
+ end
251
+
252
+ def modify
253
+ raise "TODO"
254
+ end
255
+
256
+ # ["#{@scope}__DECOM__{#{@target}}__#{@packet}"]
257
+ def generate_topics
258
+ topics = Hash.new
259
+ if @left['type'] == ITEM_TYPE
260
+ topics["#{@scope}__DECOM__{#{left['target']}}__#{left['packet']}"] = 1
261
+ end
262
+ if @right['type'] == ITEM_TYPE
263
+ topics["#{@scope}__DECOM__{#{right['target']}}__#{right['packet']}"] = 1
264
+ end
265
+ return topics.keys
266
+ end
267
+
268
+ def update_dependents(dependent:, remove: false)
269
+ if remove
270
+ @dependents.delete(dependent)
271
+ elsif @dependents.index(dependent).nil?
272
+ @dependents << dependent
273
+ end
274
+ end
275
+
276
+ # @return [String] generated from the TriggerModel
277
+ def to_s
278
+ return "(TriggerModel :: #{@name} :: #{group} :: #{@description})"
279
+ end
280
+
281
+ # @return [Hash] generated from the TriggerModel
282
+ def as_json(*a)
283
+ return {
284
+ 'name' => @name,
285
+ 'scope' => @scope,
286
+ 'state' => @state,
287
+ 'active' => @active,
288
+ 'group' => @group,
289
+ 'description' => @description,
290
+ 'dependents' => @dependents,
291
+ 'left' => @left,
292
+ 'operator' => @operator,
293
+ 'right' => @right,
294
+ 'updated_at' => @updated_at,
295
+ }
296
+ end
297
+
298
+ # @return [TriggerModel] Model generated from the passed JSON
299
+ def self.from_json(json, name:, scope:)
300
+ json = JSON.parse(json, :allow_nan => true, :create_additions => true) if String === json
301
+ raise "json data is nil" if json.nil?
302
+
303
+ json.transform_keys!(&:to_sym)
304
+ self.new(**json, name: name, scope: scope)
305
+ end
306
+
307
+ # @return [] update the redis stream / trigger topic that something has changed
308
+ def notify(kind:)
309
+ notification = {
310
+ 'kind' => kind,
311
+ 'type' => 'trigger',
312
+ 'data' => JSON.generate(as_json(:allow_nan => true)),
313
+ }
314
+ AutonomicTopic.write_notification(notification, scope: @scope)
315
+ end
316
+
317
+ # @param [String] kind - the status such as "event" or "error"
318
+ # @param [String] message - an optional message to include in the event
319
+ def log(kind:, message: nil)
320
+ notification = {
321
+ 'kind' => kind,
322
+ 'type' => 'log',
323
+ 'time' => Time.now.to_i,
324
+ 'name' => @name,
325
+ }
326
+ notification['message'] = message unless message.nil?
327
+ AutonomicTopic.write_notification(notification, scope: @scope)
328
+ end
329
+ end
330
+ end
@@ -0,0 +1,138 @@
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/top_level'
21
+ require 'openc3/models/model'
22
+ require 'openc3/models/scope_model'
23
+ require 'openc3/utilities/s3'
24
+
25
+ module OpenC3
26
+ class WidgetModel < Model
27
+ PRIMARY_KEY = 'openc3_widgets'
28
+
29
+ attr_accessor :name
30
+ attr_accessor :full_name
31
+ attr_accessor :filename
32
+ attr_accessor :s3_key
33
+ attr_accessor :needs_dependencies
34
+
35
+ # NOTE: The following three class methods are used by the ModelController
36
+ # and are reimplemented to enable various Model class methods to work
37
+ def self.get(name:, scope: nil)
38
+ super("#{scope}__#{PRIMARY_KEY}", name: name)
39
+ end
40
+
41
+ def self.names(scope: nil)
42
+ array = []
43
+ all(scope: scope).each do |name, _widget|
44
+ array << name
45
+ end
46
+ array
47
+ end
48
+
49
+ def self.all(scope: nil)
50
+ tools = Store.hgetall("#{scope}__#{PRIMARY_KEY}")
51
+ tools.each do |key, value|
52
+ tools[key] = JSON.parse(value, :allow_nan => true, :create_additions => true)
53
+ end
54
+ return tools
55
+ end
56
+
57
+ def self.all_scopes
58
+ result = {}
59
+ scopes = OpenC3::ScopeModel.all
60
+ scopes.each do |key, _scope|
61
+ widgets = all(scope: key)
62
+ result.merge!(widgets)
63
+ end
64
+ result
65
+ end
66
+
67
+ # Called by the PluginModel to allow this class to validate it's top-level keyword: "WIDGET"
68
+ def self.handle_config(parser, keyword, parameters, plugin: nil, needs_dependencies: false, scope:)
69
+ case keyword
70
+ when 'WIDGET'
71
+ parser.verify_num_parameters(1, 1, "WIDGET <Name>")
72
+ return self.new(name: parameters[0], plugin: plugin, needs_dependencies: needs_dependencies, scope: scope)
73
+ else
74
+ raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Widget: #{keyword} #{parameters.join(" ")}")
75
+ end
76
+ return nil
77
+ end
78
+
79
+ def initialize(
80
+ name:,
81
+ updated_at: nil,
82
+ plugin: nil,
83
+ needs_dependencies: false,
84
+ scope:
85
+ )
86
+ super("#{scope}__#{PRIMARY_KEY}", name: name, plugin: plugin, updated_at: updated_at, scope: scope)
87
+ @full_name = @name.capitalize + 'Widget'
88
+ @filename = @full_name + '.umd.min.js'
89
+ @s3_key = 'widgets/' + @full_name + '/' + @filename
90
+ @needs_dependencies = needs_dependencies
91
+ end
92
+
93
+ def as_json(*a)
94
+ {
95
+ 'name' => @name,
96
+ 'updated_at' => @updated_at,
97
+ 'plugin' => @plugin,
98
+ 'needs_dependencies' => @needs_dependencies,
99
+ }
100
+ end
101
+
102
+ def as_config
103
+ result = "WIDGET \"#{@name}\"\n"
104
+ result
105
+ end
106
+
107
+ def handle_config(parser, keyword, parameters)
108
+ raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Widget: #{keyword} #{parameters.join(" ")}")
109
+ end
110
+
111
+ def deploy(gem_path, variables, validate_only: false)
112
+ # Ensure tools bucket exists
113
+ OpenC3::S3Utilities.ensure_public_bucket('tools') unless validate_only
114
+
115
+ filename = gem_path + "/tools/widgets/" + @full_name + '/' + @filename
116
+
117
+ # Load widget file
118
+ data = File.read(filename, mode: "rb")
119
+ OpenC3.set_working_dir(File.dirname(filename)) do
120
+ data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable?
121
+ end
122
+ unless validate_only
123
+ cache_control = OpenC3::S3Utilities.get_cache_control(@filename)
124
+ # TODO: support widgets that aren't just a single js file (and its associated map file)
125
+ rubys3_client = Aws::S3::Client.new
126
+ rubys3_client.put_object(bucket: 'tools', content_type: 'application/javascript', cache_control: cache_control, key: @s3_key, body: data)
127
+ data = File.read(filename + '.map', mode: "rb")
128
+ rubys3_client.put_object(bucket: 'tools', content_type: 'application/json', cache_control: cache_control, key: @s3_key + '.map', body: data)
129
+ end
130
+ end
131
+
132
+ def undeploy
133
+ rubys3_client = Aws::S3::Client.new
134
+ rubys3_client.delete_object(bucket: 'tools', key: @s3_key)
135
+ rubys3_client.delete_object(bucket: 'tools', key: @s3_key + '.map')
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,128 @@
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'
21
+ require 'openc3/models/microservice_model'
22
+ require 'openc3/operators/operator'
23
+ require 'redis'
24
+ require 'open3'
25
+
26
+ module OpenC3
27
+ # Creates new OperatorProcess objects based on querying the Redis key value store.
28
+ # Any keys under 'openc3_microservices' will be created into microservices.
29
+ class MicroserviceOperator < Operator
30
+ def initialize
31
+ Logger.microservice_name = "MicroserviceOperator"
32
+ super
33
+
34
+ @microservices = {}
35
+ @previous_microservices = {}
36
+ @new_microservices = {}
37
+ @changed_microservices = {}
38
+ @removed_microservices = {}
39
+ end
40
+
41
+ def convert_microservice_to_process_definition(microservice_name, microservice_config)
42
+ process_definition = ["ruby", "plugin_microservice.rb"]
43
+ work_dir = "/openc3/lib/openc3/microservices"
44
+ env = microservice_config["env"].dup
45
+ if microservice_config["needs_dependencies"]
46
+ env['GEM_HOME'] = '/gems'
47
+ else
48
+ env['GEM_HOME'] = nil
49
+ end
50
+ env['OPENC3_MICROSERVICE_NAME'] = microservice_name
51
+ container = microservice_config["container"]
52
+ scope = microservice_name.split("__")[0]
53
+ return process_definition, work_dir, env, scope, container
54
+ end
55
+
56
+ def update
57
+ @previous_microservices = @microservices.dup
58
+ # Get all the microservice configuration
59
+ @microservices = MicroserviceModel.all
60
+
61
+ # Detect new and changed microservices
62
+ @new_microservices = {}
63
+ @changed_microservices = {}
64
+ @microservices.each do |microservice_name, microservice_config|
65
+ if @previous_microservices[microservice_name]
66
+ if @previous_microservices[microservice_name] != microservice_config
67
+ scope = microservice_name.split("__")[0]
68
+ Logger.info("Changed microservice detected: #{microservice_name}\nWas: #{@previous_microservices[microservice_name]}\nIs: #{microservice_config}", scope: scope)
69
+ @changed_microservices[microservice_name] = microservice_config
70
+ end
71
+ else
72
+ scope = microservice_name.split("__")[0]
73
+ Logger.info("New microservice detected: #{microservice_name}", scope: scope)
74
+ @new_microservices[microservice_name] = microservice_config
75
+ end
76
+ end
77
+
78
+ # Detect removed microservices
79
+ @removed_microservices = {}
80
+ @previous_microservices.each do |microservice_name, microservice_config|
81
+ unless @microservices[microservice_name]
82
+ scope = microservice_name.split("__")[0]
83
+ Logger.info("Removed microservice detected: #{microservice_name}", scope: scope)
84
+ @removed_microservices[microservice_name] = microservice_config
85
+ end
86
+ end
87
+
88
+ # Convert to processes
89
+ @mutex.synchronize do
90
+ @new_microservices.each do |microservice_name, microservice_config|
91
+ cmd_array, work_dir, env, scope, container = convert_microservice_to_process_definition(microservice_name, microservice_config)
92
+ if cmd_array
93
+ process = OperatorProcess.new(cmd_array, work_dir: work_dir, env: env, scope: scope, container: container)
94
+ @new_processes[microservice_name] = process
95
+ @processes[microservice_name] = process
96
+ end
97
+ end
98
+
99
+ @changed_microservices.each do |microservice_name, microservice_config|
100
+ cmd_array, work_dir, env, scope, container = convert_microservice_to_process_definition(microservice_name, microservice_config)
101
+ if cmd_array
102
+ process = @processes[microservice_name]
103
+ if process
104
+ process.process_definition = cmd_array
105
+ process.work_dir = work_dir
106
+ process.new_temp_dir = nil
107
+ process.env = env
108
+ @changed_processes[microservice_name] = process
109
+ else # TODO: How is this even possible?
110
+ Logger.error("Changed microservice #{microservice_name} does not exist. Creating new...", scope: scope)
111
+ process = OperatorProcess.new(cmd_array, work_dir: work_dir, env: env, scope: scope, container: container)
112
+ @new_processes[microservice_name] = process
113
+ @processes[microservice_name] = process
114
+ end
115
+ end
116
+ end
117
+
118
+ @removed_microservices.each do |microservice_name, microservice_config|
119
+ process = @processes[microservice_name]
120
+ @processes.delete(microservice_name)
121
+ @removed_processes[microservice_name] = process
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ OpenC3::MicroserviceOperator.run if __FILE__ == $0