openc3 7.2.1 → 7.4.0

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 (502) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Guardfile +0 -0
  4. data/LICENSE.md +0 -0
  5. data/README.md +0 -0
  6. data/Rakefile +0 -0
  7. data/bin/openc3cli +235 -6
  8. data/bin/pipinstall +5 -4
  9. data/bin/pipuninstall +5 -1
  10. data/bin/uvinstall +156 -0
  11. data/data/config/_array_params.yaml +0 -0
  12. data/data/config/_canvas_values.yaml +0 -0
  13. data/data/config/_graph_params.yaml +0 -0
  14. data/data/config/_id_items.yaml +0 -0
  15. data/data/config/_id_params.yaml +0 -0
  16. data/data/config/_interfaces.yaml +0 -0
  17. data/data/config/_items.yaml +0 -0
  18. data/data/config/_params.yaml +0 -0
  19. data/data/config/command.yaml +0 -0
  20. data/data/config/command_modifiers.yaml +2 -0
  21. data/data/config/command_telemetry.yaml +0 -0
  22. data/data/config/conversions.yaml +0 -0
  23. data/data/config/graph_settings.yaml +0 -0
  24. data/data/config/interface_modifiers.yaml +103 -1
  25. data/data/config/item_modifiers.yaml +2 -1
  26. data/data/config/microservice.yaml +9 -2
  27. data/data/config/param_item_modifiers.yaml +0 -0
  28. data/data/config/parameter_modifiers.yaml +0 -0
  29. data/data/config/plugins.yaml +0 -0
  30. data/data/config/processors.yaml +0 -0
  31. data/data/config/protocols.yaml +0 -0
  32. data/data/config/screen.yaml +2 -1
  33. data/data/config/settings.yaml +0 -0
  34. data/data/config/table_manager.yaml +0 -0
  35. data/data/config/table_parameter_modifiers.yaml +0 -0
  36. data/data/config/target.yaml +21 -0
  37. data/data/config/target_config.yaml +0 -0
  38. data/data/config/telemetry.yaml +0 -0
  39. data/data/config/telemetry_modifiers.yaml +2 -0
  40. data/data/config/tool.yaml +3 -1
  41. data/data/config/unknown.yaml +0 -0
  42. data/data/config/widgets.yaml +22 -3
  43. data/data/xtce_schemas/SpaceSystem_20180204.xsd +5918 -0
  44. data/data/xtce_schemas/xml.xsd +116 -0
  45. data/ext/openc3/ext/array/array.c +0 -0
  46. data/ext/openc3/ext/array/extconf.rb +0 -0
  47. data/ext/openc3/ext/buffered_file/buffered_file.c +0 -0
  48. data/ext/openc3/ext/buffered_file/extconf.rb +0 -0
  49. data/ext/openc3/ext/burst_protocol/burst_protocol.c +6 -0
  50. data/ext/openc3/ext/burst_protocol/extconf.rb +0 -0
  51. data/ext/openc3/ext/config_parser/config_parser.c +0 -0
  52. data/ext/openc3/ext/config_parser/extconf.rb +0 -0
  53. data/ext/openc3/ext/crc/crc.c +0 -0
  54. data/ext/openc3/ext/crc/extconf.rb +0 -0
  55. data/ext/openc3/ext/openc3_io/extconf.rb +0 -0
  56. data/ext/openc3/ext/openc3_io/openc3_io.c +0 -0
  57. data/ext/openc3/ext/packet/extconf.rb +0 -0
  58. data/ext/openc3/ext/packet/packet.c +0 -0
  59. data/ext/openc3/ext/platform/extconf.rb +0 -0
  60. data/ext/openc3/ext/platform/platform.c +4 -1
  61. data/ext/openc3/ext/polynomial_conversion/extconf.rb +0 -0
  62. data/ext/openc3/ext/polynomial_conversion/polynomial_conversion.c +0 -0
  63. data/ext/openc3/ext/string/extconf.rb +0 -0
  64. data/ext/openc3/ext/string/string.c +0 -0
  65. data/ext/openc3/ext/structure/structure.c +49 -17
  66. data/ext/openc3/ext/tabbed_plots_config/extconf.rb +0 -0
  67. data/ext/openc3/ext/tabbed_plots_config/tabbed_plots_config.c +0 -0
  68. data/ext/openc3/ext/telemetry/extconf.rb +0 -0
  69. data/ext/openc3/ext/telemetry/telemetry.c +1 -1
  70. data/lib/cosmos.rb +0 -0
  71. data/lib/cosmosc2.rb +0 -0
  72. data/lib/openc3/accessors/accessor.rb +0 -0
  73. data/lib/openc3/accessors/binary_accessor.rb +63 -9
  74. data/lib/openc3/accessors/cbor_accessor.rb +0 -0
  75. data/lib/openc3/accessors/form_accessor.rb +0 -0
  76. data/lib/openc3/accessors/html_accessor.rb +0 -0
  77. data/lib/openc3/accessors/http_accessor.rb +0 -0
  78. data/lib/openc3/accessors/json_accessor.rb +0 -0
  79. data/lib/openc3/accessors/template_accessor.rb +2 -2
  80. data/lib/openc3/accessors/xml_accessor.rb +0 -0
  81. data/lib/openc3/accessors.rb +0 -0
  82. data/lib/openc3/api/README.md +0 -0
  83. data/lib/openc3/api/api.rb +0 -0
  84. data/lib/openc3/api/authorized_api.rb +0 -0
  85. data/lib/openc3/api/calendar_api.rb +0 -0
  86. data/lib/openc3/api/cmd_api.rb +1 -1
  87. data/lib/openc3/api/config_api.rb +0 -0
  88. data/lib/openc3/api/interface_api.rb +2 -2
  89. data/lib/openc3/api/limits_api.rb +27 -13
  90. data/lib/openc3/api/metrics_api.rb +1 -1
  91. data/lib/openc3/api/offline_access_api.rb +1 -1
  92. data/lib/openc3/api/router_api.rb +3 -3
  93. data/lib/openc3/api/settings_api.rb +22 -18
  94. data/lib/openc3/api/stash_api.rb +1 -1
  95. data/lib/openc3/api/target_api.rb +0 -0
  96. data/lib/openc3/api/tlm_api.rb +5 -4
  97. data/lib/openc3/bridge/bridge.rb +0 -0
  98. data/lib/openc3/bridge/bridge_config.rb +0 -0
  99. data/lib/openc3/bridge/bridge_interface_thread.rb +0 -0
  100. data/lib/openc3/bridge/bridge_router_thread.rb +0 -0
  101. data/lib/openc3/ccsds/ccsds_packet.rb +0 -0
  102. data/lib/openc3/ccsds/ccsds_parser.rb +0 -0
  103. data/lib/openc3/config/config_parser.rb +78 -17
  104. data/lib/openc3/config/meta_config_parser.rb +0 -0
  105. data/lib/openc3/conversions/bit_reverse_conversion.rb +0 -0
  106. data/lib/openc3/conversions/conversion.rb +0 -0
  107. data/lib/openc3/conversions/generic_conversion.rb +0 -0
  108. data/lib/openc3/conversions/ip_read_conversion.rb +0 -0
  109. data/lib/openc3/conversions/ip_write_conversion.rb +0 -0
  110. data/lib/openc3/conversions/object_read_conversion.rb +0 -0
  111. data/lib/openc3/conversions/object_write_conversion.rb +0 -0
  112. data/lib/openc3/conversions/packet_time_formatted_conversion.rb +0 -0
  113. data/lib/openc3/conversions/packet_time_seconds_conversion.rb +0 -0
  114. data/lib/openc3/conversions/polynomial_conversion.rb +0 -0
  115. data/lib/openc3/conversions/processor_conversion.rb +0 -0
  116. data/lib/openc3/conversions/received_count_conversion.rb +0 -0
  117. data/lib/openc3/conversions/received_time_formatted_conversion.rb +0 -0
  118. data/lib/openc3/conversions/received_time_seconds_conversion.rb +0 -0
  119. data/lib/openc3/conversions/segmented_polynomial_conversion.rb +0 -0
  120. data/lib/openc3/conversions/unix_time_conversion.rb +0 -0
  121. data/lib/openc3/conversions/unix_time_formatted_conversion.rb +0 -0
  122. data/lib/openc3/conversions/unix_time_seconds_conversion.rb +0 -0
  123. data/lib/openc3/conversions.rb +0 -0
  124. data/lib/openc3/core_ext/array.rb +0 -0
  125. data/lib/openc3/core_ext/binding.rb +0 -0
  126. data/lib/openc3/core_ext/class.rb +0 -0
  127. data/lib/openc3/core_ext/exception.rb +0 -0
  128. data/lib/openc3/core_ext/faraday.rb +0 -0
  129. data/lib/openc3/core_ext/file.rb +0 -0
  130. data/lib/openc3/core_ext/io.rb +0 -0
  131. data/lib/openc3/core_ext/kernel.rb +0 -0
  132. data/lib/openc3/core_ext/math.rb +0 -0
  133. data/lib/openc3/core_ext/matrix.rb +0 -0
  134. data/lib/openc3/core_ext/objectspace.rb +0 -0
  135. data/lib/openc3/core_ext/openc3_io.rb +0 -0
  136. data/lib/openc3/core_ext/range.rb +0 -0
  137. data/lib/openc3/core_ext/socket.rb +0 -0
  138. data/lib/openc3/core_ext/string.rb +0 -0
  139. data/lib/openc3/core_ext/stringio.rb +0 -0
  140. data/lib/openc3/core_ext/tempfile.rb +0 -0
  141. data/lib/openc3/core_ext/time.rb +0 -0
  142. data/lib/openc3/core_ext.rb +0 -0
  143. data/lib/openc3/ext/.keep +0 -0
  144. data/lib/openc3/interfaces/file_interface.rb +0 -0
  145. data/lib/openc3/interfaces/http_client_interface.rb +1 -1
  146. data/lib/openc3/interfaces/http_server_interface.rb +0 -0
  147. data/lib/openc3/interfaces/interface.rb +5 -4
  148. data/lib/openc3/interfaces/mqtt_interface.rb +0 -0
  149. data/lib/openc3/interfaces/mqtt_stream_interface.rb +2 -0
  150. data/lib/openc3/interfaces/protocols/burst_protocol.rb +27 -0
  151. data/lib/openc3/interfaces/protocols/cmd_response_protocol.rb +0 -0
  152. data/lib/openc3/interfaces/protocols/cobs_protocol.rb +0 -0
  153. data/lib/openc3/interfaces/protocols/crc_protocol.rb +0 -0
  154. data/lib/openc3/interfaces/protocols/fixed_protocol.rb +0 -0
  155. data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +0 -0
  156. data/lib/openc3/interfaces/protocols/length_protocol.rb +7 -0
  157. data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +7 -0
  158. data/lib/openc3/interfaces/protocols/protocol.rb +4 -4
  159. data/lib/openc3/interfaces/protocols/slip_protocol.rb +0 -0
  160. data/lib/openc3/interfaces/protocols/template_protocol.rb +2 -2
  161. data/lib/openc3/interfaces/protocols/terminated_protocol.rb +0 -0
  162. data/lib/openc3/interfaces/serial_interface.rb +2 -0
  163. data/lib/openc3/interfaces/simulated_target_interface.rb +0 -0
  164. data/lib/openc3/interfaces/stream_interface.rb +0 -0
  165. data/lib/openc3/interfaces/tcpip_client_interface.rb +2 -0
  166. data/lib/openc3/interfaces/tcpip_server_interface.rb +0 -0
  167. data/lib/openc3/interfaces/udp_interface.rb +16 -5
  168. data/lib/openc3/interfaces.rb +0 -0
  169. data/lib/openc3/io/buffered_file.rb +0 -0
  170. data/lib/openc3/io/io_multiplexer.rb +0 -0
  171. data/lib/openc3/io/json_api.rb +3 -2
  172. data/lib/openc3/io/json_api_object.rb +0 -0
  173. data/lib/openc3/io/json_drb.rb +44 -12
  174. data/lib/openc3/io/json_drb_object.rb +0 -0
  175. data/lib/openc3/io/json_drb_rack.rb +0 -0
  176. data/lib/openc3/io/json_rpc.rb +0 -2
  177. data/lib/openc3/io/posix_serial_driver.rb +0 -0
  178. data/lib/openc3/io/serial_driver.rb +0 -0
  179. data/lib/openc3/io/stderr.rb +0 -0
  180. data/lib/openc3/io/stdout.rb +0 -0
  181. data/lib/openc3/io/udp_sockets.rb +38 -8
  182. data/lib/openc3/io/win32_serial_driver.rb +1 -0
  183. data/lib/openc3/logs/buffered_packet_log_reader.rb +0 -0
  184. data/lib/openc3/logs/buffered_packet_log_writer.rb +0 -0
  185. data/lib/openc3/logs/log_writer.rb +43 -7
  186. data/lib/openc3/logs/packet_log_constants.rb +0 -0
  187. data/lib/openc3/logs/packet_log_reader.rb +23 -2
  188. data/lib/openc3/logs/packet_log_writer.rb +6 -2
  189. data/lib/openc3/logs/stream_log.rb +1 -1
  190. data/lib/openc3/logs/stream_log_pair.rb +0 -0
  191. data/lib/openc3/logs/text_log_writer.rb +0 -0
  192. data/lib/openc3/logs.rb +0 -0
  193. data/lib/openc3/microservices/cleanup_microservice.rb +0 -0
  194. data/lib/openc3/microservices/decom_common.rb +0 -0
  195. data/lib/openc3/microservices/decom_microservice.rb +0 -0
  196. data/lib/openc3/microservices/interface_decom_common.rb +2 -1
  197. data/lib/openc3/microservices/interface_microservice.rb +73 -26
  198. data/lib/openc3/microservices/log_microservice.rb +0 -0
  199. data/lib/openc3/microservices/microservice.rb +3 -3
  200. data/lib/openc3/microservices/multi_microservice.rb +1 -1
  201. data/lib/openc3/microservices/periodic_microservice.rb +2 -2
  202. data/lib/openc3/microservices/plugin_microservice.rb +0 -0
  203. data/lib/openc3/microservices/queue_microservice.rb +0 -0
  204. data/lib/openc3/microservices/router_microservice.rb +0 -0
  205. data/lib/openc3/microservices/scope_cleanup_microservice.rb +0 -0
  206. data/lib/openc3/microservices/text_log_microservice.rb +0 -0
  207. data/lib/openc3/migrations/20220420190000_log_stuff.rb +0 -0
  208. data/lib/openc3/migrations/20221202214600_add_target_names.rb +0 -0
  209. data/lib/openc3/migrations/20221210174900_convert_to_multi.rb +0 -0
  210. data/lib/openc3/migrations/20230915000002_no_scope_log_messages.rb +0 -0
  211. data/lib/openc3/migrations/20231022000000_tlm_viewer_config.rb +0 -0
  212. data/lib/openc3/migrations/20241208080000_no_critical_cmd.rb +0 -0
  213. data/lib/openc3/migrations/20241208080001_no_trigger_group.rb +0 -0
  214. data/lib/openc3/migrations/20250108060000_news_feed.rb +0 -0
  215. data/lib/openc3/migrations/20250402000000_periodic_only_default.rb +0 -0
  216. data/lib/openc3/migrations/20260203000000_remove_store_id.rb +0 -0
  217. data/lib/openc3/migrations/20260204000000_remove_decom_reducer.rb +0 -0
  218. data/lib/openc3/migrations/20260701000000_updated_at_to_int.rb +0 -0
  219. data/lib/openc3/migrations/20260704000000_default_bridge.rb +38 -0
  220. data/lib/openc3/migrations/20260731000000_system_health_json.rb +52 -0
  221. data/lib/openc3/models/activity_model.rb +0 -0
  222. data/lib/openc3/models/auth_model.rb +35 -7
  223. data/lib/openc3/models/bridge_model.rb +133 -0
  224. data/lib/openc3/models/config_keyword_model.rb +63 -0
  225. data/lib/openc3/models/cvt_model.rb +5 -5
  226. data/lib/openc3/models/db_sharded_model.rb +0 -0
  227. data/lib/openc3/models/environment_model.rb +0 -0
  228. data/lib/openc3/models/gem_model.rb +13 -10
  229. data/lib/openc3/models/host_interface_microservice_model.rb +114 -0
  230. data/lib/openc3/models/info_model.rb +0 -0
  231. data/lib/openc3/models/interface_model.rb +179 -20
  232. data/lib/openc3/models/interface_status_model.rb +0 -0
  233. data/lib/openc3/models/metadata_model.rb +0 -0
  234. data/lib/openc3/models/metric_model.rb +0 -0
  235. data/lib/openc3/models/microservice_model.rb +26 -18
  236. data/lib/openc3/models/microservice_status_model.rb +0 -0
  237. data/lib/openc3/models/migration_model.rb +0 -0
  238. data/lib/openc3/models/model.rb +0 -0
  239. data/lib/openc3/models/news_model.rb +0 -0
  240. data/lib/openc3/models/note_model.rb +0 -0
  241. data/lib/openc3/models/offline_access_model.rb +0 -0
  242. data/lib/openc3/models/ping_model.rb +0 -0
  243. data/lib/openc3/models/plugin_model.rb +211 -50
  244. data/lib/openc3/models/plugin_store_model.rb +0 -0
  245. data/lib/openc3/models/process_status_model.rb +0 -0
  246. data/lib/openc3/models/python_package_model.rb +211 -15
  247. data/lib/openc3/models/queue_model.rb +0 -0
  248. data/lib/openc3/models/reaction_model.rb +0 -0
  249. data/lib/openc3/models/reingest_job_model.rb +0 -0
  250. data/lib/openc3/models/router_model.rb +0 -0
  251. data/lib/openc3/models/router_status_model.rb +0 -0
  252. data/lib/openc3/models/scope_model.rb +85 -56
  253. data/lib/openc3/models/script_engine_model.rb +0 -0
  254. data/lib/openc3/models/script_status_model.rb +0 -0
  255. data/lib/openc3/models/secret_model.rb +0 -0
  256. data/lib/openc3/models/setting_model.rb +546 -3
  257. data/lib/openc3/models/sorted_model.rb +0 -0
  258. data/lib/openc3/models/stash_model.rb +0 -0
  259. data/lib/openc3/models/target_model.rb +210 -31
  260. data/lib/openc3/models/timeline_model.rb +0 -0
  261. data/lib/openc3/models/tool_config_model.rb +0 -0
  262. data/lib/openc3/models/tool_model.rb +34 -20
  263. data/lib/openc3/models/trigger_group_model.rb +0 -0
  264. data/lib/openc3/models/trigger_model.rb +20 -0
  265. data/lib/openc3/models/widget_model.rb +0 -0
  266. data/lib/openc3/operators/microservice_operator.rb +79 -8
  267. data/lib/openc3/operators/operator.rb +4 -4
  268. data/lib/openc3/packets/binary_accessor.rb +0 -0
  269. data/lib/openc3/packets/command_validator.rb +0 -0
  270. data/lib/openc3/packets/commands.rb +4 -4
  271. data/lib/openc3/packets/json_packet.rb +0 -0
  272. data/lib/openc3/packets/limits.rb +15 -7
  273. data/lib/openc3/packets/limits_response.rb +0 -0
  274. data/lib/openc3/packets/packet.rb +5 -1
  275. data/lib/openc3/packets/packet_config.rb +0 -0
  276. data/lib/openc3/packets/packet_item.rb +0 -0
  277. data/lib/openc3/packets/packet_item_limits.rb +0 -0
  278. data/lib/openc3/packets/parsers/format_string_parser.rb +0 -0
  279. data/lib/openc3/packets/parsers/limits_parser.rb +0 -0
  280. data/lib/openc3/packets/parsers/limits_response_parser.rb +0 -0
  281. data/lib/openc3/packets/parsers/packet_item_parser.rb +0 -0
  282. data/lib/openc3/packets/parsers/packet_parser.rb +0 -0
  283. data/lib/openc3/packets/parsers/processor_parser.rb +0 -0
  284. data/lib/openc3/packets/parsers/state_parser.rb +1 -1
  285. data/lib/openc3/packets/parsers/xtce_converter.rb +183 -103
  286. data/lib/openc3/packets/parsers/xtce_parser.rb +71 -23
  287. data/lib/openc3/packets/structure.rb +9 -3
  288. data/lib/openc3/packets/structure_item.rb +0 -0
  289. data/lib/openc3/packets/telemetry.rb +3 -3
  290. data/lib/openc3/processors/processor.rb +2 -0
  291. data/lib/openc3/processors/statistics_processor.rb +0 -0
  292. data/lib/openc3/processors/watermark_processor.rb +0 -0
  293. data/lib/openc3/processors.rb +0 -0
  294. data/lib/openc3/script/api_shared.rb +0 -0
  295. data/lib/openc3/script/autonomic.rb +12 -12
  296. data/lib/openc3/script/calendar.rb +0 -0
  297. data/lib/openc3/script/commands.rb +1 -1
  298. data/lib/openc3/script/critical_cmd.rb +0 -0
  299. data/lib/openc3/script/exceptions.rb +0 -0
  300. data/lib/openc3/script/extract.rb +20 -25
  301. data/lib/openc3/script/limits.rb +0 -0
  302. data/lib/openc3/script/metadata.rb +0 -0
  303. data/lib/openc3/script/packages.rb +0 -0
  304. data/lib/openc3/script/plugins.rb +0 -0
  305. data/lib/openc3/script/queue.rb +0 -0
  306. data/lib/openc3/script/screen.rb +0 -0
  307. data/lib/openc3/script/script.rb +2 -2
  308. data/lib/openc3/script/script_runner.rb +0 -0
  309. data/lib/openc3/script/storage.rb +4 -4
  310. data/lib/openc3/script/suite.rb +0 -0
  311. data/lib/openc3/script/suite_results.rb +0 -0
  312. data/lib/openc3/script/suite_runner.rb +26 -0
  313. data/lib/openc3/script/tables.rb +0 -0
  314. data/lib/openc3/script/telemetry.rb +9 -3
  315. data/lib/openc3/script/web_socket_api.rb +247 -134
  316. data/lib/openc3/script.rb +0 -0
  317. data/lib/openc3/script_engines/script_engine.rb +0 -0
  318. data/lib/openc3/streams/mqtt_stream.rb +0 -0
  319. data/lib/openc3/streams/serial_stream.rb +0 -0
  320. data/lib/openc3/streams/stream.rb +0 -0
  321. data/lib/openc3/streams/tcpip_client_stream.rb +0 -0
  322. data/lib/openc3/streams/tcpip_socket_stream.rb +0 -0
  323. data/lib/openc3/streams/web_socket_client_stream.rb +0 -0
  324. data/lib/openc3/subpacketizers/subpacketizer.rb +0 -0
  325. data/lib/openc3/system/system.rb +0 -0
  326. data/lib/openc3/system/target.rb +0 -0
  327. data/lib/openc3/system.rb +0 -0
  328. data/lib/openc3/tools/cmd_tlm_server/api.rb +0 -0
  329. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +7 -0
  330. data/lib/openc3/tools/table_manager/table.rb +0 -0
  331. data/lib/openc3/tools/table_manager/table_config.rb +0 -0
  332. data/lib/openc3/tools/table_manager/table_item.rb +0 -0
  333. data/lib/openc3/tools/table_manager/table_item_parser.rb +0 -0
  334. data/lib/openc3/tools/table_manager/table_manager_core.rb +0 -0
  335. data/lib/openc3/tools/table_manager/table_parser.rb +0 -0
  336. data/lib/openc3/tools/test_runner/test.rb +0 -0
  337. data/lib/openc3/top_level.rb +1 -1
  338. data/lib/openc3/topics/autonomic_topic.rb +0 -0
  339. data/lib/openc3/topics/calendar_topic.rb +0 -0
  340. data/lib/openc3/topics/command_decom_topic.rb +16 -1
  341. data/lib/openc3/topics/command_topic.rb +0 -0
  342. data/lib/openc3/topics/config_topic.rb +0 -0
  343. data/lib/openc3/topics/decom_interface_topic.rb +2 -1
  344. data/lib/openc3/topics/interface_topic.rb +8 -2
  345. data/lib/openc3/topics/limits_event_topic.rb +1 -1
  346. data/lib/openc3/topics/notebook_topic.rb +0 -0
  347. data/lib/openc3/topics/queue_topic.rb +0 -0
  348. data/lib/openc3/topics/router_topic.rb +6 -1
  349. data/lib/openc3/topics/system_events_topic.rb +0 -0
  350. data/lib/openc3/topics/telemetry_decom_topic.rb +0 -0
  351. data/lib/openc3/topics/telemetry_topic.rb +0 -0
  352. data/lib/openc3/topics/timeline_topic.rb +0 -0
  353. data/lib/openc3/topics/topic.rb +0 -0
  354. data/lib/openc3/utilities/authentication.rb +4 -4
  355. data/lib/openc3/utilities/authorization.rb +0 -0
  356. data/lib/openc3/utilities/aws_bucket.rb +12 -4
  357. data/lib/openc3/utilities/bucket.rb +1 -1
  358. data/lib/openc3/utilities/bucket_file_cache.rb +55 -22
  359. data/lib/openc3/utilities/bucket_require.rb +0 -0
  360. data/lib/openc3/utilities/bucket_utilities.rb +3 -4
  361. data/lib/openc3/utilities/cli_generator.rb +0 -0
  362. data/lib/openc3/utilities/cmd_log.rb +0 -0
  363. data/lib/openc3/utilities/config_overlay.rb +75 -0
  364. data/lib/openc3/utilities/cosmos_rails_formatter.rb +0 -0
  365. data/lib/openc3/utilities/crc.rb +59 -32
  366. data/lib/openc3/utilities/csv.rb +5 -5
  367. data/lib/openc3/utilities/ctrf.rb +0 -0
  368. data/lib/openc3/utilities/env_helper.rb +0 -0
  369. data/lib/openc3/utilities/local_bucket.rb +0 -0
  370. data/lib/openc3/utilities/local_mode.rb +36 -7
  371. data/lib/openc3/utilities/logger.rb +30 -8
  372. data/lib/openc3/utilities/message_log.rb +0 -0
  373. data/lib/openc3/utilities/metric.rb +0 -0
  374. data/lib/openc3/utilities/migration.rb +0 -0
  375. data/lib/openc3/utilities/open_telemetry.rb +0 -0
  376. data/lib/openc3/utilities/process_manager.rb +0 -0
  377. data/lib/openc3/utilities/pypi_url.rb +0 -0
  378. data/lib/openc3/utilities/python_proxy.rb +0 -0
  379. data/lib/openc3/utilities/python_venv.rb +135 -0
  380. data/lib/openc3/utilities/quaternion.rb +0 -0
  381. data/lib/openc3/utilities/questdb_client.rb +0 -0
  382. data/lib/openc3/utilities/redis_secrets.rb +0 -0
  383. data/lib/openc3/utilities/reingest_job.rb +0 -0
  384. data/lib/openc3/utilities/ruby_lex_utils.rb +5 -1
  385. data/lib/openc3/utilities/rubygems_url.rb +39 -0
  386. data/lib/openc3/utilities/running_script.rb +119 -95
  387. data/lib/openc3/utilities/s3_autoload.rb +5 -5
  388. data/lib/openc3/utilities/script.rb +52 -12
  389. data/lib/openc3/utilities/secrets.rb +48 -3
  390. data/lib/openc3/utilities/simulated_target.rb +0 -0
  391. data/lib/openc3/utilities/sleeper.rb +0 -0
  392. data/lib/openc3/utilities/store.rb +0 -0
  393. data/lib/openc3/utilities/store_autoload.rb +2 -2
  394. data/lib/openc3/utilities/store_queued.rb +0 -0
  395. data/lib/openc3/utilities/target_file.rb +18 -1
  396. data/lib/openc3/utilities/thread_manager.rb +0 -0
  397. data/lib/openc3/utilities/throttle.rb +0 -0
  398. data/lib/openc3/utilities/zip.rb +0 -0
  399. data/lib/openc3/utilities.rb +0 -0
  400. data/lib/openc3/version.rb +6 -6
  401. data/lib/openc3/win32/excel.rb +0 -0
  402. data/lib/openc3/win32/win32.rb +0 -0
  403. data/lib/openc3/win32/win32_main.rb +0 -0
  404. data/lib/openc3.rb +2 -2
  405. data/tasks/gemfile_stats.rake +0 -0
  406. data/tasks/spec.rake +0 -0
  407. data/templates/command_validator/command_validator.py +0 -0
  408. data/templates/command_validator/command_validator.rb +0 -0
  409. data/templates/conversion/conversion.py +0 -0
  410. data/templates/conversion/conversion.rb +0 -0
  411. data/templates/limits_response/response.py +0 -0
  412. data/templates/limits_response/response.rb +0 -0
  413. data/templates/microservice/microservices/TEMPLATE/microservice.py +0 -0
  414. data/templates/microservice/microservices/TEMPLATE/microservice.rb +0 -0
  415. data/templates/plugin/.gitignore +0 -0
  416. data/templates/plugin/LICENSE.md +0 -0
  417. data/templates/plugin/README.md +0 -0
  418. data/templates/plugin/Rakefile +0 -0
  419. data/templates/plugin/plugin.gemspec +1 -1
  420. data/templates/plugin/plugin.txt +0 -0
  421. data/templates/processor/processor.py +1 -1
  422. data/templates/processor/processor.rb +0 -0
  423. data/templates/target/targets/TARGET/cmd_tlm/cmd.txt +0 -0
  424. data/templates/target/targets/TARGET/cmd_tlm/tlm.txt +0 -0
  425. data/templates/target/targets/TARGET/lib/target.py +0 -0
  426. data/templates/target/targets/TARGET/lib/target.rb +0 -0
  427. data/templates/target/targets/TARGET/procedures/procedure.py +0 -0
  428. data/templates/target/targets/TARGET/procedures/procedure.rb +0 -0
  429. data/templates/target/targets/TARGET/public/README.txt +0 -0
  430. data/templates/target/targets/TARGET/screens/status.txt +0 -0
  431. data/templates/target/targets/TARGET/target.txt +0 -0
  432. data/templates/tool_angular/.editorconfig +0 -0
  433. data/templates/tool_angular/.gitignore +0 -0
  434. data/templates/tool_angular/.vscode/extensions.json +0 -0
  435. data/templates/tool_angular/.vscode/launch.json +0 -0
  436. data/templates/tool_angular/.vscode/tasks.json +0 -0
  437. data/templates/tool_angular/angular.json +0 -0
  438. data/templates/tool_angular/extra-webpack.config.js +0 -0
  439. data/templates/tool_angular/package.json +2 -2
  440. data/templates/tool_angular/src/app/app-routing.module.ts +0 -0
  441. data/templates/tool_angular/src/app/app.component.html +0 -0
  442. data/templates/tool_angular/src/app/app.component.scss +0 -0
  443. data/templates/tool_angular/src/app/app.component.spec.ts +0 -0
  444. data/templates/tool_angular/src/app/app.component.ts +0 -0
  445. data/templates/tool_angular/src/app/app.module.ts +0 -0
  446. data/templates/tool_angular/src/app/custom-overlay-container.ts +0 -0
  447. data/templates/tool_angular/src/app/empty-route/empty-route.component.ts +0 -0
  448. data/templates/tool_angular/src/app/openc3-api.d.ts +0 -0
  449. data/templates/tool_angular/src/assets/.gitkeep +0 -0
  450. data/templates/tool_angular/src/environments/environment.prod.ts +0 -0
  451. data/templates/tool_angular/src/environments/environment.ts +0 -0
  452. data/templates/tool_angular/src/favicon.ico +0 -0
  453. data/templates/tool_angular/src/index.html +0 -0
  454. data/templates/tool_angular/src/main.single-spa.ts +0 -0
  455. data/templates/tool_angular/src/single-spa/asset-url.ts +0 -0
  456. data/templates/tool_angular/src/single-spa/single-spa-props.ts +0 -0
  457. data/templates/tool_angular/src/styles.scss +0 -0
  458. data/templates/tool_angular/tsconfig.app.json +0 -0
  459. data/templates/tool_angular/tsconfig.json +0 -0
  460. data/templates/tool_angular/tsconfig.spec.json +0 -0
  461. data/templates/tool_react/.eslintrc +0 -0
  462. data/templates/tool_react/.gitignore +0 -0
  463. data/templates/tool_react/.prettierignore +0 -0
  464. data/templates/tool_react/babel.config.json +0 -0
  465. data/templates/tool_react/jest.config.js +0 -0
  466. data/templates/tool_react/package.json +1 -1
  467. data/templates/tool_react/src/openc3-tool_name.js +0 -0
  468. data/templates/tool_react/src/root.component.js +0 -0
  469. data/templates/tool_react/src/root.component.test.js +0 -0
  470. data/templates/tool_react/webpack.config.js +0 -0
  471. data/templates/tool_svelte/.gitignore +0 -0
  472. data/templates/tool_svelte/.prettierignore +0 -0
  473. data/templates/tool_svelte/.prettierrc.js +0 -0
  474. data/templates/tool_svelte/babel.config.js +0 -0
  475. data/templates/tool_svelte/build/smui.css +0 -0
  476. data/templates/tool_svelte/jest.config.js +0 -0
  477. data/templates/tool_svelte/package.json +1 -1
  478. data/templates/tool_svelte/rollup.config.js +0 -0
  479. data/templates/tool_svelte/src/App.svelte +0 -0
  480. data/templates/tool_svelte/src/App.test.js +0 -0
  481. data/templates/tool_svelte/src/theme/_smui-theme.scss +0 -0
  482. data/templates/tool_svelte/src/tool_name.js +0 -0
  483. data/templates/tool_vue/.env.standalone +0 -0
  484. data/templates/tool_vue/.gitignore +0 -0
  485. data/templates/tool_vue/.prettierrc.cjs +0 -0
  486. data/templates/tool_vue/eslint.config.mjs +0 -0
  487. data/templates/tool_vue/jsconfig.json +0 -0
  488. data/templates/tool_vue/package.json +3 -3
  489. data/templates/tool_vue/src/App.vue +0 -0
  490. data/templates/tool_vue/src/main.js +0 -0
  491. data/templates/tool_vue/src/router.js +0 -0
  492. data/templates/tool_vue/src/tools/tool_name/tool_name.vue +0 -0
  493. data/templates/tool_vue/vite.config.js +1 -1
  494. data/templates/widget/.prettierrc.cjs +0 -0
  495. data/templates/widget/LICENSE.md +0 -0
  496. data/templates/widget/Rakefile +0 -0
  497. data/templates/widget/package.json +2 -2
  498. data/templates/widget/src/Widget.vue +0 -0
  499. data/templates/widget/vite.config.js +0 -0
  500. metadata +40 -11
  501. data/templates/tool_vue/.nycrc +0 -3
  502. data/templates/widget/.nycrc +0 -3
