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
@@ -30,6 +30,7 @@ require 'openc3/topics/config_topic'
30
30
  require 'openc3/system'
31
31
  require 'openc3/utilities/local_mode'
32
32
  require 'openc3/utilities/bucket'
33
+ require 'openc3/utilities/target_file'
33
34
  require 'openc3/utilities/zip'
34
35
  require 'fileutils'
35
36
  require 'ostruct'
@@ -50,6 +51,7 @@ module OpenC3
50
51
  ERB_EXTENSIONS = %w(.txt .rb .py .json .yaml .yml)
51
52
  ITEM_MAP_CACHE_TIMEOUT = 10.0
52
53
  PACKET_CACHE_TIMEOUT = 10.0
54
+ MAX_COLUMN_HEADER_LENGTH = 127 # QuestDB column header limit for item/param names
53
55
  @@item_map_cache = {}
54
56
  @@packet_cache = {}
55
57
  @@packet_cache_mutex = Mutex.new
@@ -76,6 +78,7 @@ module OpenC3
76
78
  attr_accessor :tlm_log_retain_time
77
79
  attr_accessor :cmd_decom_retain_time
78
80
  attr_accessor :tlm_decom_retain_time
81
+ attr_accessor :decom_flush_period
79
82
  attr_accessor :cleanup_poll_time
80
83
  attr_accessor :needs_dependencies
81
84
  attr_accessor :target_microservices
@@ -103,49 +106,123 @@ module OpenC3
103
106
  targets = self.all(scope: scope)
104
107
  targets.each { |_target_name, target| target['modified'] = false }
105
108
 
109
+ modified_targets = []
106
110
  if ENV['OPENC3_LOCAL_MODE']
107
- modified_targets = OpenC3::LocalMode.modified_targets(scope: scope)
108
- modified_targets.each do |target_name|
109
- targets[target_name]['modified'] = true if targets[target_name]
110
- end
111
- else
112
- modified_targets = Bucket.getClient().list_files(bucket: ENV['OPENC3_CONFIG_BUCKET'], path: "DEFAULT/targets_modified/", only_directories: true)
113
- modified_targets.each do |target_name|
114
- # A target could have been deleted without removing the modified files
115
- # Thus we have to check for the existence of the target_name key
116
- if targets.has_key?(target_name)
117
- targets[target_name]['modified'] = true
118
- end
119
- end
111
+ modified_targets += OpenC3::LocalMode.modified_targets(scope: scope)
112
+ end
113
+ # Always list the bucket, even in local mode, since the local directory
114
+ # only holds what COSMOS itself wrote there
115
+ modified_targets += Bucket.getClient().list_files(bucket: ENV['OPENC3_CONFIG_BUCKET'], path: "#{scope}/targets_modified/", only_directories: true)
116
+ modified_targets.each do |target_name|
117
+ # A target could have been deleted without removing the modified files
118
+ # Thus we have to check for the existence of the target_name key
119
+ targets[target_name]['modified'] = true if targets[target_name]
120
120
  end
121
121
  # Sort (which turns hash to array) and return hash
122
122
  # This enables a consistent listing of the targets
123
123
  targets.sort.to_h
124
124
  end
125
125
 
