openc3 7.3.0 → 7.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (499) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Guardfile +0 -0
  4. data/LICENSE.md +0 -0
  5. data/README.md +0 -0
  6. data/Rakefile +0 -0
  7. data/bin/openc3cli +138 -0
  8. data/bin/pipuninstall +0 -0
  9. data/bin/uvinstall +10 -3
  10. data/data/config/_array_params.yaml +0 -0
  11. data/data/config/_canvas_values.yaml +0 -0
  12. data/data/config/_graph_params.yaml +0 -0
  13. data/data/config/_id_items.yaml +0 -0
  14. data/data/config/_id_params.yaml +0 -0
  15. data/data/config/_interfaces.yaml +0 -0
  16. data/data/config/_items.yaml +0 -0
  17. data/data/config/_params.yaml +0 -0
  18. data/data/config/command.yaml +0 -0
  19. data/data/config/command_modifiers.yaml +2 -0
  20. data/data/config/command_telemetry.yaml +0 -0
  21. data/data/config/conversions.yaml +0 -0
  22. data/data/config/graph_settings.yaml +0 -0
  23. data/data/config/interface_modifiers.yaml +103 -1
  24. data/data/config/item_modifiers.yaml +0 -0
  25. data/data/config/microservice.yaml +5 -2
  26. data/data/config/param_item_modifiers.yaml +0 -0
  27. data/data/config/parameter_modifiers.yaml +0 -0
  28. data/data/config/plugins.yaml +0 -0
  29. data/data/config/processors.yaml +0 -0
  30. data/data/config/protocols.yaml +0 -0
  31. data/data/config/screen.yaml +0 -0
  32. data/data/config/settings.yaml +0 -0
  33. data/data/config/table_manager.yaml +0 -0
  34. data/data/config/table_parameter_modifiers.yaml +0 -0
  35. data/data/config/target.yaml +0 -0
  36. data/data/config/target_config.yaml +0 -0
  37. data/data/config/telemetry.yaml +0 -0
  38. data/data/config/telemetry_modifiers.yaml +2 -0
  39. data/data/config/tool.yaml +0 -0
  40. data/data/config/unknown.yaml +0 -0
  41. data/data/config/widgets.yaml +6 -0
  42. data/data/xtce_schemas/SpaceSystem_20180204.xsd +0 -0
  43. data/data/xtce_schemas/xml.xsd +0 -0
  44. data/ext/openc3/ext/array/array.c +0 -0
  45. data/ext/openc3/ext/array/extconf.rb +0 -0
  46. data/ext/openc3/ext/buffered_file/buffered_file.c +0 -0
  47. data/ext/openc3/ext/buffered_file/extconf.rb +0 -0
  48. data/ext/openc3/ext/burst_protocol/burst_protocol.c +6 -0
  49. data/ext/openc3/ext/burst_protocol/extconf.rb +0 -0
  50. data/ext/openc3/ext/config_parser/config_parser.c +0 -0
  51. data/ext/openc3/ext/config_parser/extconf.rb +0 -0
  52. data/ext/openc3/ext/crc/crc.c +0 -0
  53. data/ext/openc3/ext/crc/extconf.rb +0 -0
  54. data/ext/openc3/ext/openc3_io/extconf.rb +0 -0
  55. data/ext/openc3/ext/openc3_io/openc3_io.c +0 -0
  56. data/ext/openc3/ext/packet/extconf.rb +0 -0
  57. data/ext/openc3/ext/packet/packet.c +0 -0
  58. data/ext/openc3/ext/platform/extconf.rb +0 -0
  59. data/ext/openc3/ext/platform/platform.c +4 -1
  60. data/ext/openc3/ext/polynomial_conversion/extconf.rb +0 -0
  61. data/ext/openc3/ext/polynomial_conversion/polynomial_conversion.c +0 -0
  62. data/ext/openc3/ext/string/extconf.rb +0 -0
  63. data/ext/openc3/ext/string/string.c +0 -0
  64. data/ext/openc3/ext/structure/structure.c +49 -17
  65. data/ext/openc3/ext/tabbed_plots_config/extconf.rb +0 -0
  66. data/ext/openc3/ext/tabbed_plots_config/tabbed_plots_config.c +0 -0
  67. data/ext/openc3/ext/telemetry/extconf.rb +0 -0
  68. data/ext/openc3/ext/telemetry/telemetry.c +0 -0
  69. data/lib/cosmos.rb +0 -0
  70. data/lib/cosmosc2.rb +0 -0
  71. data/lib/openc3/accessors/accessor.rb +0 -0
  72. data/lib/openc3/accessors/binary_accessor.rb +63 -9
  73. data/lib/openc3/accessors/cbor_accessor.rb +0 -0
  74. data/lib/openc3/accessors/form_accessor.rb +0 -0
  75. data/lib/openc3/accessors/html_accessor.rb +0 -0
  76. data/lib/openc3/accessors/http_accessor.rb +0 -0
  77. data/lib/openc3/accessors/json_accessor.rb +0 -0
  78. data/lib/openc3/accessors/template_accessor.rb +0 -0
  79. data/lib/openc3/accessors/xml_accessor.rb +0 -0
  80. data/lib/openc3/accessors.rb +0 -0
  81. data/lib/openc3/api/README.md +0 -0
  82. data/lib/openc3/api/api.rb +0 -0
  83. data/lib/openc3/api/authorized_api.rb +0 -0
  84. data/lib/openc3/api/calendar_api.rb +0 -0
  85. data/lib/openc3/api/cmd_api.rb +1 -1
  86. data/lib/openc3/api/config_api.rb +0 -0
  87. data/lib/openc3/api/interface_api.rb +0 -0
  88. data/lib/openc3/api/limits_api.rb +0 -0
  89. data/lib/openc3/api/metrics_api.rb +1 -1
  90. data/lib/openc3/api/offline_access_api.rb +1 -1
  91. data/lib/openc3/api/router_api.rb +0 -0
  92. data/lib/openc3/api/settings_api.rb +22 -18
  93. data/lib/openc3/api/stash_api.rb +1 -1
  94. data/lib/openc3/api/target_api.rb +0 -0
  95. data/lib/openc3/api/tlm_api.rb +4 -3
  96. data/lib/openc3/bridge/bridge.rb +0 -0
  97. data/lib/openc3/bridge/bridge_config.rb +0 -0
  98. data/lib/openc3/bridge/bridge_interface_thread.rb +0 -0
  99. data/lib/openc3/bridge/bridge_router_thread.rb +0 -0
  100. data/lib/openc3/ccsds/ccsds_packet.rb +0 -0
  101. data/lib/openc3/ccsds/ccsds_parser.rb +0 -0
  102. data/lib/openc3/config/config_parser.rb +78 -17
  103. data/lib/openc3/config/meta_config_parser.rb +0 -0
  104. data/lib/openc3/conversions/bit_reverse_conversion.rb +0 -0
  105. data/lib/openc3/conversions/conversion.rb +0 -0
  106. data/lib/openc3/conversions/generic_conversion.rb +0 -0
  107. data/lib/openc3/conversions/ip_read_conversion.rb +0 -0
  108. data/lib/openc3/conversions/ip_write_conversion.rb +0 -0
  109. data/lib/openc3/conversions/object_read_conversion.rb +0 -0
  110. data/lib/openc3/conversions/object_write_conversion.rb +0 -0
  111. data/lib/openc3/conversions/packet_time_formatted_conversion.rb +0 -0
  112. data/lib/openc3/conversions/packet_time_seconds_conversion.rb +0 -0
  113. data/lib/openc3/conversions/polynomial_conversion.rb +0 -0
  114. data/lib/openc3/conversions/processor_conversion.rb +0 -0
  115. data/lib/openc3/conversions/received_count_conversion.rb +0 -0
  116. data/lib/openc3/conversions/received_time_formatted_conversion.rb +0 -0
  117. data/lib/openc3/conversions/received_time_seconds_conversion.rb +0 -0
  118. data/lib/openc3/conversions/segmented_polynomial_conversion.rb +0 -0
  119. data/lib/openc3/conversions/unix_time_conversion.rb +0 -0
  120. data/lib/openc3/conversions/unix_time_formatted_conversion.rb +0 -0
  121. data/lib/openc3/conversions/unix_time_seconds_conversion.rb +0 -0
  122. data/lib/openc3/conversions.rb +0 -0
  123. data/lib/openc3/core_ext/array.rb +0 -0
  124. data/lib/openc3/core_ext/binding.rb +0 -0
  125. data/lib/openc3/core_ext/class.rb +0 -0
  126. data/lib/openc3/core_ext/exception.rb +0 -0
  127. data/lib/openc3/core_ext/faraday.rb +0 -0
  128. data/lib/openc3/core_ext/file.rb +0 -0
  129. data/lib/openc3/core_ext/io.rb +0 -0
  130. data/lib/openc3/core_ext/kernel.rb +0 -0
  131. data/lib/openc3/core_ext/math.rb +0 -0
  132. data/lib/openc3/core_ext/matrix.rb +0 -0
  133. data/lib/openc3/core_ext/objectspace.rb +0 -0
  134. data/lib/openc3/core_ext/openc3_io.rb +0 -0
  135. data/lib/openc3/core_ext/range.rb +0 -0
  136. data/lib/openc3/core_ext/socket.rb +0 -0
  137. data/lib/openc3/core_ext/string.rb +0 -0
  138. data/lib/openc3/core_ext/stringio.rb +0 -0
  139. data/lib/openc3/core_ext/tempfile.rb +0 -0
  140. data/lib/openc3/core_ext/time.rb +0 -0
  141. data/lib/openc3/core_ext.rb +0 -0
  142. data/lib/openc3/ext/.keep +0 -0
  143. data/lib/openc3/interfaces/file_interface.rb +0 -0
  144. data/lib/openc3/interfaces/http_client_interface.rb +0 -0
  145. data/lib/openc3/interfaces/http_server_interface.rb +0 -0
  146. data/lib/openc3/interfaces/interface.rb +5 -4
  147. data/lib/openc3/interfaces/mqtt_interface.rb +0 -0
  148. data/lib/openc3/interfaces/mqtt_stream_interface.rb +2 -0
  149. data/lib/openc3/interfaces/protocols/burst_protocol.rb +27 -0
  150. data/lib/openc3/interfaces/protocols/cmd_response_protocol.rb +0 -0
  151. data/lib/openc3/interfaces/protocols/cobs_protocol.rb +0 -0
  152. data/lib/openc3/interfaces/protocols/crc_protocol.rb +0 -0
  153. data/lib/openc3/interfaces/protocols/fixed_protocol.rb +0 -0
  154. data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +0 -0
  155. data/lib/openc3/interfaces/protocols/length_protocol.rb +7 -0
  156. data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +7 -0
  157. data/lib/openc3/interfaces/protocols/protocol.rb +4 -4
  158. data/lib/openc3/interfaces/protocols/slip_protocol.rb +0 -0
  159. data/lib/openc3/interfaces/protocols/template_protocol.rb +2 -2
  160. data/lib/openc3/interfaces/protocols/terminated_protocol.rb +0 -0
  161. data/lib/openc3/interfaces/serial_interface.rb +2 -0
  162. data/lib/openc3/interfaces/simulated_target_interface.rb +0 -0
  163. data/lib/openc3/interfaces/stream_interface.rb +0 -0
  164. data/lib/openc3/interfaces/tcpip_client_interface.rb +2 -0
  165. data/lib/openc3/interfaces/tcpip_server_interface.rb +0 -0
  166. data/lib/openc3/interfaces/udp_interface.rb +16 -5
  167. data/lib/openc3/interfaces.rb +0 -0
  168. data/lib/openc3/io/buffered_file.rb +0 -0
  169. data/lib/openc3/io/io_multiplexer.rb +0 -0
  170. data/lib/openc3/io/json_api.rb +3 -2
  171. data/lib/openc3/io/json_api_object.rb +0 -0
  172. data/lib/openc3/io/json_drb.rb +20 -4
  173. data/lib/openc3/io/json_drb_object.rb +0 -0
  174. data/lib/openc3/io/json_drb_rack.rb +0 -0
  175. data/lib/openc3/io/json_rpc.rb +0 -0
  176. data/lib/openc3/io/posix_serial_driver.rb +0 -0
  177. data/lib/openc3/io/serial_driver.rb +0 -0
  178. data/lib/openc3/io/stderr.rb +0 -0
  179. data/lib/openc3/io/stdout.rb +0 -0
  180. data/lib/openc3/io/udp_sockets.rb +38 -8
  181. data/lib/openc3/io/win32_serial_driver.rb +1 -0
  182. data/lib/openc3/logs/buffered_packet_log_reader.rb +0 -0
  183. data/lib/openc3/logs/buffered_packet_log_writer.rb +0 -0
  184. data/lib/openc3/logs/log_writer.rb +0 -0
  185. data/lib/openc3/logs/packet_log_constants.rb +0 -0
  186. data/lib/openc3/logs/packet_log_reader.rb +23 -2
  187. data/lib/openc3/logs/packet_log_writer.rb +5 -0
  188. data/lib/openc3/logs/stream_log.rb +1 -1
  189. data/lib/openc3/logs/stream_log_pair.rb +0 -0
  190. data/lib/openc3/logs/text_log_writer.rb +0 -0
  191. data/lib/openc3/logs.rb +0 -0
  192. data/lib/openc3/microservices/cleanup_microservice.rb +0 -0
  193. data/lib/openc3/microservices/decom_common.rb +0 -0
  194. data/lib/openc3/microservices/decom_microservice.rb +0 -0
  195. data/lib/openc3/microservices/interface_decom_common.rb +2 -1
  196. data/lib/openc3/microservices/interface_microservice.rb +72 -25
  197. data/lib/openc3/microservices/log_microservice.rb +0 -0
  198. data/lib/openc3/microservices/microservice.rb +3 -3
  199. data/lib/openc3/microservices/multi_microservice.rb +1 -1
  200. data/lib/openc3/microservices/periodic_microservice.rb +2 -2
  201. data/lib/openc3/microservices/plugin_microservice.rb +0 -0
  202. data/lib/openc3/microservices/queue_microservice.rb +0 -0
  203. data/lib/openc3/microservices/router_microservice.rb +0 -0
  204. data/lib/openc3/microservices/scope_cleanup_microservice.rb +0 -0
  205. data/lib/openc3/microservices/text_log_microservice.rb +0 -0
  206. data/lib/openc3/migrations/20220420190000_log_stuff.rb +0 -0
  207. data/lib/openc3/migrations/20221202214600_add_target_names.rb +0 -0
  208. data/lib/openc3/migrations/20221210174900_convert_to_multi.rb +0 -0
  209. data/lib/openc3/migrations/20230915000002_no_scope_log_messages.rb +0 -0
  210. data/lib/openc3/migrations/20231022000000_tlm_viewer_config.rb +0 -0
  211. data/lib/openc3/migrations/20241208080000_no_critical_cmd.rb +0 -0
  212. data/lib/openc3/migrations/20241208080001_no_trigger_group.rb +0 -0
  213. data/lib/openc3/migrations/20250108060000_news_feed.rb +0 -0
  214. data/lib/openc3/migrations/20250402000000_periodic_only_default.rb +0 -0
  215. data/lib/openc3/migrations/20260203000000_remove_store_id.rb +0 -0
  216. data/lib/openc3/migrations/20260204000000_remove_decom_reducer.rb +0 -0
  217. data/lib/openc3/migrations/20260701000000_updated_at_to_int.rb +0 -0
  218. data/lib/openc3/migrations/20260704000000_default_bridge.rb +38 -0
  219. data/lib/openc3/migrations/20260731000000_system_health_json.rb +11 -0
  220. data/lib/openc3/models/activity_model.rb +0 -0
  221. data/lib/openc3/models/auth_model.rb +35 -7
  222. data/lib/openc3/models/bridge_model.rb +133 -0
  223. data/lib/openc3/models/config_keyword_model.rb +63 -0
  224. data/lib/openc3/models/cvt_model.rb +0 -0
  225. data/lib/openc3/models/db_sharded_model.rb +0 -0
  226. data/lib/openc3/models/environment_model.rb +0 -0
  227. data/lib/openc3/models/gem_model.rb +12 -9
  228. data/lib/openc3/models/host_interface_microservice_model.rb +114 -0
  229. data/lib/openc3/models/info_model.rb +0 -0
  230. data/lib/openc3/models/interface_model.rb +178 -19
  231. data/lib/openc3/models/interface_status_model.rb +0 -0
  232. data/lib/openc3/models/metadata_model.rb +0 -0
  233. data/lib/openc3/models/metric_model.rb +0 -0
  234. data/lib/openc3/models/microservice_model.rb +9 -22
  235. data/lib/openc3/models/microservice_status_model.rb +0 -0
  236. data/lib/openc3/models/migration_model.rb +0 -0
  237. data/lib/openc3/models/model.rb +0 -0
  238. data/lib/openc3/models/news_model.rb +0 -0
  239. data/lib/openc3/models/note_model.rb +0 -0
  240. data/lib/openc3/models/offline_access_model.rb +0 -0
  241. data/lib/openc3/models/ping_model.rb +0 -0
  242. data/lib/openc3/models/plugin_model.rb +9 -2
  243. data/lib/openc3/models/plugin_store_model.rb +0 -0
  244. data/lib/openc3/models/process_status_model.rb +0 -0
  245. data/lib/openc3/models/python_package_model.rb +4 -4
  246. data/lib/openc3/models/queue_model.rb +0 -0
  247. data/lib/openc3/models/reaction_model.rb +0 -0
  248. data/lib/openc3/models/reingest_job_model.rb +0 -0
  249. data/lib/openc3/models/router_model.rb +0 -0
  250. data/lib/openc3/models/router_status_model.rb +0 -0
  251. data/lib/openc3/models/scope_model.rb +51 -26
  252. data/lib/openc3/models/script_engine_model.rb +0 -0
  253. data/lib/openc3/models/script_status_model.rb +0 -0
  254. data/lib/openc3/models/secret_model.rb +0 -0
  255. data/lib/openc3/models/setting_model.rb +546 -3
  256. data/lib/openc3/models/sorted_model.rb +0 -0
  257. data/lib/openc3/models/stash_model.rb +0 -0
  258. data/lib/openc3/models/target_model.rb +30 -25
  259. data/lib/openc3/models/timeline_model.rb +0 -0
  260. data/lib/openc3/models/tool_config_model.rb +0 -0
  261. data/lib/openc3/models/tool_model.rb +1 -0
  262. data/lib/openc3/models/trigger_group_model.rb +0 -0
  263. data/lib/openc3/models/trigger_model.rb +20 -0
  264. data/lib/openc3/models/widget_model.rb +0 -0
  265. data/lib/openc3/operators/microservice_operator.rb +64 -22
  266. data/lib/openc3/operators/operator.rb +4 -4
  267. data/lib/openc3/packets/binary_accessor.rb +0 -0
  268. data/lib/openc3/packets/command_validator.rb +0 -0
  269. data/lib/openc3/packets/commands.rb +0 -0
  270. data/lib/openc3/packets/json_packet.rb +0 -0
  271. data/lib/openc3/packets/limits.rb +0 -0
  272. data/lib/openc3/packets/limits_response.rb +0 -0
  273. data/lib/openc3/packets/packet.rb +4 -0
  274. data/lib/openc3/packets/packet_config.rb +0 -0
  275. data/lib/openc3/packets/packet_item.rb +0 -0
  276. data/lib/openc3/packets/packet_item_limits.rb +0 -0
  277. data/lib/openc3/packets/parsers/format_string_parser.rb +0 -0
  278. data/lib/openc3/packets/parsers/limits_parser.rb +0 -0
  279. data/lib/openc3/packets/parsers/limits_response_parser.rb +0 -0
  280. data/lib/openc3/packets/parsers/packet_item_parser.rb +0 -0
  281. data/lib/openc3/packets/parsers/packet_parser.rb +0 -0
  282. data/lib/openc3/packets/parsers/processor_parser.rb +0 -0
  283. data/lib/openc3/packets/parsers/state_parser.rb +0 -0
  284. data/lib/openc3/packets/parsers/xtce_converter.rb +0 -0
  285. data/lib/openc3/packets/parsers/xtce_parser.rb +0 -0
  286. data/lib/openc3/packets/structure.rb +7 -1
  287. data/lib/openc3/packets/structure_item.rb +0 -0
  288. data/lib/openc3/packets/telemetry.rb +0 -0
  289. data/lib/openc3/processors/processor.rb +2 -0
  290. data/lib/openc3/processors/statistics_processor.rb +0 -0
  291. data/lib/openc3/processors/watermark_processor.rb +0 -0
  292. data/lib/openc3/processors.rb +0 -0
  293. data/lib/openc3/script/api_shared.rb +0 -0
  294. data/lib/openc3/script/autonomic.rb +12 -12
  295. data/lib/openc3/script/calendar.rb +0 -0
  296. data/lib/openc3/script/commands.rb +0 -0
  297. data/lib/openc3/script/critical_cmd.rb +0 -0
  298. data/lib/openc3/script/exceptions.rb +0 -0
  299. data/lib/openc3/script/extract.rb +0 -0
  300. data/lib/openc3/script/limits.rb +0 -0
  301. data/lib/openc3/script/metadata.rb +0 -0
  302. data/lib/openc3/script/packages.rb +0 -0
  303. data/lib/openc3/script/plugins.rb +0 -0
  304. data/lib/openc3/script/queue.rb +0 -0
  305. data/lib/openc3/script/screen.rb +0 -0
  306. data/lib/openc3/script/script.rb +2 -2
  307. data/lib/openc3/script/script_runner.rb +0 -0
  308. data/lib/openc3/script/storage.rb +4 -4
  309. data/lib/openc3/script/suite.rb +0 -0
  310. data/lib/openc3/script/suite_results.rb +0 -0
  311. data/lib/openc3/script/suite_runner.rb +26 -0
  312. data/lib/openc3/script/tables.rb +0 -0
  313. data/lib/openc3/script/telemetry.rb +9 -3
  314. data/lib/openc3/script/web_socket_api.rb +218 -145
  315. data/lib/openc3/script.rb +0 -0
  316. data/lib/openc3/script_engines/script_engine.rb +0 -0
  317. data/lib/openc3/streams/mqtt_stream.rb +0 -0
  318. data/lib/openc3/streams/serial_stream.rb +0 -0
  319. data/lib/openc3/streams/stream.rb +0 -0
  320. data/lib/openc3/streams/tcpip_client_stream.rb +0 -0
  321. data/lib/openc3/streams/tcpip_socket_stream.rb +0 -0
  322. data/lib/openc3/streams/web_socket_client_stream.rb +0 -0
  323. data/lib/openc3/subpacketizers/subpacketizer.rb +0 -0
  324. data/lib/openc3/system/system.rb +0 -0
  325. data/lib/openc3/system/target.rb +0 -0
  326. data/lib/openc3/system.rb +0 -0
  327. data/lib/openc3/tools/cmd_tlm_server/api.rb +0 -0
  328. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +7 -0
  329. data/lib/openc3/tools/table_manager/table.rb +0 -0
  330. data/lib/openc3/tools/table_manager/table_config.rb +0 -0
  331. data/lib/openc3/tools/table_manager/table_item.rb +0 -0
  332. data/lib/openc3/tools/table_manager/table_item_parser.rb +0 -0
  333. data/lib/openc3/tools/table_manager/table_manager_core.rb +0 -0
  334. data/lib/openc3/tools/table_manager/table_parser.rb +0 -0
  335. data/lib/openc3/tools/test_runner/test.rb +0 -0
  336. data/lib/openc3/top_level.rb +1 -1
  337. data/lib/openc3/topics/autonomic_topic.rb +0 -0
  338. data/lib/openc3/topics/calendar_topic.rb +0 -0
  339. data/lib/openc3/topics/command_decom_topic.rb +0 -0
  340. data/lib/openc3/topics/command_topic.rb +0 -0
  341. data/lib/openc3/topics/config_topic.rb +0 -0
  342. data/lib/openc3/topics/decom_interface_topic.rb +2 -1
  343. data/lib/openc3/topics/interface_topic.rb +8 -2
  344. data/lib/openc3/topics/limits_event_topic.rb +0 -0
  345. data/lib/openc3/topics/notebook_topic.rb +0 -0
  346. data/lib/openc3/topics/queue_topic.rb +0 -0
  347. data/lib/openc3/topics/router_topic.rb +6 -1
  348. data/lib/openc3/topics/system_events_topic.rb +0 -0
  349. data/lib/openc3/topics/telemetry_decom_topic.rb +0 -0
  350. data/lib/openc3/topics/telemetry_topic.rb +0 -0
  351. data/lib/openc3/topics/timeline_topic.rb +0 -0
  352. data/lib/openc3/topics/topic.rb +0 -0
  353. data/lib/openc3/utilities/authentication.rb +4 -4
  354. data/lib/openc3/utilities/authorization.rb +0 -0
  355. data/lib/openc3/utilities/aws_bucket.rb +2 -2
  356. data/lib/openc3/utilities/bucket.rb +1 -1
  357. data/lib/openc3/utilities/bucket_file_cache.rb +55 -22
  358. data/lib/openc3/utilities/bucket_require.rb +0 -0
  359. data/lib/openc3/utilities/bucket_utilities.rb +3 -4
  360. data/lib/openc3/utilities/cli_generator.rb +0 -0
  361. data/lib/openc3/utilities/cmd_log.rb +0 -0
  362. data/lib/openc3/utilities/config_overlay.rb +75 -0
  363. data/lib/openc3/utilities/cosmos_rails_formatter.rb +0 -0
  364. data/lib/openc3/utilities/crc.rb +59 -32
  365. data/lib/openc3/utilities/csv.rb +0 -0
  366. data/lib/openc3/utilities/ctrf.rb +0 -0
  367. data/lib/openc3/utilities/env_helper.rb +0 -0
  368. data/lib/openc3/utilities/local_bucket.rb +0 -0
  369. data/lib/openc3/utilities/local_mode.rb +32 -6
  370. data/lib/openc3/utilities/logger.rb +30 -8
  371. data/lib/openc3/utilities/message_log.rb +0 -0
  372. data/lib/openc3/utilities/metric.rb +0 -0
  373. data/lib/openc3/utilities/migration.rb +0 -0
  374. data/lib/openc3/utilities/open_telemetry.rb +0 -0
  375. data/lib/openc3/utilities/process_manager.rb +0 -0
  376. data/lib/openc3/utilities/pypi_url.rb +0 -0
  377. data/lib/openc3/utilities/python_proxy.rb +0 -0
  378. data/lib/openc3/utilities/python_venv.rb +135 -0
  379. data/lib/openc3/utilities/quaternion.rb +0 -0
  380. data/lib/openc3/utilities/questdb_client.rb +0 -0
  381. data/lib/openc3/utilities/redis_secrets.rb +0 -0
  382. data/lib/openc3/utilities/reingest_job.rb +0 -0
  383. data/lib/openc3/utilities/ruby_lex_utils.rb +0 -0
  384. data/lib/openc3/utilities/rubygems_url.rb +39 -0
  385. data/lib/openc3/utilities/running_script.rb +24 -55
  386. data/lib/openc3/utilities/s3_autoload.rb +5 -5
  387. data/lib/openc3/utilities/script.rb +12 -8
  388. data/lib/openc3/utilities/secrets.rb +48 -3
  389. data/lib/openc3/utilities/simulated_target.rb +0 -0
  390. data/lib/openc3/utilities/sleeper.rb +0 -0
  391. data/lib/openc3/utilities/store.rb +0 -0
  392. data/lib/openc3/utilities/store_autoload.rb +2 -2
  393. data/lib/openc3/utilities/store_queued.rb +0 -0
  394. data/lib/openc3/utilities/target_file.rb +18 -1
  395. data/lib/openc3/utilities/thread_manager.rb +0 -0
  396. data/lib/openc3/utilities/throttle.rb +0 -0
  397. data/lib/openc3/utilities/zip.rb +0 -0
  398. data/lib/openc3/utilities.rb +0 -0
  399. data/lib/openc3/version.rb +5 -5
  400. data/lib/openc3/win32/excel.rb +0 -0
  401. data/lib/openc3/win32/win32.rb +0 -0
  402. data/lib/openc3/win32/win32_main.rb +0 -0
  403. data/lib/openc3.rb +2 -2
  404. data/tasks/gemfile_stats.rake +0 -0
  405. data/tasks/spec.rake +0 -0
  406. data/templates/command_validator/command_validator.py +0 -0
  407. data/templates/command_validator/command_validator.rb +0 -0
  408. data/templates/conversion/conversion.py +0 -0
  409. data/templates/conversion/conversion.rb +0 -0
  410. data/templates/limits_response/response.py +0 -0
  411. data/templates/limits_response/response.rb +0 -0
  412. data/templates/microservice/microservices/TEMPLATE/microservice.py +0 -0
  413. data/templates/microservice/microservices/TEMPLATE/microservice.rb +0 -0
  414. data/templates/plugin/.gitignore +0 -0
  415. data/templates/plugin/LICENSE.md +0 -0
  416. data/templates/plugin/README.md +0 -0
  417. data/templates/plugin/Rakefile +0 -0
  418. data/templates/plugin/plugin.gemspec +0 -0
  419. data/templates/plugin/plugin.txt +0 -0
  420. data/templates/processor/processor.py +1 -1
  421. data/templates/processor/processor.rb +0 -0
  422. data/templates/target/targets/TARGET/cmd_tlm/cmd.txt +0 -0
  423. data/templates/target/targets/TARGET/cmd_tlm/tlm.txt +0 -0
  424. data/templates/target/targets/TARGET/lib/target.py +0 -0
  425. data/templates/target/targets/TARGET/lib/target.rb +0 -0
  426. data/templates/target/targets/TARGET/procedures/procedure.py +0 -0
  427. data/templates/target/targets/TARGET/procedures/procedure.rb +0 -0
  428. data/templates/target/targets/TARGET/public/README.txt +0 -0
  429. data/templates/target/targets/TARGET/screens/status.txt +0 -0
  430. data/templates/target/targets/TARGET/target.txt +0 -0
  431. data/templates/tool_angular/.editorconfig +0 -0
  432. data/templates/tool_angular/.gitignore +0 -0
  433. data/templates/tool_angular/.vscode/extensions.json +0 -0
  434. data/templates/tool_angular/.vscode/launch.json +0 -0
  435. data/templates/tool_angular/.vscode/tasks.json +0 -0
  436. data/templates/tool_angular/angular.json +0 -0
  437. data/templates/tool_angular/extra-webpack.config.js +0 -0
  438. data/templates/tool_angular/package.json +2 -2
  439. data/templates/tool_angular/src/app/app-routing.module.ts +0 -0
  440. data/templates/tool_angular/src/app/app.component.html +0 -0
  441. data/templates/tool_angular/src/app/app.component.scss +0 -0
  442. data/templates/tool_angular/src/app/app.component.spec.ts +0 -0
  443. data/templates/tool_angular/src/app/app.component.ts +0 -0
  444. data/templates/tool_angular/src/app/app.module.ts +0 -0
  445. data/templates/tool_angular/src/app/custom-overlay-container.ts +0 -0
  446. data/templates/tool_angular/src/app/empty-route/empty-route.component.ts +0 -0
  447. data/templates/tool_angular/src/app/openc3-api.d.ts +0 -0
  448. data/templates/tool_angular/src/assets/.gitkeep +0 -0
  449. data/templates/tool_angular/src/environments/environment.prod.ts +0 -0
  450. data/templates/tool_angular/src/environments/environment.ts +0 -0
  451. data/templates/tool_angular/src/favicon.ico +0 -0
  452. data/templates/tool_angular/src/index.html +0 -0
  453. data/templates/tool_angular/src/main.single-spa.ts +0 -0
  454. data/templates/tool_angular/src/single-spa/asset-url.ts +0 -0
  455. data/templates/tool_angular/src/single-spa/single-spa-props.ts +0 -0
  456. data/templates/tool_angular/src/styles.scss +0 -0
  457. data/templates/tool_angular/tsconfig.app.json +0 -0
  458. data/templates/tool_angular/tsconfig.json +0 -0
  459. data/templates/tool_angular/tsconfig.spec.json +0 -0
  460. data/templates/tool_react/.eslintrc +0 -0
  461. data/templates/tool_react/.gitignore +0 -0
  462. data/templates/tool_react/.prettierignore +0 -0
  463. data/templates/tool_react/babel.config.json +0 -0
  464. data/templates/tool_react/jest.config.js +0 -0
  465. data/templates/tool_react/package.json +1 -1
  466. data/templates/tool_react/src/openc3-tool_name.js +0 -0
  467. data/templates/tool_react/src/root.component.js +0 -0
  468. data/templates/tool_react/src/root.component.test.js +0 -0
  469. data/templates/tool_react/webpack.config.js +0 -0
  470. data/templates/tool_svelte/.gitignore +0 -0
  471. data/templates/tool_svelte/.prettierignore +0 -0
  472. data/templates/tool_svelte/.prettierrc.js +0 -0
  473. data/templates/tool_svelte/babel.config.js +0 -0
  474. data/templates/tool_svelte/build/smui.css +0 -0
  475. data/templates/tool_svelte/jest.config.js +0 -0
  476. data/templates/tool_svelte/package.json +1 -1
  477. data/templates/tool_svelte/rollup.config.js +0 -0
  478. data/templates/tool_svelte/src/App.svelte +0 -0
  479. data/templates/tool_svelte/src/App.test.js +0 -0
  480. data/templates/tool_svelte/src/theme/_smui-theme.scss +0 -0
  481. data/templates/tool_svelte/src/tool_name.js +0 -0
  482. data/templates/tool_vue/.env.standalone +0 -0
  483. data/templates/tool_vue/.gitignore +0 -0
  484. data/templates/tool_vue/.prettierrc.cjs +0 -0
  485. data/templates/tool_vue/eslint.config.mjs +0 -0
  486. data/templates/tool_vue/jsconfig.json +0 -0
  487. data/templates/tool_vue/package.json +3 -3
  488. data/templates/tool_vue/src/App.vue +0 -0
  489. data/templates/tool_vue/src/main.js +0 -0
  490. data/templates/tool_vue/src/router.js +0 -0
  491. data/templates/tool_vue/src/tools/tool_name/tool_name.vue +0 -0
  492. data/templates/tool_vue/vite.config.js +1 -1
  493. data/templates/widget/.prettierrc.cjs +0 -0
  494. data/templates/widget/LICENSE.md +0 -0
  495. data/templates/widget/Rakefile +0 -0
  496. data/templates/widget/package.json +2 -2
  497. data/templates/widget/src/Widget.vue +0 -0
  498. data/templates/widget/vite.config.js +0 -0
  499. metadata +36 -9