@@ -22,6 +22,7 @@ require 'rubygems'
22
22
  require 'rubygems/uninstaller'
23
23
  require 'tempfile'
24
24
  require 'openc3/utilities/process_manager'
25
+ require 'openc3/utilities/rubygems_url'
25
26
  require 'openc3/api/api'
26
27
  require 'pathname'
27
28
 
@@ -34,8 +35,8 @@ module OpenC3
34
35
  extend Api
35
36
 
36
37
  def self.names
37
- if Dir.exist?("#{ENV['GEM_HOME']}/gems")
38
- result = Pathname.new("#{ENV['GEM_HOME']}/gems").children.select { |c| c.directory? }.collect { |p| File.basename(p) + '.gem' }
38
+ if Dir.exist?("#{ENV.fetch('GEM_HOME', nil)}/gems")
39
+ result = Pathname.new("#{ENV.fetch('GEM_HOME', nil)}/gems").children.select { |c| c.directory? }.collect { |p| File.basename(p) + '.gem' }
39
40
  else
40
41
  result = []
41
42
  end
@@ -43,19 +44,19 @@ module OpenC3
43
44
  end
44
45
 
45
46
  def self.get(name)
46
- path = "#{ENV['GEM_HOME']}/cosmoscache/#{name}"
47
+ path = "#{ENV.fetch('GEM_HOME', nil)}/cosmoscache/#{name}"
47
48
  return path if File.exist?(path)
