openc3 7.3.0 → 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 (499) 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 +138 -0
  8. data/bin/pipuninstall +0 -0
  9. data/bin/uvinstall +10 -3
  10. data/data/config/_array_params.yaml +0 -0
  11. data/data/config/_canvas_values.yaml +0 -0
  12. data/data/config/_graph_params.yaml +0 -0
  13. data/data/config/_id_items.yaml +0 -0
  14. data/data/config/_id_params.yaml +0 -0
  15. data/data/config/_interfaces.yaml +0 -0
  16. data/data/config/_items.yaml +0 -0
  17. data/data/config/_params.yaml +0 -0
  18. data/data/config/command.yaml +0 -0
  19. data/data/config/command_modifiers.yaml +2 -0
  20. data/data/config/command_telemetry.yaml +0 -0
  21. data/data/config/conversions.yaml +0 -0
  22. data/data/config/graph_settings.yaml +0 -0
  23. data/data/config/interface_modifiers.yaml +103 -1
  24. data/data/config/item_modifiers.yaml +0 -0
  25. data/data/config/microservice.yaml +5 -2
  26. data/data/config/param_item_modifiers.yaml +0 -0
  27. data/data/config/parameter_modifiers.yaml +0 -0
  28. data/data/config/plugins.yaml +0 -0
  29. data/data/config/processors.yaml +0 -0
  30. data/data/config/protocols.yaml +0 -0
  31. data/data/config/screen.yaml +0 -0
  32. data/data/config/settings.yaml +0 -0
  33. data/data/config/table_manager.yaml +0 -0
  34. data/data/config/table_parameter_modifiers.yaml +0 -0
  35. data/data/config/target.yaml +0 -0
  36. data/data/config/target_config.yaml +0 -0
  37. data/data/config/telemetry.yaml +0 -0
  38. data/data/config/telemetry_modifiers.yaml +2 -0
  39. data/data/config/tool.yaml +0 -0
  40. data/data/config/unknown.yaml +0 -0
  41. data/data/config/widgets.yaml +6 -0
  42. data/data/xtce_schemas/SpaceSystem_20180204.xsd +0 -0
  43. data/data/xtce_schemas/xml.xsd +0 -0
  44. data/ext/openc3/ext/array/array.c +0 -0
  45. data/ext/openc3/ext/array/extconf.rb +0 -0
  46. data/ext/openc3/ext/buffered_file/buffered_file.c +0 -0
  47. data/ext/openc3/ext/buffered_file/extconf.rb +0 -0
  48. data/ext/openc3/ext/burst_protocol/burst_protocol.c +6 -0
  49. data/ext/openc3/ext/burst_protocol/extconf.rb +0 -0
  50. data/ext/openc3/ext/config_parser/config_parser.c +0 -0
  51. data/ext/openc3/ext/config_parser/extconf.rb +0 -0
  52. data/ext/openc3/ext/crc/crc.c +0 -0
  53. data/ext/openc3/ext/crc/extconf.rb +0 -0
  54. data/ext/openc3/ext/openc3_io/extconf.rb +0 -0
  55. data/ext/openc3/ext/openc3_io/openc3_io.c +0 -0
  56. data/ext/openc3/ext/packet/extconf.rb +0 -0
  57. data/ext/openc3/ext/packet/packet.c +0 -0
  58. data/ext/openc3/ext/platform/extconf.rb +0 -0
  59. data/ext/openc3/ext/platform/platform.c +4 -1
  60. data/ext/openc3/ext/polynomial_conversion/extconf.rb +0 -0
  61. data/ext/openc3/ext/polynomial_conversion/polynomial_conversion.c +0 -0
  62. data/ext/openc3/ext/string/extconf.rb +0 -0
  63. data/ext/openc3/ext/string/string.c +0 -0
  64. data/ext/openc3/ext/structure/structure.c +49 -17
  65. data/ext/openc3/ext/tabbed_plots_config/extconf.rb +0 -0
  66. data/ext/openc3/ext/tabbed_plots_config/tabbed_plots_config.c +0 -0
  67. data/ext/openc3/ext/telemetry/extconf.rb +0 -0
  68. data/ext/openc3/ext/telemetry/telemetry.c +0 -0
  69. data/lib/cosmos.rb +0 -0
  70. data/lib/cosmosc2.rb +0 -0
  71. data/lib/openc3/accessors/accessor.rb +0 -0
  72. data/lib/openc3/accessors/binary_accessor.rb +63 -9
  73. data/lib/openc3/accessors/cbor_accessor.rb +0 -0
  74. data/lib/openc3/accessors/form_accessor.rb +0 -0
  75. data/lib/openc3/accessors/html_accessor.rb +0 -0
  76. data/lib/openc3/accessors/http_accessor.rb +0 -0
  77. data/lib/openc3/accessors/json_accessor.rb +0 -0
  78. data/lib/openc3/accessors/template_accessor.rb +0 -0
  79. data/lib/openc3/accessors/xml_accessor.rb +0 -0
  80. data/lib/openc3/accessors.rb +0 -0
  81. data/lib/openc3/api/README.md +0 -0
  82. data/lib/openc3/api/api.rb +0 -0
  83. data/lib/openc3/api/authorized_api.rb +0 -0
  84. data/lib/openc3/api/calendar_api.rb +0 -0
  85. data/lib/openc3/api/cmd_api.rb +1 -1
  86. data/lib/openc3/api/config_api.rb +0 -0
  87. data/lib/openc3/api/interface_api.rb +0 -0
  88. data/lib/openc3/api/limits_api.rb +0 -0
  89. data/lib/openc3/api/metrics_api.rb +1 -1
  90. data/lib/openc3/api/offline_access_api.rb +1 -1
  91. data/lib/openc3/api/router_api.rb +0 -0
  92. data/lib/openc3/api/settings_api.rb +22 -18
  93. data/lib/openc3/api/stash_api.rb +1 -1
  94. data/lib/openc3/api/target_api.rb +0 -0
  95. data/lib/openc3/api/tlm_api.rb +4 -3
  96. data/lib/openc3/bridge/bridge.rb +0 -0
  97. data/lib/openc3/bridge/bridge_config.rb +0 -0
  98. data/lib/openc3/bridge/bridge_interface_thread.rb +0 -0
  99. data/lib/openc3/bridge/bridge_router_thread.rb +0 -0
  100. data/lib/openc3/ccsds/ccsds_packet.rb +0 -0
  101. data/lib/openc3/ccsds/ccsds_parser.rb +0 -0
  102. data/lib/openc3/config/config_parser.rb +78 -17
  103. data/lib/openc3/config/meta_config_parser.rb +0 -0
  104. data/lib/openc3/conversions/bit_reverse_conversion.rb +0 -0
  105. data/lib/openc3/conversions/conversion.rb +0 -0
  106. data/lib/openc3/conversions/generic_conversion.rb +0 -0
  107. data/lib/openc3/conversions/ip_read_conversion.rb +0 -0
  108. data/lib/openc3/conversions/ip_write_conversion.rb +0 -0
  109. data/lib/openc3/conversions/object_read_conversion.rb +0 -0
  110. data/lib/openc3/conversions/object_write_conversion.rb +0 -0
  111. data/lib/openc3/conversions/packet_time_formatted_conversion.rb +0 -0
  112. data/lib/openc3/conversions/packet_time_seconds_conversion.rb +0 -0
  113. data/lib/openc3/conversions/polynomial_conversion.rb +0 -0
  114. data/lib/openc3/conversions/processor_conversion.rb +0 -0
  115. data/lib/openc3/conversions/received_count_conversion.rb +0 -0
  116. data/lib/openc3/conversions/received_time_formatted_conversion.rb +0 -0
  117. data/lib/openc3/conversions/received_time_seconds_conversion.rb +0 -0
  118. data/lib/openc3/conversions/segmented_polynomial_conversion.rb +0 -0
  119. data/lib/openc3/conversions/unix_time_conversion.rb +0 -0
  120. data/lib/openc3/conversions/unix_time_formatted_conversion.rb +0 -0
  121. data/lib/openc3/conversions/unix_time_seconds_conversion.rb +0 -0
  122. data/lib/openc3/conversions.rb +0 -0
  123. data/lib/openc3/core_ext/array.rb +0 -0
  124. data/lib/openc3/core_ext/binding.rb +0 -0
  125. data/lib/openc3/core_ext/class.rb +0 -0
  126. data/lib/openc3/core_ext/exception.rb +0 -0
  127. data/lib/openc3/core_ext/faraday.rb +0 -0
  128. data/lib/openc3/core_ext/file.rb +0 -0
  129. data/lib/openc3/core_ext/io.rb +0 -0
  130. data/lib/openc3/core_ext/kernel.rb +0 -0
  131. data/lib/openc3/core_ext/math.rb +0 -0
  132. data/lib/openc3/core_ext/matrix.rb +0 -0
  133. data/lib/openc3/core_ext/objectspace.rb +0 -0
  134. data/lib/openc3/core_ext/openc3_io.rb +0 -0
  135. data/lib/openc3/core_ext/range.rb +0 -0
  136. data/lib/openc3/core_ext/socket.rb +0 -0
  137. data/lib/openc3/core_ext/string.rb +0 -0
  138. data/lib/openc3/core_ext/stringio.rb +0 -0
  139. data/lib/openc3/core_ext/tempfile.rb +0 -0
  140. data/lib/openc3/core_ext/time.rb +0 -0
  141. data/lib/openc3/core_ext.rb +0 -0
  142. data/lib/openc3/ext/.keep +0 -0
  143. data/lib/openc3/interfaces/file_interface.rb +0 -0
  144. data/lib/openc3/interfaces/http_client_interface.rb +0 -0
  145. data/lib/openc3/interfaces/http_server_interface.rb +0 -0
  146. data/lib/openc3/interfaces/interface.rb +5 -4
  147. data/lib/openc3/interfaces/mqtt_interface.rb +0 -0
  148. data/lib/openc3/interfaces/mqtt_stream_interface.rb +2 -0
  149. data/lib/openc3/interfaces/protocols/burst_protocol.rb +27 -0
  150. data/lib/openc3/interfaces/protocols/cmd_response_protocol.rb +0 -0
  151. data/lib/openc3/interfaces/protocols/cobs_protocol.rb +0 -0
  152. data/lib/openc3/interfaces/protocols/crc_protocol.rb +0 -0
  153. data/lib/openc3/interfaces/protocols/fixed_protocol.rb +0 -0
  154. data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +0 -0
  155. data/lib/openc3/interfaces/protocols/length_protocol.rb +7 -0
  156. data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +7 -0
  157. data/lib/openc3/interfaces/protocols/protocol.rb +4 -4
  158. data/lib/openc3/interfaces/protocols/slip_protocol.rb +0 -0
  159. data/lib/openc3/interfaces/protocols/template_protocol.rb +2 -2
  160. data/lib/openc3/interfaces/protocols/terminated_protocol.rb +0 -0
  161. data/lib/openc3/interfaces/serial_interface.rb +2 -0
  162. data/lib/openc3/interfaces/simulated_target_interface.rb +0 -0
  163. data/lib/openc3/interfaces/stream_interface.rb +0 -0
  164. data/lib/openc3/interfaces/tcpip_client_interface.rb +2 -0
  165. data/lib/openc3/interfaces/tcpip_server_interface.rb +0 -0
  166. data/lib/openc3/interfaces/udp_interface.rb +16 -5
  167. data/lib/openc3/interfaces.rb +0 -0
  168. data/lib/openc3/io/buffered_file.rb +0 -0
  169. data/lib/openc3/io/io_multiplexer.rb +0 -0
  170. data/lib/openc3/io/json_api.rb +3 -2
  171. data/lib/openc3/io/json_api_object.rb +0 -0
  172. data/lib/openc3/io/json_drb.rb +20 -4
  173. data/lib/openc3/io/json_drb_object.rb +0 -0
  174. data/lib/openc3/io/json_drb_rack.rb +0 -0
  175. data/lib/openc3/io/json_rpc.rb +0 -0
  176. data/lib/openc3/io/posix_serial_driver.rb +0 -0
  177. data/lib/openc3/io/serial_driver.rb +0 -0
  178. data/lib/openc3/io/stderr.rb +0 -0
  179. data/lib/openc3/io/stdout.rb +0 -0
  180. data/lib/openc3/io/udp_sockets.rb +38 -8
  181. data/lib/openc3/io/win32_serial_driver.rb +1 -0
  182. data/lib/openc3/logs/buffered_packet_log_reader.rb +0 -0
  183. data/lib/openc3/logs/buffered_packet_log_writer.rb +0 -0
  184. data/lib/openc3/logs/log_writer.rb +0 -0
  185. data/lib/openc3/logs/packet_log_constants.rb +0 -0
  186. data/lib/openc3/logs/packet_log_reader.rb +23 -2
  187. data/lib/openc3/logs/packet_log_writer.rb +5 -0
  188. data/lib/openc3/logs/stream_log.rb +1 -1
  189. data/lib/openc3/logs/stream_log_pair.rb +0 -0
  190. data/lib/openc3/logs/text_log_writer.rb +0 -0
  191. data/lib/openc3/logs.rb +0 -0
  192. data/lib/openc3/microservices/cleanup_microservice.rb +0 -0
  193. data/lib/openc3/microservices/decom_common.rb +0 -0
  194. data/lib/openc3/microservices/decom_microservice.rb +0 -0
  195. data/lib/openc3/microservices/interface_decom_common.rb +2 -1
  196. data/lib/openc3/microservices/interface_microservice.rb +72 -25
  197. data/lib/openc3/microservices/log_microservice.rb +0 -0
  198. data/lib/openc3/microservices/microservice.rb +3 -3
  199. data/lib/openc3/microservices/multi_microservice.rb +1 -1
  200. data/lib/openc3/microservices/periodic_microservice.rb +2 -2
  201. data/lib/openc3/microservices/plugin_microservice.rb +0 -0
  202. data/lib/openc3/microservices/queue_microservice.rb +0 -0
  203. data/lib/openc3/microservices/router_microservice.rb +0 -0
  204. data/lib/openc3/microservices/scope_cleanup_microservice.rb +0 -0
  205. data/lib/openc3/microservices/text_log_microservice.rb +0 -0
  206. data/lib/openc3/migrations/20220420190000_log_stuff.rb +0 -0
  207. data/lib/openc3/migrations/20221202214600_add_target_names.rb +0 -0
  208. data/lib/openc3/migrations/20221210174900_convert_to_multi.rb +0 -0
  209. data/lib/openc3/migrations/20230915000002_no_scope_log_messages.rb +0 -0
  210. data/lib/openc3/migrations/20231022000000_tlm_viewer_config.rb +0 -0
  211. data/lib/openc3/migrations/20241208080000_no_critical_cmd.rb +0 -0
  212. data/lib/openc3/migrations/20241208080001_no_trigger_group.rb +0 -0
  213. data/lib/openc3/migrations/20250108060000_news_feed.rb +0 -0
  214. data/lib/openc3/migrations/20250402000000_periodic_only_default.rb +0 -0
  215. data/lib/openc3/migrations/20260203000000_remove_store_id.rb +0 -0
  216. data/lib/openc3/migrations/20260204000000_remove_decom_reducer.rb +0 -0
  217. data/lib/openc3/migrations/20260701000000_updated_at_to_int.rb +0 -0
  218. data/lib/openc3/migrations/20260704000000_default_bridge.rb +38 -0
  219. data/lib/openc3/migrations/20260731000000_system_health_json.rb +11 -0
  220. data/lib/openc3/models/activity_model.rb +0 -0
  221. data/lib/openc3/models/auth_model.rb +35 -7
  222. data/lib/openc3/models/bridge_model.rb +133 -0
  223. data/lib/openc3/models/config_keyword_model.rb +63 -0
  224. data/lib/openc3/models/cvt_model.rb +0 -0
  225. data/lib/openc3/models/db_sharded_model.rb +0 -0
  226. data/lib/openc3/models/environment_model.rb +0 -0
  227. data/lib/openc3/models/gem_model.rb +12 -9
  228. data/lib/openc3/models/host_interface_microservice_model.rb +114 -0
  229. data/lib/openc3/models/info_model.rb +0 -0
  230. data/lib/openc3/models/interface_model.rb +178 -19
  231. data/lib/openc3/models/interface_status_model.rb +0 -0
  232. data/lib/openc3/models/metadata_model.rb +0 -0
  233. data/lib/openc3/models/metric_model.rb +0 -0
  234. data/lib/openc3/models/microservice_model.rb +9 -22
  235. data/lib/openc3/models/microservice_status_model.rb +0 -0
  236. data/lib/openc3/models/migration_model.rb +0 -0
  237. data/lib/openc3/models/model.rb +0 -0
  238. data/lib/openc3/models/news_model.rb +0 -0
  239. data/lib/openc3/models/note_model.rb +0 -0
  240. data/lib/openc3/models/offline_access_model.rb +0 -0
  241. data/lib/openc3/models/ping_model.rb +0 -0
  242. data/lib/openc3/models/plugin_model.rb +9 -2
  243. data/lib/openc3/models/plugin_store_model.rb +0 -0
  244. data/lib/openc3/models/process_status_model.rb +0 -0
  245. data/lib/openc3/models/python_package_model.rb +4 -4
  246. data/lib/openc3/models/queue_model.rb +0 -0
  247. data/lib/openc3/models/reaction_model.rb +0 -0
  248. data/lib/openc3/models/reingest_job_model.rb +0 -0
  249. data/lib/openc3/models/router_model.rb +0 -0
  250. data/lib/openc3/models/router_status_model.rb +0 -0
  251. data/lib/openc3/models/scope_model.rb +51 -26
  252. data/lib/openc3/models/script_engine_model.rb +0 -0
  253. data/lib/openc3/models/script_status_model.rb +0 -0
  254. data/lib/openc3/models/secret_model.rb +0 -0
  255. data/lib/openc3/models/setting_model.rb +546 -3
  256. data/lib/openc3/models/sorted_model.rb +0 -0
  257. data/lib/openc3/models/stash_model.rb +0 -0
  258. data/lib/openc3/models/target_model.rb +30 -25
  259. data/lib/openc3/models/timeline_model.rb +0 -0
  260. data/lib/openc3/models/tool_config_model.rb +0 -0
  261. data/lib/openc3/models/tool_model.rb +1 -0
  262. data/lib/openc3/models/trigger_group_model.rb +0 -0
  263. data/lib/openc3/models/trigger_model.rb +20 -0
  264. data/lib/openc3/models/widget_model.rb +0 -0
  265. data/lib/openc3/operators/microservice_operator.rb +64 -22
  266. data/lib/openc3/operators/operator.rb +4 -4
  267. data/lib/openc3/packets/binary_accessor.rb +0 -0
  268. data/lib/openc3/packets/command_validator.rb +0 -0
  269. data/lib/openc3/packets/commands.rb +0 -0
  270. data/lib/openc3/packets/json_packet.rb +0 -0
  271. data/lib/openc3/packets/limits.rb +0 -0
  272. data/lib/openc3/packets/limits_response.rb +0 -0
  273. data/lib/openc3/packets/packet.rb +4 -0
  274. data/lib/openc3/packets/packet_config.rb +0 -0
  275. data/lib/openc3/packets/packet_item.rb +0 -0
  276. data/lib/openc3/packets/packet_item_limits.rb +0 -0
  277. data/lib/openc3/packets/parsers/format_string_parser.rb +0 -0
  278. data/lib/openc3/packets/parsers/limits_parser.rb +0 -0
  279. data/lib/openc3/packets/parsers/limits_response_parser.rb +0 -0
  280. data/lib/openc3/packets/parsers/packet_item_parser.rb +0 -0
  281. data/lib/openc3/packets/parsers/packet_parser.rb +0 -0
  282. data/lib/openc3/packets/parsers/processor_parser.rb +0 -0
  283. data/lib/openc3/packets/parsers/state_parser.rb +0 -0
  284. data/lib/openc3/packets/parsers/xtce_converter.rb +0 -0
  285. data/lib/openc3/packets/parsers/xtce_parser.rb +0 -0
  286. data/lib/openc3/packets/structure.rb +7 -1
  287. data/lib/openc3/packets/structure_item.rb +0 -0
  288. data/lib/openc3/packets/telemetry.rb +0 -0
  289. data/lib/openc3/processors/processor.rb +2 -0
  290. data/lib/openc3/processors/statistics_processor.rb +0 -0
  291. data/lib/openc3/processors/watermark_processor.rb +0 -0
  292. data/lib/openc3/processors.rb +0 -0
  293. data/lib/openc3/script/api_shared.rb +0 -0
  294. data/lib/openc3/script/autonomic.rb +12 -12
  295. data/lib/openc3/script/calendar.rb +0 -0
  296. data/lib/openc3/script/commands.rb +0 -0
  297. data/lib/openc3/script/critical_cmd.rb +0 -0
  298. data/lib/openc3/script/exceptions.rb +0 -0
  299. data/lib/openc3/script/extract.rb +0 -0
  300. data/lib/openc3/script/limits.rb +0 -0
  301. data/lib/openc3/script/metadata.rb +0 -0
  302. data/lib/openc3/script/packages.rb +0 -0
  303. data/lib/openc3/script/plugins.rb +0 -0
  304. data/lib/openc3/script/queue.rb +0 -0
  305. data/lib/openc3/script/screen.rb +0 -0
  306. data/lib/openc3/script/script.rb +2 -2
  307. data/lib/openc3/script/script_runner.rb +0 -0
  308. data/lib/openc3/script/storage.rb +4 -4
  309. data/lib/openc3/script/suite.rb +0 -0
  310. data/lib/openc3/script/suite_results.rb +0 -0
  311. data/lib/openc3/script/suite_runner.rb +26 -0
  312. data/lib/openc3/script/tables.rb +0 -0
  313. data/lib/openc3/script/telemetry.rb +9 -3
  314. data/lib/openc3/script/web_socket_api.rb +218 -145
  315. data/lib/openc3/script.rb +0 -0
  316. data/lib/openc3/script_engines/script_engine.rb +0 -0
  317. data/lib/openc3/streams/mqtt_stream.rb +0 -0
  318. data/lib/openc3/streams/serial_stream.rb +0 -0
  319. data/lib/openc3/streams/stream.rb +0 -0
  320. data/lib/openc3/streams/tcpip_client_stream.rb +0 -0
  321. data/lib/openc3/streams/tcpip_socket_stream.rb +0 -0
  322. data/lib/openc3/streams/web_socket_client_stream.rb +0 -0
  323. data/lib/openc3/subpacketizers/subpacketizer.rb +0 -0
  324. data/lib/openc3/system/system.rb +0 -0
  325. data/lib/openc3/system/target.rb +0 -0
  326. data/lib/openc3/system.rb +0 -0
  327. data/lib/openc3/tools/cmd_tlm_server/api.rb +0 -0
  328. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +7 -0
  329. data/lib/openc3/tools/table_manager/table.rb +0 -0
  330. data/lib/openc3/tools/table_manager/table_config.rb +0 -0
  331. data/lib/openc3/tools/table_manager/table_item.rb +0 -0
  332. data/lib/openc3/tools/table_manager/table_item_parser.rb +0 -0
  333. data/lib/openc3/tools/table_manager/table_manager_core.rb +0 -0
  334. data/lib/openc3/tools/table_manager/table_parser.rb +0 -0
  335. data/lib/openc3/tools/test_runner/test.rb +0 -0
  336. data/lib/openc3/top_level.rb +1 -1
  337. data/lib/openc3/topics/autonomic_topic.rb +0 -0
  338. data/lib/openc3/topics/calendar_topic.rb +0 -0
  339. data/lib/openc3/topics/command_decom_topic.rb +0 -0
  340. data/lib/openc3/topics/command_topic.rb +0 -0
  341. data/lib/openc3/topics/config_topic.rb +0 -0
  342. data/lib/openc3/topics/decom_interface_topic.rb +2 -1
  343. data/lib/openc3/topics/interface_topic.rb +8 -2
  344. data/lib/openc3/topics/limits_event_topic.rb +0 -0
  345. data/lib/openc3/topics/notebook_topic.rb +0 -0
  346. data/lib/openc3/topics/queue_topic.rb +0 -0
  347. data/lib/openc3/topics/router_topic.rb +6 -1
  348. data/lib/openc3/topics/system_events_topic.rb +0 -0
  349. data/lib/openc3/topics/telemetry_decom_topic.rb +0 -0
  350. data/lib/openc3/topics/telemetry_topic.rb +0 -0
  351. data/lib/openc3/topics/timeline_topic.rb +0 -0
  352. data/lib/openc3/topics/topic.rb +0 -0
  353. data/lib/openc3/utilities/authentication.rb +4 -4
  354. data/lib/openc3/utilities/authorization.rb +0 -0
  355. data/lib/openc3/utilities/aws_bucket.rb +2 -2
  356. data/lib/openc3/utilities/bucket.rb +1 -1
  357. data/lib/openc3/utilities/bucket_file_cache.rb +55 -22
  358. data/lib/openc3/utilities/bucket_require.rb +0 -0
  359. data/lib/openc3/utilities/bucket_utilities.rb +3 -4
  360. data/lib/openc3/utilities/cli_generator.rb +0 -0
  361. data/lib/openc3/utilities/cmd_log.rb +0 -0
  362. data/lib/openc3/utilities/config_overlay.rb +75 -0
  363. data/lib/openc3/utilities/cosmos_rails_formatter.rb +0 -0
  364. data/lib/openc3/utilities/crc.rb +59 -32
  365. data/lib/openc3/utilities/csv.rb +0 -0
  366. data/lib/openc3/utilities/ctrf.rb +0 -0
  367. data/lib/openc3/utilities/env_helper.rb +0 -0
  368. data/lib/openc3/utilities/local_bucket.rb +0 -0
  369. data/lib/openc3/utilities/local_mode.rb +32 -6
  370. data/lib/openc3/utilities/logger.rb +30 -8
  371. data/lib/openc3/utilities/message_log.rb +0 -0
  372. data/lib/openc3/utilities/metric.rb +0 -0
  373. data/lib/openc3/utilities/migration.rb +0 -0
  374. data/lib/openc3/utilities/open_telemetry.rb +0 -0
  375. data/lib/openc3/utilities/process_manager.rb +0 -0
  376. data/lib/openc3/utilities/pypi_url.rb +0 -0
  377. data/lib/openc3/utilities/python_proxy.rb +0 -0
  378. data/lib/openc3/utilities/python_venv.rb +135 -0
  379. data/lib/openc3/utilities/quaternion.rb +0 -0
  380. data/lib/openc3/utilities/questdb_client.rb +0 -0
  381. data/lib/openc3/utilities/redis_secrets.rb +0 -0
  382. data/lib/openc3/utilities/reingest_job.rb +0 -0
  383. data/lib/openc3/utilities/ruby_lex_utils.rb +0 -0
  384. data/lib/openc3/utilities/rubygems_url.rb +39 -0
  385. data/lib/openc3/utilities/running_script.rb +24 -55
  386. data/lib/openc3/utilities/s3_autoload.rb +5 -5
  387. data/lib/openc3/utilities/script.rb +12 -8
  388. data/lib/openc3/utilities/secrets.rb +48 -3
  389. data/lib/openc3/utilities/simulated_target.rb +0 -0
  390. data/lib/openc3/utilities/sleeper.rb +0 -0
  391. data/lib/openc3/utilities/store.rb +0 -0
  392. data/lib/openc3/utilities/store_autoload.rb +2 -2
  393. data/lib/openc3/utilities/store_queued.rb +0 -0
  394. data/lib/openc3/utilities/target_file.rb +18 -1
  395. data/lib/openc3/utilities/thread_manager.rb +0 -0
  396. data/lib/openc3/utilities/throttle.rb +0 -0
  397. data/lib/openc3/utilities/zip.rb +0 -0
  398. data/lib/openc3/utilities.rb +0 -0
  399. data/lib/openc3/version.rb +5 -5
  400. data/lib/openc3/win32/excel.rb +0 -0
  401. data/lib/openc3/win32/win32.rb +0 -0
  402. data/lib/openc3/win32/win32_main.rb +0 -0
  403. data/lib/openc3.rb +2 -2
  404. data/tasks/gemfile_stats.rake +0 -0
  405. data/tasks/spec.rake +0 -0
  406. data/templates/command_validator/command_validator.py +0 -0
  407. data/templates/command_validator/command_validator.rb +0 -0
  408. data/templates/conversion/conversion.py +0 -0
  409. data/templates/conversion/conversion.rb +0 -0
  410. data/templates/limits_response/response.py +0 -0
  411. data/templates/limits_response/response.rb +0 -0
  412. data/templates/microservice/microservices/TEMPLATE/microservice.py +0 -0
  413. data/templates/microservice/microservices/TEMPLATE/microservice.rb +0 -0
  414. data/templates/plugin/.gitignore +0 -0
  415. data/templates/plugin/LICENSE.md +0 -0
  416. data/templates/plugin/README.md +0 -0
  417. data/templates/plugin/Rakefile +0 -0
  418. data/templates/plugin/plugin.gemspec +0 -0
  419. data/templates/plugin/plugin.txt +0 -0
  420. data/templates/processor/processor.py +1 -1
  421. data/templates/processor/processor.rb +0 -0
  422. data/templates/target/targets/TARGET/cmd_tlm/cmd.txt +0 -0
  423. data/templates/target/targets/TARGET/cmd_tlm/tlm.txt +0 -0
  424. data/templates/target/targets/TARGET/lib/target.py +0 -0
  425. data/templates/target/targets/TARGET/lib/target.rb +0 -0
  426. data/templates/target/targets/TARGET/procedures/procedure.py +0 -0
  427. data/templates/target/targets/TARGET/procedures/procedure.rb +0 -0
  428. data/templates/target/targets/TARGET/public/README.txt +0 -0
  429. data/templates/target/targets/TARGET/screens/status.txt +0 -0
  430. data/templates/target/targets/TARGET/target.txt +0 -0
  431. data/templates/tool_angular/.editorconfig +0 -0
  432. data/templates/tool_angular/.gitignore +0 -0
  433. data/templates/tool_angular/.vscode/extensions.json +0 -0
  434. data/templates/tool_angular/.vscode/launch.json +0 -0
  435. data/templates/tool_angular/.vscode/tasks.json +0 -0
  436. data/templates/tool_angular/angular.json +0 -0
  437. data/templates/tool_angular/extra-webpack.config.js +0 -0
  438. data/templates/tool_angular/package.json +2 -2
  439. data/templates/tool_angular/src/app/app-routing.module.ts +0 -0
  440. data/templates/tool_angular/src/app/app.component.html +0 -0
  441. data/templates/tool_angular/src/app/app.component.scss +0 -0
  442. data/templates/tool_angular/src/app/app.component.spec.ts +0 -0
  443. data/templates/tool_angular/src/app/app.component.ts +0 -0
  444. data/templates/tool_angular/src/app/app.module.ts +0 -0
  445. data/templates/tool_angular/src/app/custom-overlay-container.ts +0 -0
  446. data/templates/tool_angular/src/app/empty-route/empty-route.component.ts +0 -0
  447. data/templates/tool_angular/src/app/openc3-api.d.ts +0 -0
  448. data/templates/tool_angular/src/assets/.gitkeep +0 -0
  449. data/templates/tool_angular/src/environments/environment.prod.ts +0 -0
  450. data/templates/tool_angular/src/environments/environment.ts +0 -0
  451. data/templates/tool_angular/src/favicon.ico +0 -0
  452. data/templates/tool_angular/src/index.html +0 -0
  453. data/templates/tool_angular/src/main.single-spa.ts +0 -0
  454. data/templates/tool_angular/src/single-spa/asset-url.ts +0 -0
  455. data/templates/tool_angular/src/single-spa/single-spa-props.ts +0 -0
  456. data/templates/tool_angular/src/styles.scss +0 -0
  457. data/templates/tool_angular/tsconfig.app.json +0 -0
  458. data/templates/tool_angular/tsconfig.json +0 -0
  459. data/templates/tool_angular/tsconfig.spec.json +0 -0
  460. data/templates/tool_react/.eslintrc +0 -0
  461. data/templates/tool_react/.gitignore +0 -0
  462. data/templates/tool_react/.prettierignore +0 -0
  463. data/templates/tool_react/babel.config.json +0 -0
  464. data/templates/tool_react/jest.config.js +0 -0
  465. data/templates/tool_react/package.json +1 -1
  466. data/templates/tool_react/src/openc3-tool_name.js +0 -0
  467. data/templates/tool_react/src/root.component.js +0 -0
  468. data/templates/tool_react/src/root.component.test.js +0 -0
  469. data/templates/tool_react/webpack.config.js +0 -0
  470. data/templates/tool_svelte/.gitignore +0 -0
  471. data/templates/tool_svelte/.prettierignore +0 -0
  472. data/templates/tool_svelte/.prettierrc.js +0 -0
  473. data/templates/tool_svelte/babel.config.js +0 -0
  474. data/templates/tool_svelte/build/smui.css +0 -0
  475. data/templates/tool_svelte/jest.config.js +0 -0
  476. data/templates/tool_svelte/package.json +1 -1
  477. data/templates/tool_svelte/rollup.config.js +0 -0
  478. data/templates/tool_svelte/src/App.svelte +0 -0
  479. data/templates/tool_svelte/src/App.test.js +0 -0
  480. data/templates/tool_svelte/src/theme/_smui-theme.scss +0 -0
  481. data/templates/tool_svelte/src/tool_name.js +0 -0
  482. data/templates/tool_vue/.env.standalone +0 -0
  483. data/templates/tool_vue/.gitignore +0 -0
  484. data/templates/tool_vue/.prettierrc.cjs +0 -0
  485. data/templates/tool_vue/eslint.config.mjs +0 -0
  486. data/templates/tool_vue/jsconfig.json +0 -0
  487. data/templates/tool_vue/package.json +3 -3
  488. data/templates/tool_vue/src/App.vue +0 -0
  489. data/templates/tool_vue/src/main.js +0 -0
  490. data/templates/tool_vue/src/router.js +0 -0
  491. data/templates/tool_vue/src/tools/tool_name/tool_name.vue +0 -0
  492. data/templates/tool_vue/vite.config.js +1 -1
  493. data/templates/widget/.prettierrc.cjs +0 -0
  494. data/templates/widget/LICENSE.md +0 -0
  495. data/templates/widget/Rakefile +0 -0
  496. data/templates/widget/package.json +2 -2
  497. data/templates/widget/src/Widget.vue +0 -0
  498. data/templates/widget/vite.config.js +0 -0
  499. metadata +36 -9