126
+ # Splits a plugin instance name ("openc3-cosmos-demo-7.2.0.gem__0") into
127
+ # [base_name, version] (["openc3-cosmos-demo", "7.2.0"]), or nil when the
128
+ # name doesn't carry a version segment. Drops the "__N" install-instance
129
+ # suffix and the ".gem" extension first.
130
+ def self.plugin_name_version(plugin_instance)
131
+ return nil if plugin_instance.nil? || plugin_instance.empty?
132
+ gem = plugin_instance.split('__')[0].sub(/\.gem\z/, '')
133
+ parts = gem.split('-')
134
+ return nil if parts.length < 2
135
+ [parts[0..-2].join('-'), parts[-1]]
136
+ end
137
+
138
+ # "name version" of the plugin that installed this target (e.g.
139
+ # "openc3-cosmos-demo 7.2.0"), derived from the plugin instance name
140
+ # ("openc3-cosmos-demo-7.2.0.gem__0"), or nil if unavailable. Used to
141
+ # annotate the Version History baseline with the file's origin.
142
+ def self.plugin_version_label(target_name, scope:)
143
+ model = get(name: target_name, scope: scope)
144
+ name, version = plugin_name_version(model && model['plugin'])
145
+ return nil unless name
146
+ "#{name} #{version}"
147
+ rescue
148
+ nil
149
+ end
150
+
151
+ # Version-stripped plugin base name that owns this target, e.g.
152
+ # "openc3-cosmos-demo" from instance "openc3-cosmos-demo-7.2.0.gem__0".
153
+ # Used as the per-plugin Version History repo key: stable across upgrades
154
+ # (only the version segment changes), so history survives version bumps.
155
+ # Returns nil when the target has no owning plugin (e.g. a hand-created
156
+ # target); callers fall back to a no-plugin bucket.
157
+ def self.plugin_base_name(target_name, scope:)
158
+ model = get(name: target_name, scope: scope)
159
+ name, _version = plugin_name_version(model && model['plugin'])
160
+ name
161
+ rescue
162
+ nil
163
+ end
164
+
165
+ # Uninstall-only: remove a plugin's entire Version History repo. Called
166
+ # from the CLI uninstall path with the plugin instance name; upgrade reuses
167
+ # the repo and must never call this. No-op in Core builds (or whenever
168
+ # OPENC3_VERSION_HISTORY_DIR is unset) since VersionStore is absent or
169
+ # disabled. Best-effort: a failure here must not abort the uninstall.
170
+ def self.destroy_script_versions(plugin_instance_name, scope:)
171
+ name, _version = plugin_name_version(plugin_instance_name)
172
+ return unless name
173
+ begin
174
+ require 'openc3-enterprise/utilities/version_store'
175
+ rescue LoadError
176
+ return
177
+ end
178
+ ::VersionStore.destroy_repo(scope: scope, plugin: name)
179
+ rescue => e
180
+ Logger.warn("destroy_script_versions failed for #{scope}/#{plugin_instance_name}: #{e.message}")
181
+ end
182
+
126
183
  # Given target's modified file list
184
+ # Returns the modified files as names relative to the scope,
185
+ # i.e. "INST/procedures/new.rb"
127
186
  def self.modified_files(target_name, scope:)
128
187
  modified = []
129
188
 
130
189
  if ENV['OPENC3_LOCAL_MODE']
131
- modified = OpenC3::LocalMode.modified_files(target_name, scope: scope)
132
- else
133
- resp = Bucket.getClient().list_objects(
134
- bucket: ENV['OPENC3_CONFIG_BUCKET'],
135
- # The trailing slash is important!
136
- prefix: "#{scope}/targets_modified/#{target_name}/",
137
- )
138
- resp.each do |item|
139
- # Results look like DEFAULT/targets_modified/INST/procedures/new.rb
140
- # so split on '/' and ignore the first two values
141
- modified << item.key.split('/')[2..-1].join('/')
190
+ # LocalMode reports target relative paths, i.e. "procedures/new.rb",
191
+ # so add the target name to match the bucket listing below
192
+ modified += OpenC3::LocalMode.modified_files(target_name, scope: scope).map do |file_path|
193
+ "#{target_name}/#{file_path}"
142
194
  end
143
195
  end
196
+ # Always list the bucket, even in local mode. The local directory only
197
+ # holds what COSMOS itself wrote there, so a file placed directly in the
198
+ # bucket is modified but has no local copy.
199
+ resp = Bucket.getClient().list_objects(
200
+ bucket: ENV['OPENC3_CONFIG_BUCKET'],
201
+ # The trailing slash is important!
202
+ prefix: "#{scope}/targets_modified/#{target_name}/",
203
+ )
204
+ resp.each do |item|
205
+ # Results look like DEFAULT/targets_modified/INST/procedures/new.rb
206
+ # so split on '/' and ignore the first two values
207
+ modified << item.key.split('/')[2..-1].join('/')
208
+ end
144
209
  # Sort to enable a consistent listing of the modified files