@@ -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
File without changes
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
@@ -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
File without changes
File without changes
File without changes
File without changes
@@ -99,7 +99,7 @@ module OpenC3
99
99
 
100
100
  Dir.each_child(path) do |filename|
101
101
  full_path = "#{path}/#{filename}"
102
- if not File.directory?(full_path)
102
+ unless File.directory?(full_path)
103
103
  if File.extname(filename) == '.gem'
104
104
  gems << full_path
105
105
  elsif filename == 'plugin_instance.json'
@@ -283,7 +283,7 @@ module OpenC3
283
283
  return if DEFAULT_PLUGINS.include?(gem_name)
284
284
  puts "Updating local plugin files: #{full_folder_path}"
285
285
  FileUtils.mkdir_p(full_folder_path)
286
- gems, _ = scan_plugin_dir(full_folder_path)
286
+ gems = scan_plugin_dir(full_folder_path).first
287
287
  gems.each do |gem|
288
288
  File.delete(gem)
289
289
  end
@@ -377,7 +377,7 @@ module OpenC3
377
377
  end
378
378
  end
379
379
 
380
- def self.put_target_file(path, io_or_string, scope:)
380
+ def self.put_target_file(path, io_or_string, scope:) # NOSONAR - scope: is part of the caller-facing signature
381
381
  full_folder_path = "#{OPENC3_LOCAL_MODE_PATH}/#{path}"