@@ -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,6 +270,7 @@ 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
@@ -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
@@ -146,16 +150,15 @@ module OpenC3
146
150
  python_bin = ENV['OPENC3_PYTHON_BIN'] || '/openc3/python/.venv/bin/python'
147
151
  result = { 'OPENC3_PYTHON_BIN' => python_bin }
148
152
  if @needs_dependencies && @plugin
149
- sanitized_name = "#{@scope}__#{@plugin}".tr('^a-zA-Z0-9_-', '_')
150
- candidate = "/gems/plugin_venvs/#{sanitized_name}/.venv"
151
- if File.directory?(candidate)
153
+ candidate = PythonVenv.plugin_venv_path(scope: @scope, plugin_name: @plugin)
154
+ if candidate
152
155
  result['VIRTUAL_ENV'] = candidate
153
156
  result['PYTHONUSERBASE'] = candidate
154
157
  site_packages = Dir.glob("#{candidate}/lib/python*/site-packages").first
155
158
  result['PYTHONPATH'] = site_packages
156
159
  else
157
160
  result['VIRTUAL_ENV'] = nil
158
- result['PYTHONUSERBASE'] = '/gems/python_packages'
161
+ result['PYTHONUSERBASE'] = PythonVenv::DEFAULT_PYTHONUSERBASE
159
162
  end