145
- modified.sort
210
+ modified.uniq.sort
146
211
  end
147
212
 
148
- def self.delete_modified(target_name, scope:)
213
+ # files: optional list of specific modified files to delete, each a name
214
+ # relative to scope ("TARGET/sub/path", matching modified_files output).
215
+ # When given, only those files are removed (used by plugin upgrade to drop
216
+ # some modified files while keeping others); when nil, every modified file
217
+ # for the target is removed (the original behavior).
218
+ def self.delete_modified(target_name, scope:, files: nil)
219
+ if files && !files.empty?
220
+ files.each do |name|
221
+ # TargetFile.destroy handles both local mode and the bucket.
222
+ OpenC3::TargetFile.destroy(scope, name)
223
+ end
224
+ return
225
+ end
149
226
  if ENV['OPENC3_LOCAL_MODE']
150
227
  OpenC3::LocalMode.delete_modified(target_name, scope: scope)
151
228
  end
@@ -224,7 +301,7 @@ module OpenC3
224
301
 
225
302
  # Assume it exists and just try to get it to avoid an extra call to Store.exist?
226
303
  json = store_for_target(target_name, scope: scope).hget("#{scope}__openc3#{type.to_s.downcase}__#{target_name}", packet_name)
227
- raise "Packet '#{target_name} #{packet_name}' does not exist" if json.nil?
304
+ raise "Packet definition '#{target_name} #{packet_name}' does not exist" if json.nil?
228
305
 
229
306
  packet = JSON.parse(json, allow_nan: true, create_additions: true)
230
307
 
@@ -245,7 +322,7 @@ module OpenC3
245
322
  # @return [Array<Hash>] All packet hashes under the target_name
246
323
  def self.packets(target_name, type: :TLM, scope:)
247
324
  raise "Unknown type #{type} for #{target_name}" unless VALID_TYPES.include?(type)
248
- raise "Target '#{target_name}' does not exist for scope: #{scope}" unless get(name: target_name, scope: scope)
325
+ raise "Target '#{target_name}' does not exist for scope: #{scope} (TargetModel)" unless get(name: target_name, scope: scope)
249
326
 
250
327
  result = []
251
328
  packets = store_for_target(target_name, scope: scope).hgetall("#{scope}__openc3#{type.to_s.downcase}__#{target_name}")
@@ -281,7 +358,7 @@ module OpenC3
281
358
  def self.packet_item(target_name, packet_name, item_name, type: :TLM, scope:)
282
359
  packet = packet(target_name, packet_name, type: type, scope: scope)
283
360
  item = packet['items'].find { |item| item['name'] == item_name.to_s }
284
- raise "Item '#{packet['target_name']} #{packet['packet_name']} #{item_name}' does not exist" unless item
361
+ raise "Item '#{packet['target_name']} #{packet['packet_name']} #{item_name}' does not exist (TargetModel)" unless item
285
362
  item
286
363
  end
287
364
 
@@ -400,6 +477,7 @@ module OpenC3
400
477
  tlm_log_retain_time: nil,
401
478
  cmd_decom_retain_time: nil,
402
479
  tlm_decom_retain_time: nil,
480
+ decom_flush_period: 5.0,
403
481
  cleanup_poll_time: 3600,
404
482
  needs_dependencies: false,
405
483
  target_microservices: {},
@@ -428,6 +506,7 @@ module OpenC3
428
506
  @tlm_log_retain_time = tlm_log_retain_time
429
507
  @cmd_decom_retain_time = cmd_decom_retain_time
430
508
  @tlm_decom_retain_time = tlm_decom_retain_time