382
382
  return unless File.expand_path(full_folder_path).start_with?(OPENC3_LOCAL_MODE_PATH)
383
383
  FileUtils.mkdir_p(File.dirname(full_folder_path))
@@ -468,16 +468,42 @@ module OpenC3
468
468
  end
469
469
 
470
470
  def self.sync_settings()
471
+ # The files are authoritative here, unlike the OPENC3_SETTING_* env var
472
+ # seeding done by `openc3cli initsettings`. An Admin Console edit is
473
+ # mirrored straight back into the file by set_setting, so the file is
474
+ # already the edited value and applying it reverts nothing. Skipping a
475
+ # setting that already exists would instead mean a file was never applied
476
+ # at all: localinit runs after the first plugin load creates the scope,
477
+ # and that seeds source_url, rubygems_url, pypi_url, news_feed and
478
+ # system_health, so those five always exist by the time this runs.
479
+ #
480
+ # Because initsettings runs before localinit, a local mode file wins over
481
+ # OPENC3_SETTING_<NAME> for the same setting.
482
+ #
483
+ # A file that won't coerce is reported and skipped rather than raising,
484
+ # for the same reason `initsettings` doesn't abort: one bad settings file
485
+ # shouldn't stop the rest of localinit.
486
+ problems = []
471
487
  scopes = ScopeModel.names()