48
- path = "#{ENV['GEM_HOME']}/cache/#{name}"
49
+ path = "#{ENV.fetch('GEM_HOME', nil)}/cache/#{name}"
49
50
  return path if File.exist?(path)
50
- raise "Gem #{name} not found"
51
+ raise "Gem '#{name}' not found"
51
52
  end
52
53
 
53
54
  def self.put(gem_file_path, gem_install: true, scope:)
54
55
  if File.file?(gem_file_path)
55
56
  gem_filename = File.basename(gem_file_path)
56
57
  # Put into cosmoscache folder that we control
57
- FileUtils.mkdir_p("#{ENV['GEM_HOME']}/cosmoscache") unless Dir.exist?("#{ENV['GEM_HOME']}/cosmoscache")
58
- FileUtils.cp(gem_file_path, "#{ENV['GEM_HOME']}/cosmoscache/#{File.basename(gem_file_path)}")
58
+ FileUtils.mkdir_p("#{ENV.fetch('GEM_HOME', nil)}/cosmoscache") unless Dir.exist?("#{ENV.fetch('GEM_HOME', nil)}/cosmoscache")
59
+ FileUtils.cp(gem_file_path, "#{ENV.fetch('GEM_HOME', nil)}/cosmoscache/#{File.basename(gem_file_path)}")
59
60
  if gem_install