509
+ @decom_flush_period = decom_flush_period
431
510
  @cleanup_poll_time = cleanup_poll_time
432
511
  @needs_dependencies = needs_dependencies
433
512
  @target_microservices = target_microservices
@@ -461,6 +540,7 @@ module OpenC3
461
540
  'tlm_log_retain_time' => @tlm_log_retain_time,
462
541
  'cmd_decom_retain_time' => @cmd_decom_retain_time,
463
542
  'tlm_decom_retain_time' => @tlm_decom_retain_time,
543
+ 'decom_flush_period' => @decom_flush_period,
464
544
  'cleanup_poll_time' => @cleanup_poll_time,
465
545
  'needs_dependencies' => @needs_dependencies,
466
546
  'target_microservices' => @target_microservices.as_json(),
@@ -515,6 +595,9 @@ module OpenC3
515
595
  if @tlm_decom_retain_time and !@tlm_decom_retain_time.match?(/^\d+[hdwMy]$/)
516
596
  raise ConfigParser::Error.new(parser, "TLM_DECOM_RETAIN_TIME must be a number followed by h, d, w, M, or y (e.g., 24h, 7d, 1y)")
517
597
  end
598
+ when 'DECOM_FLUSH_PERIOD'
599
+ parser.verify_num_parameters(1, 1, "#{keyword} <Flush period in seconds>")
600
+ @decom_flush_period = Float(parameters[0])
518
601
  when 'REDUCED_MINUTE_LOG_RETAIN_TIME', 'REDUCED_HOUR_LOG_RETAIN_TIME', 'REDUCED_DAY_LOG_RETAIN_TIME', 'REDUCED_LOG_RETAIN_TIME'
519
602
  # DEPRECATED
520
603
  when 'REDUCER_DISABLE', 'REDUCER_DISABLED', 'REDUCER_MAX_CPU_UTILIZATION', 'REDUCED_MAX_CPU_UTILIZATION'
@@ -576,11 +659,23 @@ module OpenC3
576
659
  return nil
577
660
  end
578
661
 
579
- def deploy(gem_path, variables, validate_only: false)
662
+ # upgrade_context (plugin upgrades only) drives two modes, both keyed by
663
+ # the deployed file name "TARGET/sub/path" (matches modified_files output):
664
+ # { diff_collector: [] } — dry run (with validate_only): append the names
665
+ # of modified files whose live content differs from the rendered plugin
666
+ # content. Read-only; used to warn before an upgrade.
667
+ # { username:, plugin: "name version", version_files: [<name>...] } — for
668
+ # each listed file with a modified copy: record the modified copy in
669
+ # Version History, drop the shadow so the plugin content becomes current,
670
+ # then commit the incoming content as a plugin-upgrade version.
671
+ # nil (the default and Core builds without VersionStore) = no-op.
672
+ def deploy(gem_path, variables, validate_only: false, upgrade_context: nil)
580
673
  variables["target_name"] = @name
581
674
  start_path = "/targets/#{@folder_name}/"
582
675
  temp_dir = Dir.mktmpdir
583
676
  found = false
677
+ diff_collector = upgrade_context && upgrade_context[:diff_collector]
678
+ versioning = !validate_only && upgrade_context && upgrade_context[:version_files] && version_store_available?
584
679
  begin
585
680
  target_path = gem_path + start_path + "**/*"
586
681
  Dir.glob(target_path) do |filename|
@@ -616,15 +711,30 @@ module OpenC3
616
711
  File.open(local_path, 'wb') { |file| file.write(data) }
617
712
  found = true
618
713
  @bucket.put_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: key, body: data) unless validate_only
714
+
715
+ # Plugin upgrade: either collect this file into the diff (dry run) or
716
+ # take it from the plugin while preserving the modified copy in
717
+ # Version History. Both skip files with no modified copy.
718
+ name = "#{@name}/#{target_folder_path}"
719
+ if diff_collector
720
+ collect_modified_diff(name, data, diff_collector)
721
+ elsif versioning && upgrade_context[:version_files].include?(name)
722
+ apply_upgrade_version(name, data, upgrade_context)
723
+ end
619
724
  end