472
488
  scopes.each do |scope|
473
489
  Dir["#{OPENC3_LOCAL_MODE_PATH}/#{scope}/settings/*.json"].each do |config|
474
490
  name = File.basename(config, ".json")
491
+ # save_setting writes a boolean as the JSON text "true"/"false", so
492
+ # convert it back rather than storing the file contents verbatim -
493
+ # the string "false" is truthy in the frontend. Settings that hold
494
+ # JSON text (classification_banner, astro) stay strings, which is
495
+ # what their components JSON.parse.
496
+ begin
497
+ data = SettingModel.coerce(name, File.read(config))
498
+ rescue StandardError => error
499
+ problems << "#{config}: #{error.message}"
500
+ next
501
+ end
475
502
  puts "Syncing setting #{name}"
476
- # Anything can be stored in settings so read and set directly
477
- data = File.read(config)
478
503
  SettingModel.set({ name: name, data: data }, scope: scope)
479
504
  end
480
505
  end
506
+ problems.each { |problem| puts "ERROR: #{problem}" }
481
507
  end
482
508
 
483
509
  def self.save_setting(scope, name, data)
@@ -582,7 +608,7 @@ module OpenC3
582
608
  # Both files exist - Handled earlier
583
609
  else
584
610
  # Local is missing remote file