60
61
  Logger.info "Installing gem: #{gem_filename}"
61
62
  result = OpenC3::ProcessManager.instance.spawn(["ruby", "/openc3/bin/openc3cli", "geminstall", gem_filename, scope], "package_install", gem_filename, Time.now + 3600.0, scope: scope)
@@ -79,9 +80,11 @@ module OpenC3
79
80
  rubygems_url = get_setting('rubygems_url', scope: scope)
80
81
  rescue
81
82
  # If Redis isn't running try the ENV, then simply rubygems.org
82
- rubygems_url = ENV['RUBYGEMS_URL']
83
- rubygems_url ||= 'https://rubygems.org'
83
+ rubygems_url = ENV.fetch('RUBYGEMS_URL', RubygemsUrl::DEFAULT)
84
84
  end
85
+ # The url comes from a user-writable setting or ENV so validate it before
86
+ # using it as a Gem source
87
+ rubygems_url = RubygemsUrl.validate(rubygems_url) if rubygems_url
85
88
  Gem.sources = [rubygems_url] if rubygems_url
86
89
  Gem.done_installing_hooks.clear
87
90
  begin
@@ -92,7 +95,7 @@ module OpenC3
92
95
  Gem.install(gem_file_path, "> 0.pre", build_args: ['--no-document, --without development,test'], prerelease: true, domain: :both)