160
163
  end
161
164
  result
@@ -196,24 +199,7 @@ module OpenC3
196
199
  parser.verify_num_parameters(1, 1, "#{keyword} <Dir>")
197
200
  @work_dir = parameters[0]
198
201
  when 'PORT'
199
- usage = "PORT <Number> <Protocol (Optional)"
200
- parser.verify_num_parameters(1, 2, usage)
201
- begin
202
- @ports << [Integer(parameters[0])]
203
- rescue # In case Integer fails
204
- raise ConfigParser::Error.new(parser, "Port must be an integer: #{parameters[0]}", usage)
205
- end
206
- protocol = ConfigParser.handle_nil(parameters[1])
207
- if protocol
208
- # Per https://kubernetes.io/docs/concepts/services-networking/service/#protocol-support
209
- if %w(TCP UDP SCTP).include?(protocol.upcase)
210
- @ports[-1] << protocol.upcase
211
- else
212
- raise ConfigParser::Error.new(parser, "Unknown port protocol: #{parameters[1]}", usage)
213
- end
214
- else
215
- @ports[-1] << 'TCP'
216
- end
202
+ parse_port(parser, keyword, parameters)
217
203
  when 'TOPIC'
218
204
  parser.verify_num_parameters(1, 1, "#{keyword} <Topic Name>")