585
- if not ENV['OPENC3_LOCAL_MODE_SECONDARY'] and ENV['OPENC3_LOCAL_MODE_SYNC_REMOVE']
611
+ if not ENV.fetch('OPENC3_LOCAL_MODE_SECONDARY', nil) and ENV.fetch('OPENC3_LOCAL_MODE_SYNC_REMOVE', nil)
586
612
  delete_remote(bucket, key)
587
613
  else
588
614
  # Go ahead and copy down to get in sync
@@ -42,39 +42,61 @@ module OpenC3
42
42
 
43
43
  @@mutex = Mutex.new
44
44
  @@instance = nil
45
- @@scope = ENV['OPENC3_SCOPE']
45
+ @@scope = ENV.fetch('OPENC3_SCOPE', 'DEFAULT')
46
46
 
47
- # DEBUG only prints DEBUG messages
47
+ # Levels in increasing severity. Logging at a level prints that level
48
+ # and everything above it, e.g. WARN prints WARN, ERROR and FATAL.
48
49
  DEBUG = ::Logger::DEBUG
49
- # INFO prints INFO, DEBUG messages
50
50
  INFO = ::Logger::INFO
51
- # WARN prints WARN, INFO, DEBUG messages
52
51
  WARN = ::Logger::WARN
53
- # ERROR prints ERROR, WARN, INFO, DEBUG messages
54
52
  ERROR = ::Logger::ERROR