93
96
  end
94
97
  rescue => e
95
- message = "Gem file #{gem_file_path} error installing to #{ENV['GEM_HOME']}\n#{e.formatted}"
98
+ message = "Gem file #{gem_file_path} error installing to #{ENV.fetch('GEM_HOME', nil)}\n#{e.formatted}"
96
99
  Logger.error message
97
100
  raise e
98
101
  end
@@ -0,0 +1,114 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2026 OpenC3, Inc.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is distributed in the hope that it will be useful,
7
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
+ # See LICENSE.md for more details.
10
+ #
11
+ # This file may also be used under the terms of a commercial license
12
+ # if purchased from OpenC3, Inc.
13
+
14
+ require 'openc3/models/model'
15
+
16
+ module OpenC3
17
+ # Captures the information openc3-app needs to spawn a COSMOS interface on the
18
+ # host computer (outside Docker) so it can reach hardware such as serial ports
19
+ # that aren't available inside a container. A host microservice performs raw
20
+ # data transfer and basic functionality only; complex processing (protocols,
21
+ # target definitions) stays on the Docker side of COSMOS. The host side speaks
22
+ # Iroh back to openc3-app which forwards the data into COSMOS through the named
23
+ # bridge_microservice relay.
24
+ #
25
+ # This model is written by InterfaceModel#deploy for bridged interfaces (those
26
+ # configured with the BRIDGE keyword) and read by openc3-app. It is deliberately
27
+ # kept separate from MicroserviceModel so it is NOT run by the normal COSMOS
28
+ # microservice operator.
29
+ class HostInterfaceMicroserviceModel < Model
30
+ PRIMARY_KEY = 'openc3_host_interface_microservices'
31
+
32
+ # The real interface class and its parameters, e.g. ['serial_interface.py', ...]
33
+ attr_accessor :config_params
34
+ attr_accessor :work_dir
35
+ attr_accessor :env
36
+ attr_accessor :options
37
+ attr_accessor :secret_options
38
+ attr_accessor :protocols
39
+ attr_accessor :secrets
40
+ attr_accessor :container
41
+ attr_accessor :needs_dependencies
42
+ # The bridge (bridge_microservice relay) this host interface routes through.
43
+ attr_accessor :bridge_name
44
+ # The Iroh ALPN stream name (the interface name) used to route data back to
45
+ # openc3-app and on to the matching COSMOS bridge_interface.
46
+ attr_accessor :stream
47
+
48
+ # NOTE: The following three class methods are reimplemented (like other
49
+ # scoped models) so the ModelController and Model class methods work.
50
+ def self.get(name:, scope:)
51
+ super("#{scope}__#{PRIMARY_KEY}", name: name)
52
+ end
53
+
54
+ def self.names(scope:)
55
+ super("#{scope}__#{PRIMARY_KEY}")
56
+ end
57
+
58
+ def self.all(scope:)
59
+ super("#{scope}__#{PRIMARY_KEY}")
60
+ end
61
+ # END NOTE
62
+
63
+ def initialize(
64
+ name:,
65
+ bridge_name:,
66
+ stream:,
67
+ config_params: [],
68
+ work_dir: '.',
69
+ env: {},
70
+ options: [],
71
+ secret_options: [],
72
+ protocols: [],
73
+ secrets: [],
74
+ container: nil,
75
+ needs_dependencies: false,
76
+ updated_at: nil,
77
+ plugin: nil,
78
+ scope:
79
+ )
80
+ super("#{scope}__#{PRIMARY_KEY}", name: name, updated_at: updated_at, plugin: plugin, scope: scope)
81
+ @bridge_name = bridge_name
82
+ @stream = stream
83
+ @config_params = config_params
84
+ @work_dir = work_dir
85
+ @env = env
86
+ @options = options
87
+ @secret_options = secret_options
88
+ @protocols = protocols
89
+ @secrets = secrets
90
+ @container = container
91
+ @needs_dependencies = needs_dependencies
92
+ end
93
+
94
+ def as_json(*a)
95
+ {
96
+ 'name' => @name,
97
+ 'scope' => @scope,
98
+ 'bridge_name' => @bridge_name,
99
+ 'stream' => @stream,
100
+ 'config_params' => @config_params,
101
+ 'work_dir' => @work_dir,
102
+ 'env' => @env,
103
+ 'options' => @options,
104
+ 'secret_options' => @secret_options,
105
+ 'protocols' => @protocols,
106
+ 'secrets' => @secrets.as_json(*a),
107
+ 'container' => @container,
108
+ 'needs_dependencies' => @needs_dependencies,
109
+ 'plugin' => @plugin,
110
+ 'updated_at' => @updated_at
111
+ }
112
+ end
113
+ end
114
+ end
File without changes
@@ -16,11 +16,16 @@
16
16
  # if purchased from OpenC3, Inc.