620
725
  raise "No target files found at #{target_path}" unless found
621
726
 
622
727
  target_folder = File.join(temp_dir, @name)
623
728
  # Build a System for just this target
624
729
  system = System.new([@name], temp_dir)
730
+ check_column_header_lengths(system)
625
731
  if variables["xtce_output"]
626
732
  puts "Converting target #{@name} to .xtce files in #{variables["xtce_output"]}/#{@name}"
627
- puts " Using mnemonic '#{variables['time_association_name']}' as the packet time item."
733
+ if variables['time_association_name'].to_s.empty?
734
+ puts " No packet time item given, so no TimeAssociation will be written."
735
+ else
736
+ puts " Using mnemonic '#{variables['time_association_name']}' as the packet time item."
737
+ end
628
738
  system.packet_config.to_xtce(variables["xtce_output"], variables['time_association_name'])
629
739
  end
630
740
  unless validate_only
@@ -638,6 +748,74 @@ module OpenC3
638
748
  end
639
749
  end
640
750
 
751
+ # Version History is an Enterprise feature. The plugin deploy runs in the
752
+ # cmd-tlm-api process, which (unlike script-runner-api) doesn't otherwise
753
+ # require the store, so lazily load it on demand. Returns false in Core
754
+ # builds where the Enterprise gem isn't present.
755
+ def version_store_available?
756
+ return true if defined?(::VersionStore)
757
+ require 'openc3-enterprise/utilities/version_store'
758
+ defined?(::VersionStore) ? true : false
759
+ rescue LoadError
760
+ false
761
+ end
762
+
763
+ # See deploy/upgrade_context. Reads the modified shadow directly (not
764
+ # TargetFile.body, which would fall back to the just-overwritten pristine
765
+ # copy), commits it, removes it, then commits the incoming plugin content.
766
+ # Best-effort: a versioning failure must not abort the plugin upgrade.
767
+ def apply_upgrade_version(name, new_data, ctx)
768
+ modified_key = "#{@scope}/targets_modified/#{name}"
769
+ resp = @bucket.get_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: modified_key)
770
+ return unless resp && resp.body
771
+ old_body = resp.body.read
772
+ # Nothing actually changed — the modification already matches the plugin.
773
+ # Leave the (identical) shadow in place and create no version churn.
774
+ return if old_body.b == new_data.b
775
+ old_body = old_body.force_encoding('UTF-8') unless File.extname(name) == '.bin'
776
+ # Preserve the pre-upgrade (user-modified) content as a version.
777
+ # Idempotent: a no-op when it already matches the latest version.
778
+ ::VersionStore.commit(scope: @scope, name: name, text: old_body)
779
+ # Drop the modified shadow so the plugin file (already written to
780
+ # targets/) becomes the live content.
781
+ OpenC3::TargetFile.destroy(@scope, name)
782
+ # Commit the incoming plugin content as a plugin-upgrade version,
783
+ # attributed to the installing user.
784
+ ::VersionStore.commit(scope: @scope, name: name, text: new_data,
785
+ username: ctx[:username], source: 'plugin-upgrade', plugin: ctx[:plugin])
786
+ rescue => e
787
+ Logger.warn("Version History upgrade capture failed for #{@scope}/#{name}: #{e.message}")
788
+ end
789
+
790
+ # Dry-run companion to apply_upgrade_version: append name to collector when
791
+ # a modified copy exists and differs (by bytes) from the rendered plugin
792
+ # content. Read-only.
793
+ def collect_modified_diff(name, new_data, collector)
794
+ resp = @bucket.get_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: "#{@scope}/targets_modified/#{name}")
795
+ return unless resp && resp.body
796
+ collector << name if resp.body.read.b != new_data.b
797
+ rescue => e
798
+ Logger.warn("Modified diff check failed for #{@scope}/#{name}: #{e.message}")
799
+ end
800
+
801
+ def check_column_header_lengths(system)
802
+ too_long = []
803
+ [system.packet_config.telemetry, system.packet_config.commands].each do |packets_by_target|
804
+ packets_by_target.each do |target_name, packets|
805
+ packets.each do |packet_name, packet|
806
+ packet.sorted_items.each do |item|
807
+ if item.name.length > MAX_COLUMN_HEADER_LENGTH
808
+ too_long << "#{target_name} #{packet_name} #{item.name} (#{item.name.length})"
809
+ end
810
+ end
811
+ end
812
+ end
813
+ end
814
+ unless too_long.empty?
815
+ raise "Item / parameter names must be #{MAX_COLUMN_HEADER_LENGTH} characters or less (QuestDB column header limit). The following are too long:\n #{too_long.join("\n ")}"
816
+ end
817
+ end
818
+
641
819
  def undeploy