219
205
  @topics << parameters[0]
@@ -244,6 +230,7 @@ module OpenC3
244
230
  @container = parameters[0]
245
231
  when 'SECRET'
246
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)
247
234
  if ConfigParser.handle_nil(parameters[3])
248
235
  @secrets << parameters.dup
249
236
  else
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -19,9 +19,11 @@ require 'rubygems'
19
19
  require 'rubygems/package'
20
20
  require 'openc3'
21
21
  require 'openc3/utilities/bucket'
22
+ require 'openc3/utilities/python_venv'
22
23
  require 'openc3/utilities/store'
23
24
  require 'openc3/config/config_parser'
24
25
  require 'openc3/models/model'
26
+ require 'openc3/models/scope_model'
25
27
  require 'openc3/models/gem_model'
26
28
  require 'openc3/models/target_model'
27
29
  require 'openc3/models/interface_model'
@@ -291,7 +293,12 @@ module OpenC3
291
293
  uv_args = [plugin_venv_name, gem_path] + pypi_args
292
294
  output, status = Open3.capture2e("/openc3/bin/uvinstall", *uv_args)
293
295
  puts output
294
- unless status.success?
296
+ if status.success?
297
+ # A plugin that declares openc3 itself would shadow the system
298
+ # library once its site-packages goes on PYTHONPATH, so drop it
299
+ # here rather than let the wrong client reach a running script.
300
+ PythonVenv.purge_reserved_packages(File.join('/gems', 'plugin_venvs', plugin_venv_name, '.venv'))
301
+ else
295
302
  Logger.warn "UV per-plugin install failed, falling back to shared pipinstall"