17
17
 
18
18
  require 'openc3/models/model'
19
+ require 'openc3/models/config_keyword_model'
19
20
  require 'openc3/models/microservice_model'
21
+ require 'openc3/models/bridge_model'
22
+ require 'openc3/models/host_interface_microservice_model'
20
23
  require 'openc3/models/target_model'
21
24
 
22
25
  module OpenC3
23
26
  class InterfaceModel < Model
27
+ include ConfigKeywordModel
28
+
24
29
  INTERFACES_PRIMARY_KEY = 'openc3_interfaces'
25
30
  ROUTERS_PRIMARY_KEY = 'openc3_routers'
26
31
 
@@ -36,7 +41,10 @@ module OpenC3
36
41
  attr_accessor :disable_disconnect
37
42
  attr_accessor :options
38
43
  attr_accessor :secret_options
44
+ attr_accessor :bridge_options
45
+ attr_accessor :bridge_secret_options
39
46
  attr_accessor :protocols
47
+ attr_accessor :bridge_protocols
40
48
  attr_accessor :interfaces
41
49
  attr_accessor :log_stream
42
50
  attr_accessor :needs_dependencies
@@ -49,6 +57,7 @@ module OpenC3
49
57
  attr_accessor :prefix
50
58
  attr_accessor :shard
51
59
  attr_accessor :db_shard
60
+ attr_accessor :bridge_name
52
61
 
53
62
  # NOTE: The following three class methods are used by the ModelController
54
63
  # and are reimplemented to enable various Model class methods to work
@@ -111,6 +120,9 @@ module OpenC3
111
120
  options: [],
112
121
  secret_options: [],
113
122
  protocols: [],
123
+ bridge_options: [],
124
+ bridge_secret_options: [],
125
+ bridge_protocols: [],
114
126
  log_stream: nil,
115
127
  updated_at: nil,
116
128
  plugin: nil,
@@ -124,6 +136,7 @@ module OpenC3
124
136
  prefix: nil,
125
137
  shard: 0,
126
138
  db_shard: 0,
139
+ bridge_name: nil,
127
140
  scope:
128
141
  )
129
142
  if self.class._get_type == 'INTERFACE'
@@ -157,7 +170,10 @@ module OpenC3
157
170
  @disable_disconnect = disable_disconnect
158
171
  @options = options
159
172
  @secret_options = secret_options
173
+ @bridge_options = bridge_options
174
+ @bridge_secret_options = bridge_secret_options
160
175
  @protocols = protocols
176
+ @bridge_protocols = bridge_protocols
161
177
  @log_stream = log_stream
162
178
  @needs_dependencies = needs_dependencies
163
179
  @cmd = cmd
@@ -180,6 +196,7 @@ module OpenC3
180
196
  @prefix = prefix
181
197
  @shard = shard.to_i # to_i to handle nil
182
198
  @db_shard = db_shard.to_i # to_i to handle nil
199
+ @bridge_name = bridge_name&.to_s&.upcase
183
200
  @secrets = secrets
184
201
  end
185
202
 
@@ -238,6 +255,9 @@ module OpenC3
238
255
  'options' => @options,
239
256
  'secret_options' => @secret_options,
240
257
  'protocols' => @protocols,
258
+ 'bridge_options' => @bridge_options,
259
+ 'bridge_secret_options' => @bridge_secret_options,
260
+ 'bridge_protocols' => @bridge_protocols,
241
261
  'log_stream' => @log_stream,
242
262
  'plugin' => @plugin,
243
263
  'needs_dependencies' => @needs_dependencies,
@@ -250,13 +270,14 @@ module OpenC3
250
270
  'prefix' => @prefix,
251
271
  'shard' => @shard,
252
272
  'db_shard' => @db_shard,
273
+ 'bridge_name' => @bridge_name,
253
274
  'updated_at' => @updated_at
254
275
  }
255
276
  end
256
277
 
257
278
  def ensure_target_exists(target_name)
258
279
  target = TargetModel.get(name: target_name, scope: @scope)
259
- raise "Target #{target_name} does not exist" unless target
280
+ raise "Target '#{target_name}' does not exist (InterfaceModel)" unless target
260
281
  target
261
282
  end
262
283
 
@@ -277,6 +298,8 @@ module OpenC3
277
298
 
278
299
  # Handles Interface/Router specific configuration keywords
279
300
  def handle_config(parser, keyword, parameters)
301
+ type = self.class._get_type
302
+
280
303
  case keyword
281
304
  when 'MAP_TARGET'
282
305
  usage = "#{keyword} <Target Name> <ENABLED/DISABLED>"