642
820
  prefix = "#{@scope}/targets/#{@name}/"
643
821
  objects = @bucket.list_objects(bucket: ENV['OPENC3_CONFIG_BUCKET'], prefix: prefix)
@@ -1032,6 +1210,7 @@ module OpenC3
1032
1210
  options = []
1033
1211
  options << ["CMD_DECOM_RETAIN_TIME", @cmd_decom_retain_time] if @cmd_decom_retain_time
1034
1212
  options << ["TLM_DECOM_RETAIN_TIME", @tlm_decom_retain_time] if @tlm_decom_retain_time
1213
+ options << ["DECOM_FLUSH_PERIOD", @decom_flush_period] if @decom_flush_period
1035
1214
  microservice = MicroserviceModel.new(
1036
1215
  name: microservice_name,
1037
1216
  folder_name: @folder_name,
File without changes
File without changes
@@ -16,6 +16,7 @@
16
16
  # if purchased from OpenC3, Inc.
17
17
 
18
18
  require 'openc3/models/model'
19
+ require 'openc3/topics/config_topic'
19
20
  # require 'openc3/models/scope_model' # Circular require
20
21
  require 'openc3/utilities/bucket'
21
22
  require 'openc3/utilities/bucket_utilities'
@@ -25,6 +26,13 @@ module OpenC3
25
26
  class ToolModel < Model
26
27
  PRIMARY_KEY = 'openc3_tools'
27
28
 
29
+ # Tools are global to the entire COSMOS installation and are always stored
30
+ # in the DEFAULT scope. The frontend navigation always requests the tool
31
+ # list from DEFAULT, so a tool installed into any other scope would be
32
+ # inaccessible. Every scope parameter in this class is therefore ignored
33
+ # and replaced with TOOL_SCOPE.
34
+ TOOL_SCOPE = 'DEFAULT'
35
+
28
36
  attr_accessor :folder_name
29
37
  attr_accessor :icon
30
38
  attr_accessor :url
@@ -39,21 +47,24 @@ module OpenC3
39
47
 
40
48
  # NOTE: The following three class methods are used by the ModelController
41
49
  # and are reimplemented to enable various Model class methods to work
50
+ # NOTE: scope is deprecated since 7.3.0 as tools always live in the DEFAULT scope, see TOOL_SCOPE
42
51
  def self.get(name:, scope: nil)
43
- super("#{scope}__#{PRIMARY_KEY}", name: name)
52
+ super("#{TOOL_SCOPE}__#{PRIMARY_KEY}", name: name)
44
53
  end
45
54
 
55
+ # NOTE: scope is deprecated since 7.3.0 as tools always live in the DEFAULT scope, see TOOL_SCOPE
46
56
  def self.names(scope: nil)
47
57
  array = []
48
- all(scope: scope).each do |name, _tool|
58
+ all().each do |name, _tool|
49
59
  array << name
50
60
  end
51
61
  array
52
62
  end
53
63
 
64
+ # NOTE: scope is deprecated since 7.3.0 as tools always live in the DEFAULT scope, see TOOL_SCOPE
54
65
  def self.all(scope: nil)
55
66
  ordered_array = []
56
- tools = unordered_all(scope: scope)
67
+ tools = unordered_all()
57
68
  tools.each do |_name, tool|
58
69
  ordered_array << tool
59
70
  end
@@ -65,14 +76,10 @@ module OpenC3
65
76
  ordered_hash
66
77
  end
67
78
 
79
+ # Tools only exist in the DEFAULT scope. Kept for backwards compatibility
80
+ # with the ToolsController importmap endpoint.
68
81
  def self.all_scopes
69
- result = {}
70
- scopes = OpenC3::ScopeModel.all
71
- scopes.each do |key, _scope|
72
- tools = unordered_all(scope: key)
73
- result.merge!(tools)
74
- end
75
- result
82
+ unordered_all()
76
83
  end
77
84
 
78
85
  # Called by the PluginModel to allow this class to validate it's top-level keyword: "TOOL"
@@ -80,7 +87,8 @@ module OpenC3
80
87
  case keyword
81
88
  when 'TOOL'
82
89
  parser.verify_num_parameters(2, 2, "TOOL <Folder Name> <Name>")
83
- return self.new(folder_name: parameters[0], name: parameters[1], plugin: plugin, needs_dependencies: needs_dependencies, scope: scope)
90
+ # NOTE: scope is intentionally ignored, see TOOL_SCOPE
91
+ return self.new(folder_name: parameters[0], name: parameters[1], plugin: plugin, needs_dependencies: needs_dependencies, scope: TOOL_SCOPE)
84
92
  else
85
93
  raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Tool: #{keyword} #{parameters.join(" ")}")
86
94
  end
@@ -89,8 +97,10 @@ module OpenC3
89
97
 
90
98
  # The ToolsTab.vue calls the ToolsController which uses this method to reorder the tools
91
99
  # Position is index in the list starting with 0 = first
92
- def self.set_position(name:, position:, scope:)
93
- moving = from_json(get(name: name, scope: scope), scope: scope)
100
+ # NOTE: scope is deprecated since 7.3.0 as tools always live in the DEFAULT scope, see TOOL_SCOPE
101
+ def self.set_position(name:, position:, scope: nil)
102
+ tool_scope = TOOL_SCOPE
103
+ moving = from_json(get(name: name, scope: tool_scope), scope: tool_scope)
94
104
  old_pos = moving.position
95
105
  new_pos = position
96
106
  direction = :down
@@ -101,8 +111,8 @@ module OpenC3
101
111
  end
102
112
 
103
113
  # Go through all the tools and reorder
104
- all(scope: scope).each do |_tool_name, tool|
105
- tool_model = from_json(tool, scope: scope)
114
+ all(scope: tool_scope).each do |_tool_name, tool|
115
+ tool_model = from_json(tool, scope: tool_scope)
106
116
  # Update the requested model to the new position
107
117
  if tool_model.name == name
108
118
  tool_model.position = position
@@ -134,9 +144,11 @@ module OpenC3
134
144
  needs_dependencies: false,
135
145
  disable_erb: nil,
136
146
  import_map_items: nil,
137
- scope:
147
+ scope: nil # NOTE: deprecated since 7.3.0 as tools are always created in the DEFAULT scope, see TOOL_SCOPE
138
148
  )
139
- super("#{scope}__#{PRIMARY_KEY}", name: name, plugin: plugin, updated_at: updated_at, scope: scope)
149
+ # Tools are always created in the DEFAULT scope regardless of the scope
150
+ # the plugin is being installed into, see TOOL_SCOPE
151
+ super("#{TOOL_SCOPE}__#{PRIMARY_KEY}", name: name, plugin: plugin, updated_at: updated_at, scope: TOOL_SCOPE)
140
152
  @folder_name = folder_name
141
153
  @icon = icon
142
154
  @url = url
@@ -156,14 +168,15 @@ module OpenC3
156
168
  end
157
169
 
158
170
  def create(update: false, force: false, queued: false)
159
- tools = self.class.all(scope: @scope)
171
+ tools = self.class.all()
160
172
 
161
173
  # Make sure a tool with this folder_name doesn't already exist
174
+ # NOTE: Tools are global (TOOL_SCOPE) so this check is across all scopes
162
175
  unless update
163
176
  if @folder_name
164
177
  tools.each do |_tool_name, tool|
165
178
  if tool['folder_name'] == @folder_name
166
- raise "Tool with folder_name #{@folder_name} already exists at create"
179
+ raise "Tool with folder_name #{@folder_name} already exists at create. Tools are global to the entire COSMOS installation and can only be installed once."
167
180
  end
168
181
  end
169
182
  end
@@ -308,8 +321,9 @@ module OpenC3
308
321
  ##################################################
309
322
 
310
323
  # Returns the list of tools or the default OpenC3 tool set if no tools have been created
324
+ # NOTE: scope is deprecated since 7.3.0 as tools always live in the DEFAULT scope, see TOOL_SCOPE
311
325
  def self.unordered_all(scope: nil)
312
- tools = Store.hgetall("#{scope}__#{PRIMARY_KEY}")
326
+ tools = Store.hgetall("#{TOOL_SCOPE}__#{PRIMARY_KEY}")
313
327
  tools.each do |key, value|
314
328
  tools[key] = JSON.parse(value, allow_nan: true, create_additions: true)
315
329
  end
File without changes
@@ -50,6 +50,7 @@ module OpenC3
50
50
  STRING_TYPE = 'string'.freeze
51
51
  REGEX_TYPE = 'regex'.freeze
52
52
  TRIGGER_TYPE = 'trigger'.freeze
53
+ ITEM_VALUE_TYPES = ['RAW', 'CONVERTED', 'FORMATTED', 'WITH_UNITS'].freeze
53
54
 
54
55
  def self.create_unique_name(group:, scope:)
55
56
  trigger_names = self.names(group: group, scope: scope)
@@ -180,6 +181,24 @@ module OpenC3
180
181
  end
181
182
  end
182
183
 
184
+ # Validate the items hold a legal valueType and reference telemetry items
185
+ # which actually exist. Only called on create and update, never from initialize.
186
+ # Anything which can reject an already persisted trigger must live here rather than
187
+ # in validate_operand, because validate_operand runs on every from_json
188
+ def validate_trigger_items
189
+ {'left' => @left, 'right' => @right}.each do |side, operand|
190
+ next unless operand.is_a?(Hash) and operand['type'] == ITEM_TYPE
191
+ unless ITEM_VALUE_TYPES.include?(operand['valueType'])
192
+ raise TriggerInputError.new "invalid #{side} operand, valueType '#{operand['valueType']}' must be one of #{ITEM_VALUE_TYPES}"
193
+ end
194
+ begin
195
+ TargetModel.packet_item(operand['target'], operand['packet'], operand['item'], scope: @scope)
196
+ rescue StandardError => e
197
+ raise TriggerInputError.new "invalid #{side} trigger: #{e.message}"
198
+ end
199
+ end
200
+ end
201
+
183
202
  # Validate that all root triggers exist, but do not persist dependent changes yet.
184
203
  # Returns the list of root trigger models that need updating.
185
204
  def validate_roots
@@ -207,6 +226,7 @@ module OpenC3
207
226
  unless Store.hget(@primary_key, @name).nil?
208
227
  raise TriggerInputError.new "existing trigger found: '#{@name}'"
209
228
  end
229
+ validate_trigger_items()
210
230
  models = validate_roots()
211
231
  @updated_at = Time.now.to_nsec_from_epoch
212
232
  Store.hset(@primary_key, @name, JSON.generate(as_json, allow_nan: true))
File without changes