55
- # FATAL prints FATAL, ERROR, WARN, INFO, DEBUG messages
56
53
  FATAL = ::Logger::FATAL
57
54
 
55
+ # Level names as they appear in log messages and OPENC3_LOG_LEVEL
58
56
  DEBUG_LEVEL = 'DEBUG'
59
57
  INFO_LEVEL = 'INFO'
60
58
  WARN_LEVEL = 'WARN'
61
59
  ERROR_LEVEL = 'ERROR'
62
60
  FATAL_LEVEL = 'FATAL'
63
61
 
62
+ LEVELS = {
63
+ DEBUG_LEVEL => DEBUG,
64
+ INFO_LEVEL => INFO,
65
+ WARN_LEVEL => WARN,
66
+ ERROR_LEVEL => ERROR,
67
+ FATAL_LEVEL => FATAL
68
+ }.freeze
69
+
64
70
  # Types
65
71
  LOG = 'log'
66
72
  NOTIFICATION = 'notification'
67
73
  ALERT = 'alert'
68
74
  EPHEMERAL = 'ephemeral'
69
75
 
76
+ # @return [Integer] Level named by the OPENC3_LOG_LEVEL env var.
77
+ # INFO if the var is unset or isn't a level name.
78
+ def self.default_level
79
+ level = ENV.fetch('OPENC3_LOG_LEVEL', '')
80
+ name = level.to_s.strip.upcase
81
+ return INFO if name.empty?
82
+
83
+ LEVELS.fetch(name) do
84
+ unless @warned_bad_level
85
+ @warned_bad_level = true
86
+ $stderr.puts "OPENC3_LOG_LEVEL '#{level}' is not one of #{LEVELS.keys.join(', ')}, using INFO"
87
+ end
88
+ INFO
89
+ end
90
+ end
91
+
70
92
  # @param level [Integer] The initial logging level
71
- def initialize(level = Logger::INFO)
93
+ def initialize(level = Logger.default_level)
72
94
  @stdout = true
73
95
  @level = level
74
96
  @detail_string = nil
75
97
  @container_name = Socket.gethostname
76
98
  @microservice_name = nil
77
- @no_store = ENV['OPENC3_NO_STORE']
99
+ @no_store = ENV.fetch('OPENC3_NO_STORE', nil)
78
100
  end
79
101
 
80
102
  # Only set the microservice name once (to help with multi microservices)
File without changes
File without changes
File without changes