@@ -334,6 +357,14 @@ module OpenC3
334
357
  parser.verify_num_parameters(2, nil, "#{keyword} <Option Name> <Option Value 1> <Option Value 2 (optional)> <etc>")
335
358
  @options << parameters.dup
336
359
 
360
+ when 'BRIDGE_OPTION'
361
+ if type == 'INTERFACE'
362
+ parser.verify_num_parameters(2, nil, "#{keyword} <Option Name> <Option Value 1> <Option Value 2 (optional)> <etc>")
363
+ @bridge_options << parameters.dup
364
+ else
365
+ raise ConfigParser::Error.new(parser, "BRIDGE_OPTION not supported for Router: #{keyword} #{parameters.join(" ")}")
366
+ end
367
+
337
368
  when 'PROTOCOL'
338
369
  usage = "#{keyword} <READ WRITE READ_WRITE> <protocol filename or classname> <Protocol specific parameters>"
339
370
  parser.verify_num_parameters(2, nil, usage)
@@ -343,6 +374,19 @@ module OpenC3
343
374
 
344
375
  @protocols << parameters.dup
345
376
 
377
+ when 'BRIDGE_PROTOCOL'
378
+ if type == 'INTERFACE'
379
+ usage = "#{keyword} <READ WRITE READ_WRITE> <protocol filename or classname> <Protocol specific parameters>"
380
+ parser.verify_num_parameters(2, nil, usage)
381
+ unless %w(READ WRITE READ_WRITE).include? parameters[0].upcase
382
+ raise parser.error("Invalid protocol type: #{parameters[0]}", usage)
383
+ end
384
+
385
+ @bridge_protocols << parameters.dup
386
+ else
387
+ raise ConfigParser::Error.new(parser, "BRIDGE_PROTOCOL not supported for Router: #{keyword} #{parameters.join(" ")}")
388
+ end
389
+
346
390
  when 'DONT_LOG'
347
391
  Logger.warn "DONT_LOG is deprecated and does nothing."
348
392
 
@@ -352,6 +396,7 @@ module OpenC3
352
396
 
353
397
  when 'SECRET'
354
398
  parser.verify_num_parameters(3, 5, "#{keyword} <Secret Type: ENV or FILE> <Secret Name> <Environment Variable Name or File Path> <Option Name (Optional)> <Secret Store Name (Optional)>")
399
+ validate_secret(parser, keyword, parameters)
355
400
  @secrets << parameters[0..2]
356
401
  if ConfigParser.handle_nil(parameters[3])
357
402
  # Option Name, Secret Name
@@ -359,29 +404,26 @@ module OpenC3
359
404
  end
360
405
  @secrets[-1] << ConfigParser.handle_nil(parameters[4])
361
406
 
407
+ when 'BRIDGE_SECRET'
408
+ if type == 'INTERFACE'
409
+ parser.verify_num_parameters(3, 5, "#{keyword} <Secret Type: ENV or FILE> <Secret Name> <Environment Variable Name or File Path> <Option Name (Optional)> <Secret Store Name (Optional)>")
410
+ validate_secret(parser, keyword, parameters)
411
+ @secrets << parameters[0..2]
412
+ if ConfigParser.handle_nil(parameters[3])
413
+ # Option Name, Secret Name
414
+ @bridge_secret_options << [parameters[3], parameters[1]]
415
+ end
416
+ @secrets[-1] << ConfigParser.handle_nil(parameters[4])
417
+ else
418
+ raise ConfigParser::Error.new(parser, "BRIDGE_SECRET not supported for Router: #{keyword} #{parameters.join(" ")}")
419
+ end
420
+
362
421
  when 'ENV'
363
422
  parser.verify_num_parameters(2, 2, "#{keyword} <Key> <Value>")
364
423
  @env[parameters[0]] = parameters[1]
365
424
 
366
425
  when 'PORT'
367
- usage = "PORT <Number> <Protocol (Optional)"
368
- parser.verify_num_parameters(1, 2, usage)
369
- begin
370
- @ports << [Integer(parameters[0])]
371
- rescue # In case Integer fails
372
- raise ConfigParser::Error.new(parser, "Port must be an integer: #{parameters[0]}", usage)
373
- end
374
- protocol = ConfigParser.handle_nil(parameters[1])
375
- if protocol
376
- # Per https://kubernetes.io/docs/concepts/services-networking/service/#protocol-support
377
- if %w(TCP UDP SCTP).include?(protocol.upcase)
378
- @ports[-1] << protocol.upcase
379
- else
380
- raise ConfigParser::Error.new(parser, "Unknown port protocol: #{parameters[1]}", usage)
381
- end
382
- else
383
- @ports[-1] << 'TCP'
384
- end
426
+ parse_port(parser, keyword, parameters)
385
427
 
386
428
  when 'WORK_DIR'
387
429
  parser.verify_num_parameters(1, 1, "#{keyword} <Dir>")
@@ -404,8 +446,17 @@ module OpenC3
404
446
  @shard = Integer(parameters[0])
405
447
 
406
448
  when 'DB_SHARD'
407
- parser.verify_num_parameters(1, 1, "#{keyword} <Shard Number Starting from 0>")
449
+ parser.verify_num_parameters(1, 1, "#{keyword} <DB_Shard Number Starting from 0>")
408
450
  @db_shard = Integer(parameters[0])
451
+
452
+ when 'BRIDGE'
453
+ if type == 'INTERFACE'
454
+ parser.verify_num_parameters(1, 1, "#{keyword} <Bridge Name>")
455
+ @bridge_name = parameters[0].to_s.upcase
456
+ else
457
+ raise ConfigParser::Error.new(parser, "BRIDGE not supported for Router: #{keyword} #{parameters.join(" ")}")
458
+ end
459
+
409
460
  else
410
461
  raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Interface/Router: #{keyword} #{parameters.join(" ")}")
411
462
 
@@ -418,6 +469,11 @@ module OpenC3
418
469
  def deploy(gem_path, variables, validate_only: false)
419
470
  type = self.class._get_type
420
471
  microservice_name = "#{@scope}__#{type}__#{@name}"
