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
File without changes
@@ -46,7 +46,7 @@ module OpenC3
46
46
  raise "Timeout of #{timeout}s waiting for cmd ack. Does target '#{target_name}' exist?"
47
47
  end
48
48
 
49
- def self.inject_tlm(target_name, packet_name, item_hash = nil, type: :CONVERTED, stored: false, timeout: 5, scope:)
49
+ def self.inject_tlm(target_name, packet_name, item_hash = nil, type: :CONVERTED, stored: false, timeout: 5, received_time: nil, scope:)
50
50
  data = {}
51
51
  data['target_name'] = target_name.to_s.upcase
52
52
  data['packet_name'] = packet_name.to_s.upcase
@@ -55,6 +55,7 @@ module OpenC3
55
55
 
56
56
  db_shard = Store.db_shard_for_target(target_name, scope: scope)
57
57
  data['stored'] = stored
58
+ data['received_time'] = received_time unless received_time.nil?
58
59
  ack_topic = "{#{scope}__ACKCMD}TARGET__#{target_name}"
59
60
  Topic.update_topic_offsets([ack_topic], db_shard: db_shard)
60
61
  decom_id = Topic.write_topic("#{scope}__DECOMINTERFACE__{#{target_name}}",
@@ -148,7 +148,7 @@ module OpenC3
148
148
  Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'protocol_cmd' => JSON.generate(data, allow_nan: true) }, '*', 100, db_shard: db_shard)
149
149
  end
150
150
 
151
- def self.inject_tlm(interface_name, target_name, packet_name, item_hash = nil, type: :CONVERTED, stored: false, timeout: nil, scope:)
151
+ def self.inject_tlm(interface_name, target_name, packet_name, item_hash = nil, type: :CONVERTED, stored: false, timeout: nil, received_time: nil, scope:)
152
152
  interface_name = interface_name.upcase
153
153
  db_shard = _db_shard_for_interface(interface_name, scope: scope)
154
154
 
@@ -162,6 +162,7 @@ module OpenC3
162
162
  data['item_hash'] = item_hash
163
163
  data['type'] = type
164
164
  data['stored'] = stored
165
+ data['received_time'] = received_time unless received_time.nil?
165
166
  cmd_id = Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'inject_tlm' => JSON.generate(data, allow_nan: true) }, '*', 100, db_shard: db_shard)
166
167
  time = Time.now
167
168
  while (Time.now - time) < timeout
@@ -211,7 +212,12 @@ module OpenC3
211
212
  while (Time.now - time) < timeout
212
213
  Topic.read_topics([ack_topic], db_shard: db_shard) do |_topic, _msg_id, msg_hash, _redis|
213
214
  if msg_hash["id"] == cmd_id
214
- return JSON.parse(msg_hash["result"], :allow_nan => true, :create_additions => true)
215
+ begin
216
+ return JSON.parse(msg_hash["result"], :allow_nan => true, :create_additions => true)
217
+ rescue JSON::ParserError
218
+ # The microservice returns a plain error message rather than JSON if details raises
219
+ raise "interface_details failed: #{msg_hash['result']}"
220
+ end
215
221
  end
216
222
  end
217
223
  end
@@ -100,7 +100,7 @@ module OpenC3
100
100
 
101
101
  when :LIMITS_SET
102
102
  sets = sets(scope: scope)
103
- raise "Set '#{event[:set]}' does not exist!" unless sets.key?(event[:set])
103
+ raise "Limits set '#{event[:set]}' does not exist" unless sets.key?(event[:set])
104
104
 
105
105
  # Set all existing sets to "false"
106
106
  sets = sets.transform_values! { |_key, _value| "false" }
File without changes
File without changes
@@ -170,7 +170,12 @@ module OpenC3
170
170
  while (Time.now - time) < timeout
171
171
  Topic.read_topics([ack_topic], db_shard: db_shard) do |_topic, _msg_id, msg_hash, _redis|
172
172
  if msg_hash["id"] == cmd_id
173
- return JSON.parse(msg_hash["result"], :allow_nan => true, :create_additions => true)
173
+ begin
174
+ return JSON.parse(msg_hash["result"], :allow_nan => true, :create_additions => true)
175
+ rescue JSON::ParserError
176
+ # The microservice returns a plain error message rather than JSON if details raises
177
+ raise "router_details failed: #{msg_hash['result']}"
178
+ end
174
179
  end
175
180
  end
176
181
  end
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -28,7 +28,7 @@ module OpenC3
28
28
  # OpenC3 COSMOS Core authentication code
29
29
  class OpenC3Authentication
30
30
  def initialize()
31
- password = ENV['OPENC3_API_PASSWORD']
31
+ password = ENV.fetch('OPENC3_API_PASSWORD', nil)
32
32
  if password.nil?
33
33
  raise OpenC3AuthenticationError, "Authentication requires environment variable OPENC3_API_PASSWORD"
34
34
  end
@@ -172,8 +172,8 @@ module OpenC3
172
172
  if ENV['OPENC3_API_USER'] and ENV['OPENC3_API_PASSWORD']
173
173
  # Username and password
174
174
  data = {
175
- 'username' => ENV['OPENC3_API_USER'],
176
- 'password' => ENV['OPENC3_API_PASSWORD'],
175
+ 'username' => ENV.fetch('OPENC3_API_USER', nil),
176
+ 'password' => ENV.fetch('OPENC3_API_PASSWORD', nil),
177
177
  'client_id' => client_id,
178
178
  'grant_type' => 'password',
179
179
  'scope' => openid_scope
@@ -189,7 +189,7 @@ module OpenC3
189
189
  @refresh_expires_at = current_time + oath['refresh_expires_in'] - REFRESH_OFFSET_SECONDS
190
190
  else
191
191
  # Offline Access Token
192
- @refresh_token ||= ENV['OPENC3_API_TOKEN']
192
+ @refresh_token ||= ENV.fetch('OPENC3_API_TOKEN', nil)
193
193
  _refresh_token(current_time)
194
194
  end
195
195
  end
File without changes
@@ -99,9 +99,9 @@ module OpenC3
99
99
  # Apply bucket policy to grant ScriptRunner user access to config and logs buckets
100
100
  # This provides reduced permissions compared to the root user for security
101
101
  def ensure_scriptrunner_policy(config_bucket, logs_bucket)
102
- sr_username = ENV['OPENC3_SR_BUCKET_USERNAME']
102
+ sr_username = ENV.fetch('OPENC3_SR_BUCKET_USERNAME', nil)
103
103
  return unless sr_username
104
- return if sr_username == ENV['OPENC3_BUCKET_USERNAME'] # Same as root, no policy needed
104
+ return if sr_username == ENV.fetch('OPENC3_BUCKET_USERNAME', nil) # Same as root, no policy needed
105
105
 
106
106
  # Policy for config bucket - read targets, read/write targets_modified
107
107
  # Note: versitygw expects Principal as comma-separated raw usernames
@@ -213,6 +213,14 @@ module OpenC3
213
213
  # If the key is not found return nil
214
214
  rescue Aws::S3::Errors::NoSuchKey
215
215
  nil
216
+ # AWS S3 returns 403 AccessDenied rather than 404 NoSuchKey when the caller
217
+ # does not have s3:ListBucket on the bucket. Callers which probe for an
218
+ # optional key (i.e. TargetFile.body checking targets_modified first) would
219
+ # otherwise raise instead of falling through, so treat it as not found.
220
+ # Warn since a genuine permission problem looks identical from here.
221
+ rescue Aws::S3::Errors::AccessDenied
222
+ Logger.warn("Access denied reading #{bucket}/#{key}. Treating as not found. Verify bucket permissions if this key should exist.")
223
+ nil
216
224
  end
217
225
 
218
226
  def list_objects(bucket:, prefix: nil, max_request: 1000, max_total: 100_000)
@@ -233,7 +241,7 @@ module OpenC3
233
241
  # Array of objects with key and size methods
234
242
  result
235
243
  rescue Aws::S3::Errors::NoSuchBucket
236
- raise NotFound, "Bucket '#{bucket}' does not exist."
244
+ raise NotFound, "Bucket '#{bucket}' does not exist (list_objects)"
237
245
  end
238
246
 
239
247
  # Lists the files under a specified path
@@ -283,7 +291,7 @@ module OpenC3
283
291
  end
284
292
  result
285
293
  rescue Aws::S3::Errors::NoSuchBucket
286
- raise NotFound, "Bucket '#{bucket}' does not exist."
294
+ raise NotFound, "Bucket '#{bucket}' does not exist (list_files)"
287
295
  end
288
296
 
289
297
  # get metadata for a specific object
@@ -23,7 +23,7 @@ module OpenC3
23
23
  def self.getClient
24
24
  raise 'OPENC3_CLOUD environment variable is required' unless ENV['OPENC3_CLOUD']
25
25
  # Base is AwsBucket which works with S3-compatible storage (versitygw), Enterprise implements additional
26
- bucket_class = ENV['OPENC3_CLOUD'].capitalize + 'Bucket'
26
+ bucket_class = ENV.fetch('OPENC3_CLOUD', 'local').capitalize + 'Bucket'
27
27
  klass = OpenC3.require_class('openc3/utilities/'+bucket_class.class_name_to_filename)
28
28
  klass.new
29
29
  end
@@ -163,16 +163,24 @@ class BucketFileCache
163
163
 
164
164
  @current_disk_usage = 0
165
165
  @queued_bucket_files = []
166
+ # Mirrors @queued_bucket_files as bucket_path => true. Membership is checked
167
+ # once per file close in unreserve() and once per entry in age_out_files(),
168
+ # both of which are O(cache size) linear scans against the bare Array.
169
+ @queued_path_hash = {}
166
170
  @bucket_file_hash = {}
167
171
  bucket_file = nil
168
172
 
173
+ # NOTE: check_time must be initialized outside the loop or it is pushed
174
+ # forward on every iteration and the age out check below never fires
175
+ check_time = Time.now + CHECK_TIME_SECONDS # Check for aged out files periodically
176
+
169
177
  @thread = Thread.new do
170
178
  client = OpenC3::Bucket.getClient()
171
179
  while true
172
- check_time = Time.now + CHECK_TIME_SECONDS # Check for aged out files periodically
173
180
  if @queued_bucket_files.length > 0 and @current_disk_usage < MAX_DISK_USAGE
174
181
  @@mutex.synchronize do
175
182
  bucket_file = @queued_bucket_files.shift
183
+ @queued_path_hash.delete(bucket_file.bucket_path) if bucket_file
176
184
  end
177
185
  begin
178
186
  retrieved = bucket_file.retrieve(client)
@@ -187,18 +195,7 @@ class BucketFileCache
187
195
  # Nothing to do or disk full
188
196
  if Time.now > check_time
189
197
  check_time = Time.now + CHECK_TIME_SECONDS
190
- # Delete any files that aren't reserved and are old
191
- removed_files = []
192
- @bucket_file_hash.each do |bucket_path, bucket_file|
193
- deleted = bucket_file.age_check
194
- if deleted
195
- removed_files << bucket_path
196
- @current_disk_usage -= bucket_file.size
197
- end
198
- end
199
- removed_files.each do |bucket_path|
200
- @bucket_file_hash.delete(bucket_path)
201
- end
198
+ age_out_files()
202
199
  end
203
200
  sleep(1)
204
201
  end
@@ -216,8 +213,9 @@ class BucketFileCache
216
213
  return instance().reserve(bucket_path)
217
214
  end
218
215
 
219
- def self.unreserve(bucket_path)
220
- return instance().unreserve(bucket_path)
216
+ # @param bucket_file_or_path [BucketFile|String] BucketFile or its bucket path
217
+ def self.unreserve(bucket_file_or_path)
218
+ return instance().unreserve(bucket_file_or_path)
221
219
  end
222
220
 
223
221
  def hint(bucket_paths)
@@ -236,30 +234,65 @@ class BucketFileCache
236
234
  retrieved = bucket_file.reserve
237
235
  @current_disk_usage += bucket_file.size if retrieved
238
236
  @queued_bucket_files.delete(bucket_file)
237
+ @queued_path_hash.delete(bucket_path)
239
238
  return bucket_file
240
239
  end
241
240
  end
242
241
 
243
- def unreserve(bucket_path)
242
+ # Callers hold the BucketFile returned by reserve() but the cache is keyed by
243
+ # bucket path, so accept either and normalize to the path.
244
+ # @param bucket_file_or_path [BucketFile|String] BucketFile or its bucket path
245
+ def unreserve(bucket_file_or_path)
246
+ if bucket_file_or_path.is_a?(BucketFile)
247
+ released = bucket_file_or_path
248
+ bucket_path = released.bucket_path
249
+ else
250
+ released = nil
251
+ bucket_path = bucket_file_or_path
252
+ end
244
253
  @@mutex.synchronize do
245
254
  bucket_file = @bucket_file_hash[bucket_path]
246
- if bucket_file
247
- bucket_file.unreserve
248
- if bucket_file.reservation_count <= 0 and !@queued_bucket_files.include?(bucket_file)
249
- @current_disk_usage -= bucket_file.size
250
- @bucket_file_hash.delete(bucket_file)
251
- end
255
+ return if bucket_file.nil?
256
+ # Releasing to zero drops the entry, so the next reserve of the same path
257
+ # builds a new BucketFile. A BucketFile that is no longer the entry for
258
+ # its path was therefore already released, and releasing it again would
259
+ # decrement its replacement and delete a local file another reader still
260
+ # has open. Only the current owner of the path may release it.
261
+ return unless released.nil? or released.equal?(bucket_file)
262
+
263
+ bucket_file.unreserve
264
+ if bucket_file.reservation_count <= 0 and !@queued_path_hash[bucket_path]
265
+ @current_disk_usage -= bucket_file.size
266
+ @bucket_file_hash.delete(bucket_path)
252
267
  end
253
268
  end
254
269
  end
255
270
 
256
271
  # Private
257
272
 
273
+ # Delete the local copy of any file that is old and no longer reserved.
274
+ # Files still waiting in the download queue are skipped because the download
275
+ # thread would re-add their size to @current_disk_usage after removal.
276
+ def age_out_files
277
+ @@mutex.synchronize do
278
+ @bucket_file_hash.delete_if do |bucket_path, bucket_file|
279
+ next false if @queued_path_hash[bucket_path]
280
+ if bucket_file.age_check
281
+ @current_disk_usage -= bucket_file.size
282
+ true
283
+ else
284
+ false
285
+ end
286
+ end
287
+ end
288
+ end
289
+
258
290
  def create_bucket_file(bucket_path)
259
291
  bucket_file = @bucket_file_hash[bucket_path]
260
292
  unless bucket_file
261
293
  bucket_file = BucketFile.new(bucket_path)
262
294
  @queued_bucket_files << bucket_file
295
+ @queued_path_hash[bucket_path] = true
263
296
  @bucket_file_hash[bucket_path] = bucket_file
264
297
  end
265
298
  return bucket_file
File without changes
@@ -25,8 +25,7 @@ module OpenC3
25
25
  DIRECTORY_TIMESTAMP_FORMAT = "%Y%m%d"
26
26
 
27
27
  def self.bucket_load(*args, scope: $openc3_scope)
28
- scope = ENV['OPENC3_SCOPE'] unless scope
29
- scope = 'DEFAULT' unless scope
28
+ scope = ENV.fetch('OPENC3_SCOPE', 'DEFAULT') unless scope
30
29
  path = args[0]
31
30
 
32
31
  # Only support TARGET files
@@ -93,7 +92,7 @@ module OpenC3
93
92
  # this to work with really large files. Otherwise the entire file has
94
93
  # to be held in memory!
95
94
  File.open(filename, 'rb') do |file|
96
- client.put_object(bucket: ENV['OPENC3_LOGS_BUCKET'], key: bucket_key, body: file, metadata: metadata)
95
+ client.put_object(bucket: ENV.fetch('OPENC3_LOGS_BUCKET', nil), key: bucket_key, body: file, metadata: metadata)
97
96
  end
98
97
  rescue => err
99
98
  # Try to upload file three times
@@ -104,7 +103,7 @@ module OpenC3
104
103
  retry
105
104
  end
106
105
 
107
- Logger.debug "wrote #{ENV['OPENC3_LOGS_BUCKET']}/#{bucket_key}"
106
+ Logger.debug "wrote #{ENV.fetch('OPENC3_LOGS_BUCKET', nil)}/#{bucket_key}"
108
107
 
109
108
  File.delete(orig_filename) if orig_filename
110
109
  File.delete(filename)
File without changes
File without changes
@@ -0,0 +1,75 @@
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
+ module OpenC3
15
+ # Shared predicates describing which parts of the user-writable config overlay
16
+ # (targets_modified) a non-admin may write.
17
+ #
18
+ # The cmd_tlm overlay (targets_modified/<TARGET>/cmd_tlm/...) is loaded by
19
+ # System.setup_targets and processed by PacketConfig, which evaluates
20
+ # GENERIC_READ_CONVERSION / GENERIC_WRITE_CONVERSION blocks as code in the
21
+ # decom microservices. Writing it is therefore an admin operation, matching the
22
+ # tier COSMOS requires everywhere else code is introduced (plugin install).
23
+ # Only the server-side dynamic-packet mechanism (TargetModel#dynamic_update)
24
+ # writes that area without going through an API request.
25
+ #
26
+ # Every API writer that can reach the overlay must consult these predicates:
27
+ # - storage_controller#get_upload_presigned_request / delete (presigned S3 upload)
28
+ # - tables_controller#save / save_as / generate / destroy (Table -> TargetFile)
29
+ # - scripts_controller#create / destroy (Script -> TargetFile)
30
+ module ConfigOverlay
31
+ # Config bucket areas a non-admin is allowed to write at all
32
+ NON_ADMIN_AREAS = ['targets_modified', 'tmp'].freeze
33
+
34
+ # Target subdirectory whose contents are executed as code
35
+ CODE_AREA = 'cmd_tlm'
36
+
37
+ # Split a path into segments, or nil if the path is not canonical.
38
+ #
39
+ # Positional segment checks (parts[1], parts[3]) can be bypassed by a path
40
+ # the object store normalizes differently, so empty '//' segments, '.'/'..'
41
+ # segments, and leading/trailing slashes yield nil and every caller fails
42
+ # closed (requires admin) on nil.
43
+ def self.canonical_parts(path)
44
+ return nil if path.nil? || path.empty?
45
+ return nil if path.start_with?('/') || path.end_with?('/')
46
+ parts = path.split('/')
47
+ return nil if parts.any? { |part| part.empty? || part == '.' || part == '..' }
48
+ parts
49
+ end
50
+
51
+ # True if the overlay-relative name targets the cmd_tlm subtree, i.e. writing
52
+ # it requires admin. Name is relative to targets_modified/, e.g.
53
+ # "<TARGET>/cmd_tlm/tlm.txt". Fails closed (true) on non-canonical names.
54
+ def self.cmd_tlm_overlay?(name)
55
+ parts = canonical_parts(name)
56
+ return true if parts.nil?
57
+ # parts: <TARGET> / <area> / ...
58
+ parts[1] == CODE_AREA
59
+ end
60
+
61
+ # True if the given config bucket key is an overlay path a non-admin may
62
+ # write. Key is the full bucket key, e.g.
63
+ # "<SCOPE>/targets_modified/<TARGET>/screens/x.txt". Fails closed (false) on
64
+ # non-canonical keys.
65
+ def self.non_admin_writable_key?(key)
66
+ parts = canonical_parts(key)
67
+ return false if parts.nil?
68
+ # parts: <SCOPE> / <area> / <TARGET> / <subdir> / ...
69
+ area = parts[1]
70
+ return false unless NON_ADMIN_AREAS.include?(area)
71
+ return false if area == 'targets_modified' && parts[3] == CODE_AREA
72
+ true
73
+ end
74
+ end
75
+ end
File without changes
@@ -75,18 +75,22 @@ module OpenC3
75
75
  @bit_size = 8
76
76
  pack = 'C'
77
77
  filter_mask = 0xFF
78
+ right_shift = 0
78
79
  when 'OpenC3::Crc16'
79
80
  @bit_size = 16
80
81
  pack = 'S'
81
82
  filter_mask = 0xFFFF
83
+ right_shift = 8
82
84
  when 'OpenC3::Crc32'
83
85
  @bit_size = 32
84
86
  pack = 'I'
85
87
  filter_mask = 0xFFFFFFFF
88
+ right_shift = 24
86
89
  when 'OpenC3::Crc64'
87
90
  @bit_size = 64
88
91
  pack = 'Q'
89
92
  filter_mask = 0xFFFFFFFFFFFFFFFF
93
+ right_shift = 56
90
94
  end
91
95
  if RUBY_ENGINE == 'ruby' and !ENV['OPENC3_NO_EXT']
92
96
  (0..255).each do |index|
@@ -96,6 +100,29 @@ module OpenC3
96
100
  (0..255).each do |index|
97
101
  @table << (compute_table_entry(index, @bit_size) & filter_mask)
98
102
  end
103
+
104
+ @filter_mask = filter_mask
105
+ @right_shift = right_shift
106
+ @bit_reverse = method("bit_reverse_#{@bit_size}")
107
+
108
+ # A reflected (least-significant-bit first) table avoids reversing each
109
+ # input byte in calc. CRC32 and CRC64 get three additional tables so
110
+ # their common reflected forms can process four bytes per Ruby loop.
111
+ if @reflect
112
+ reflected_table = Array.new(256) do |index|
113
+ @bit_reverse.call(@table[BIT_REVERSE_TABLE[index]])
114
+ end
115
+ @reflected_tables = [reflected_table]
116
+ if @bit_size >= 32
117
+ 3.times do
118
+ previous_table = @reflected_tables[-1]
119
+ @reflected_tables << Array.new(256) do |index|
120
+ value = previous_table[index]
121
+ (value >> 8) ^ reflected_table[value & 0xFF]
122
+ end
123
+ end
124
+ end
125
+ end
99
126
  end
100
127
  end
101
128
 
@@ -140,47 +167,47 @@ module OpenC3
140
167
 
141
168
  if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
142
169
  def calc(data, seed = @seed)
143
- crc = seed
144
-
145
- case @bit_size
146
- when 8
147
- right_shift = 0
148
- filter_mask = 0xFF
149
- final_bit_reverse = method(:bit_reverse_8)
150
- when 16
151
- right_shift = 8
152
- filter_mask = 0xFFFF
153
- final_bit_reverse = method(:bit_reverse_16)
154
- when 32
155
- right_shift = 24
156
- filter_mask = 0xFFFFFFFF
157
- final_bit_reverse = method(:bit_reverse_32)
158
- when 64
159
- right_shift = 56
160
- filter_mask = 0xFFFFFFFFFFFFFFFF
161
- final_bit_reverse = method(:bit_reverse_64)
162
- end
170
+ seed = @seed if seed.nil?
163
171
 
164
172
  if @reflect
165
- data.each_byte do |byte|
166
- crc = ((crc << 8) & filter_mask) ^ @table[(crc >> right_shift) ^ bit_reverse_8(byte)]
173
+ crc = @bit_reverse.call(seed)
174
+ reflected_table = @reflected_tables[0]
175
+ index = 0
176
+
177
+ if @bit_size >= 32
178
+ table1 = @reflected_tables[1]
179
+ table2 = @reflected_tables[2]
180
+ table3 = @reflected_tables[3]
181
+ word_count = data.bytesize / 4
182
+ unless word_count.zero?
183
+ data.unpack("V#{word_count}").each do |word|
184
+ value = crc ^ word
185
+ crc = (value >> 32) ^
186
+ table3[value & 0xFF] ^
187
+ table2[(value >> 8) & 0xFF] ^
188
+ table1[(value >> 16) & 0xFF] ^
189
+ reflected_table[(value >> 24) & 0xFF]
190
+ end
191
+ index = word_count * 4
192
+ end
167
193
  end
168
194
 
169
- if @xor
170
- return final_bit_reverse.call(crc ^ filter_mask)
171
- else
172
- return final_bit_reverse.call(crc)
195
+ while index < data.bytesize
196
+ crc = (crc >> 8) ^ reflected_table[(crc ^ data.getbyte(index)) & 0xFF]
197
+ index += 1
173
198
  end
199
+
200
+ return @xor ? crc ^ @filter_mask : crc
174
201
  else
202
+ crc = seed
203
+ table = @table
204
+ filter_mask = @filter_mask
205
+ right_shift = @right_shift
175
206
  data.each_byte do |byte|
176
- crc = ((crc << 8) & filter_mask) ^ @table[(crc >> right_shift) ^ byte]
207
+ crc = ((crc << 8) & filter_mask) ^ table[(crc >> right_shift) ^ byte]
177
208
  end
178
209
 
179
- if @xor
180
- return crc ^ filter_mask
181
- else
182
- return crc
183
- end
210
+ return @xor ? crc ^ filter_mask : crc
184
211
  end
185
212
  end
186
213
  end
@@ -59,7 +59,7 @@ module OpenC3
59
59
  # @param index [Integer] Which value to return
60
60
  # @return [Boolean] Single value converted to a boolean (true or false)
61
61
  def bool(item, index = 0)
62
- raise "#{item} not found" unless keys.include?(item)
62
+ raise "CSV key '#{item}' not found for bool conversion" unless keys.include?(item)
63
63
 
64
64
  if Range === index
65
65
  @hash[item][index].map do |x|
@@ -91,7 +91,7 @@ module OpenC3
91
91
  # @param index [Integer] Which value to return
92
92
  # @return [Integer] Single value converted to an integer
93
93
  def int(item, index = 0)
94
- raise "#{item} not found" unless keys.include?(item)
94
+ raise "CSV key '#{item}' not found for int conversion" unless keys.include?(item)
95
95
 
96
96
  if Range === index
97
97
  @hash[item][index].map { |x| x.to_i }
@@ -107,7 +107,7 @@ module OpenC3
107
107
  # @param index [Integer] Which value to return
108
108
  # @return [Float] Single value converted to a float
109
109
  def float(item, index = 0)
110
- raise "#{item} not found" unless keys.include?(item)
110
+ raise "CSV key '#{item}' not found for float conversion" unless keys.include?(item)
111
111
 
112
112
  if Range === index
113
113
  @hash[item][index].map { |x| x.to_f }
@@ -122,7 +122,7 @@ module OpenC3
122
122
  # @param index [Integer] Which value to return
123
123
  # @return [String] Single value converted to a string
124
124
  def string(item, index = 0)
125
- raise "#{item} not found" unless keys.include?(item)
125
+ raise "CSV key '#{item}' not found for string lookup" unless keys.include?(item)
126
126
 
127
127
  if Range === index
128
128
  @hash[item][index].map { |x| x.to_s }
@@ -138,7 +138,7 @@ module OpenC3
138
138
  # @param index [Integer] Which value to return
139
139
  # @return [Symbol] Single value converted to a symbol
140
140
  def symbol(item, index = 0)
141
- raise "#{item} not found" unless keys.include?(item)
141
+ raise "CSV key '#{item}' not found for symbol lookup" unless keys.include?(item)
142
142
 
143
143
  if Range === index
144
144
  @hash[item][index].map { |x| x.intern }
File without changes
File without changes
File without changes