296
303
  uv_installed = false
297
304
  end
@@ -596,7 +603,7 @@ module OpenC3
596
603
  Logger.error("Failed to retrieve pypi_url: #{e.formatted}")
597
604
  ensure
598
605
  if pypi_url.nil?
599
- pypi_url = ENV['PYPI_URL']
606
+ pypi_url = ENV.fetch('PYPI_URL', nil)
600
607
  pypi_url += '/simple' if pypi_url
601
608
  pypi_url ||= PypiUrl::DEFAULT
602
609
  end
File without changes
File without changes
@@ -188,7 +188,7 @@ module OpenC3
188
188
  end
189
189
 
190
190
  def self.get(name)
191
- path = "#{ENV['PYTHONUSERBASE']}/cache"
191
+ path = "#{ENV.fetch('PYTHONUSERBASE', nil)}/cache"
192
192
  FileUtils.mkdir_p(path) unless Dir.exist?(path)
193
193
  result = Pathname.new(path).children.select { |c| c.file? and File.basename(c, File.extname(c)) == name }
194
194
  if result.length > 0
@@ -200,8 +200,8 @@ module OpenC3
200
200
  def self.put(package_file_path, package_install: true, scope:, plugin: nil)
201
201
  if File.file?(package_file_path)
202
202
  package_filename = File.basename(package_file_path)
203
- FileUtils.mkdir_p("#{ENV['PYTHONUSERBASE']}/cache") unless Dir.exist?("#{ENV['PYTHONUSERBASE']}/cache")
204
- cache_path = "#{ENV['PYTHONUSERBASE']}/cache/#{File.basename(package_file_path)}"
203
+ FileUtils.mkdir_p("#{ENV.fetch('PYTHONUSERBASE')}/cache") unless Dir.exist?("#{ENV.fetch('PYTHONUSERBASE')}/cache")
204
+ cache_path = "#{ENV.fetch('PYTHONUSERBASE')}/cache/#{File.basename(package_file_path)}"
205
205
  FileUtils.cp(package_file_path, cache_path)
206
206
 
207
207
  # Copy uploaded .whl files to the UV uploads directory so they appear
@@ -244,7 +244,7 @@ module OpenC3
244
244
  ensure
245
245
  if pypi_url.nil?
246
246
  # If Redis isn't running try the ENV, then simply pypi.org/simple
247
- pypi_url = ENV['PYPI_URL']
247
+ pypi_url = ENV.fetch('PYPI_URL', nil)
248
248
  if pypi_url
249
249
  pypi_url += '/simple'
250
250
  end
File without changes
File without changes
File without changes
File without changes
File without changes