472
+ # A bridged interface runs on the host (openc3-app) rather than in Docker.
473
+ # COSMOS runs a bridge_interface tunnel + a shared bridge_microservice relay
474
+ # and hands the real interface's spawn info to openc3-app. See deploy_bridge.
475
+ return deploy_bridge(gem_path, variables, microservice_name, type, validate_only: validate_only) if @bridge_name
476
+
421
477
  microservice = MicroserviceModel.new(
422
478
  name: microservice_name,
423
479
  work_dir: @work_dir,
@@ -444,6 +500,92 @@ module OpenC3
444
500
  microservice
445
501
  end
446
502
 
503
+ # Deploy a bridged interface. This splits the interface across the Docker
504
+ # side of COSMOS (bridge_interface tunnel + protocols/target processing) and
505
+ # the host side (openc3-app spawns the real interface for raw data transfer):
506
+ # 1. A COSMOS interface_microservice that builds a bridge_interface tunnel
507
+ # (InterfaceModel#build substitutes BridgeInterface when bridge_name is
508
+ # set) and connects to the named bridge_microservice relay.
509
+ # 2. A shared bridge_microservice relay (one per bridge_name) that this
510
+ # interface's stream (ALPN stream/<name>) is added to.
511
+ # 3. A HostInterfaceMicroserviceModel holding the real interface's spawn info for
512
+ # openc3-app to run the interface on the host.
513
+ def deploy_bridge(gem_path, variables, microservice_name, type, validate_only: false)
514
+ # The COSMOS side runs the Python interface_microservice which builds a
515
+ # BridgeInterface (see InterfaceModel#build). It keeps this interface's
516
+ # protocols and target mapping; the raw device I/O is tunneled to the host.
517
+ cmd = [BridgeModel.bridge_python_bin, "#{type.downcase}_microservice.py", microservice_name]
518
+ microservice = MicroserviceModel.new(
519
+ name: microservice_name,
520
+ work_dir: BridgeModel::BRIDGE_WORK_DIR,
521
+ cmd: cmd,
522
+ env: @env,
523
+ ports: @ports,
524
+ container: @container,
525
+ target_names: @target_names,
526
+ plugin: @plugin,
527
+ needs_dependencies: @needs_dependencies,
528
+ secrets: @secrets,
529
+ prefix: @prefix,
530
+ shard: @shard,
531
+ db_shard: @db_shard,
532
+ scope: @scope
533
+ )
534
+ unless validate_only
535
+ @target_names.each { |target_name| ensure_target_exists(target_name) }
536
+ check_bridge_relay()
537
+ microservice.create
538
+ microservice.deploy(gem_path, variables)
539
+ create_host_interface_microservice()
540
+ ConfigTopic.write({ kind: 'created', type: type.downcase, name: @name, plugin: @plugin }, scope: @scope)
541
+ Logger.info "Configured bridged #{type.downcase} microservice #{microservice_name} (bridge #{@bridge_name})"
542
+ end
543
+ microservice
544
+ end
545
+
546
+ # Name of the shared bridge_microservice relay for a given bridge name.
547
+ def bridge_relay_name
548
+ "#{@scope}__BRIDGE__#{@bridge_name}"
549
+ end
550
+
551
+ # Verify the shared bridge_microservice relay for @bridge_name exists.
552
+ # Multiple bridged interfaces sharing a bridge name all route through the
553
+ # same relay. The relay discovers which streams to advertise by querying the
554
+ # HostInterfaceMicroserviceModels for its bridge (see bridge_microservice.py), so we
555
+ # deliberately do NOT push this interface's stream onto the relay as an
556
+ # OPTION: mutating (updating) the relay's MicroserviceModel would make the
557
+ # operator respawn the relay every time a bridged interface is added or
558
+ # removed. Leaving the model untouched lets the relay adapt live instead.
559
+ def check_bridge_relay
560
+ relay = MicroserviceModel.get_model(name: bridge_relay_name, scope: @scope)
561
+ raise "Bridge #{@bridge_name} does not exist" unless relay
562
+ relay
563
+ end
564
+
565
+ # Capture the real interface's spawn info for openc3-app. The host side does
566
+ # raw data transfer only, so protocols and target definitions are omitted;
567
+ # the connection options and secrets needed to open the device are included.
568
+ def create_host_interface_microservice
569
+ host = HostInterfaceMicroserviceModel.new(
570
+ name: @name,
571
+ bridge_name: @bridge_name,
572
+ stream: @name,
573
+ config_params: @config_params,
574
+ work_dir: @work_dir,
575
+ env: @env,
576
+ options: @bridge_options,
577
+ secret_options: @bridge_secret_options,
578
+ protocols: @bridge_protocols,
579
+ secrets: @secrets,
580
+ container: @container,
581
+ needs_dependencies: @needs_dependencies,
582
+ plugin: @plugin,
583
+ scope: @scope
584
+ )
585
+ host.create(force: true)
586
+ host
587
+ end
588
+
447
589
  # Destroys the deployed MicroserviceModel which triggers the operator to kill
448
590
  # the microservice. Also directly destroys the Interface/RouterStatusModel in
449
591
  # an ensure block. The microservice stop() no longer recreates the status
@@ -469,6 +611,23 @@ module OpenC3
469
611
  status_model.destroy if status_model
470
612
  rescue Exception => error
471
613
  Logger.error("Error destroying #{type&.downcase || 'unknown'} status model #{@name} in scope #{@scope} due to #{error}")
614
+ ensure
615
+ undeploy_bridge if @bridge_name
616
+ end
617
+ end
618
+
619
+ # Clean up the host-side pieces of a bridged interface by destroying its
620
+ # HostInterfaceMicroserviceModel. The shared bridge_microservice relay is left
621
+ # untouched: it discovers its streams by querying the HostInterfaceMicroserviceModels,
622
+ # so removing this one makes the relay drop the stream's ALPN on its own.
623
+ # (Mutating the relay's MicroserviceModel here would respawn it.) The relay
624
+ # itself is destroyed with its BridgeModel, not per-interface.
625
+ def undeploy_bridge
626
+ begin
627
+ host = HostInterfaceMicroserviceModel.get_model(name: @name, scope: @scope)
628
+ host.destroy if host
629
+ rescue Exception => error
630
+ Logger.error("Error destroying HostInterfaceMicroservice model #{@name} in scope #{@scope} due to #{error}")
472
631
  end
473
632
  end
474
633
 
File without changes
File without changes
File without changes
@@ -17,12 +17,16 @@
17
17
 
18
18
  require 'openc3/top_level'
19
19
  require 'openc3/models/model'
20
+ require 'openc3/models/config_keyword_model'
20
21
  require 'openc3/models/metric_model'
21
22
  require 'openc3/topics/config_topic'
22
23
  require 'openc3/utilities/bucket'
24
+ require 'openc3/utilities/python_venv'
23
25
 
24
26
  module OpenC3
25
27
  class MicroserviceModel < Model
28
+ include ConfigKeywordModel
29
+
26
30
  PRIMARY_KEY = 'openc3_microservices'
27
31
 
28
32
  attr_accessor :cmd
@@ -140,6 +144,26 @@ module OpenC3
140
144
  @bucket = Bucket.getClient()
141
145
  end
142
146
 
147
+ # Resolve the Python environment variables that will be used at runtime.
148
+ # This mirrors the logic in MicroserviceOperator#convert_microservice_to_process_definition.
149
+ def runtime_python_env
150
+ python_bin = ENV['OPENC3_PYTHON_BIN'] || '/openc3/python/.venv/bin/python'
151
+ result = { 'OPENC3_PYTHON_BIN' => python_bin }
152
+ if @needs_dependencies && @plugin
153
+ candidate = PythonVenv.plugin_venv_path(scope: @scope, plugin_name: @plugin)
154
+ if candidate
155
+ result['VIRTUAL_ENV'] = candidate
156
+ result['PYTHONUSERBASE'] = candidate
157
+ site_packages = Dir.glob("#{candidate}/lib/python*/site-packages").first
158
+ result['PYTHONPATH'] = site_packages
159
+ else
160
+ result['VIRTUAL_ENV'] = nil
161
+ result['PYTHONUSERBASE'] = PythonVenv::DEFAULT_PYTHONUSERBASE
162
+ end
163
+ end
164
+ result
165
+ end
166
+
143
167
  def as_json(*a)
144
168
  {
145
169
  'name' => @name,
@@ -175,24 +199,7 @@ module OpenC3
175
199
  parser.verify_num_parameters(1, 1, "#{keyword} <Dir>")
176
200
  @work_dir = parameters[0]
177
201
  when 'PORT'
178
- usage = "PORT <Number> <Protocol (Optional)"
179
- parser.verify_num_parameters(1, 2, usage)
180
- begin
181
- @ports << [Integer(parameters[0])]
182
- rescue # In case Integer fails
183
- raise ConfigParser::Error.new(parser, "Port must be an integer: #{parameters[0]}", usage)
184
- end
185
- protocol = ConfigParser.handle_nil(parameters[1])
186
- if protocol
187
- # Per https://kubernetes.io/docs/concepts/services-networking/service/#protocol-support
188
- if %w(TCP UDP SCTP).include?(protocol.upcase)
189
- @ports[-1] << protocol.upcase
190
- else
191
- raise ConfigParser::Error.new(parser, "Unknown port protocol: #{parameters[1]}", usage)
192
- end
193
- else
194
- @ports[-1] << 'TCP'
195
- end
202
+ parse_port(parser, keyword, parameters)
196
203
  when 'TOPIC'
197
204
  parser.verify_num_parameters(1, 1, "#{keyword} <Topic Name>")
198
205
  @topics << parameters[0]
@@ -223,6 +230,7 @@ module OpenC3
223
230
  @container = parameters[0]
224
231
  when 'SECRET'
225
232
  parser.verify_num_parameters(3, 4, "#{keyword} <Secret Type: ENV or FILE> <Secret Name> <Environment Variable Name or File Path> <Secret Store Name (Optional)>")
233
+ validate_secret(parser, keyword, parameters)
226
234
  if ConfigParser.handle_nil(parameters[3])
227
235
  @secrets << parameters.dup
228
236
  else
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes