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
@@ -20,14 +20,50 @@ module OpenC3
20
20
  class WebSocketApi
21
21
  USER_AGENT = 'OpenC3 / v7 (ruby/openc3/lib/io/web_socket_api)'.freeze
22
22
 
23
+ # Options every websocket api accepts, and their defaults. Subclasses
24
+ # forward **options rather than restating these.
25
+ DEFAULT_OPTIONS = {
26
+ write_timeout: 10.0,
27
+ read_timeout: 10.0,
28
+ connect_timeout: 5.0,
29
+ authentication: nil,
30
+ }.freeze
31
+
32
+ # Build a cable URL from the standard OPENC3 environment variable quartet:
33
+ # <prefix>_SCHEMA, <prefix>_HOSTNAME, <prefix>_CABLE_PORT, <prefix>_PORT
34
+ def self.cable_url(env_prefix:, default_hostname:, default_port:, path:)
35
+ schema = ENV.fetch("#{env_prefix}_SCHEMA", 'http')
36
+ # Normalize to the websocket schemes (mirrors the Python client). The
37
+ # websocket gem accepts http/https too, but ws/wss is what the URL
38
+ # actually is and Python's websockets library rejects anything else.
39
+ schema = 'ws' if schema == 'http'
40
+ schema = 'wss' if schema == 'https'
41
+ hostname = ENV.fetch("#{env_prefix}_HOSTNAME", nil) || (ENV['OPENC3_DEVEL'] ? '127.0.0.1' : default_hostname)
42
+ port = (ENV.fetch("#{env_prefix}_CABLE_PORT", nil) || ENV.fetch("#{env_prefix}_PORT", default_port)).to_i
43
+ return "#{schema}://#{hostname}:#{port}#{path}"
44
+ end
45
+
23
46
  # Create the WebsocketApi object. If a block is given will automatically connect/disconnect
24
- def initialize(url:, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope, &block)
25
- @scope = scope
26
- @authentication = authentication.nil? ? generate_auth() : authentication
47
+ #
48
+ # @param url [String] The cable URL to connect to
49
+ # @param scope [String] The scope to connect with
50
+ # @param options [Hash] See DEFAULT_OPTIONS
51
+ def initialize(url:, scope: $openc3_scope, **options, &block)
52
+ # Restore the arity checking that explicit keyword arguments used to give
53
+ # us, so a typo'd option is an error rather than a silently ignored value
54
+ unknown = options.keys - DEFAULT_OPTIONS.keys
55
+ raise ArgumentError, "unknown keyword#{'s' if unknown.length > 1}: #{unknown.join(', ')}" unless unknown.empty?
56
+
57
+ options = DEFAULT_OPTIONS.merge(options)
58
+ # $openc3_scope is only set inside the Script Runner / microservice
59
+ # environment. Fall back to OPENC3_SCOPE (mirrors the Python client) so a
60
+ # bare `ruby script.rb` doesn't send a nil scope that the server rejects.
61
+ @scope = scope || ENV.fetch('OPENC3_SCOPE', 'DEFAULT')
62
+ @authentication = options[:authentication] || generate_auth()
27
63
  @url = url
28
- @write_timeout = write_timeout
29
- @read_timeout = read_timeout
30
- @connect_timeout = connect_timeout
64
+ @write_timeout = options[:write_timeout]
65
+ @read_timeout = options[:read_timeout]
66
+ @connect_timeout = options[:connect_timeout]
31
67
  @subscribed = false
32
68
  if block_given?
33
69
  begin
@@ -55,23 +91,11 @@ module OpenC3
55
91
  # loops exit cleanly instead of hitting JSON::ParserError on JSON.parse("").
56
92
  return nil if message.nil? || message.empty?
57
93
 
58
- begin
59
- json_hash = JSON.parse(message, allow_nan: true, create_additions: true)
60
- rescue JSON::ParserError
61
- # Defense-in-depth: treat malformed frames as end-of-stream rather than crashing.
62
- return nil
63
- end
94
+ json_hash = parse_message(message)
64
95
  if ignore_protocol_messages
65
96
  type = json_hash['type']
66
97
  if type # ping, welcome, confirm_subscription, reject_subscription, disconnect
67
- if type == 'disconnect'
68
- if json_hash['reason'] == 'unauthorized'
69
- raise "Unauthorized"
70
- end
71
- end
72
- if type == 'reject_subscription'
73
- raise "Subscription Rejected"
74
- end
98
+ check_protocol_frame(json_hash)
75
99
  if timeout
76
100
  end_time = Time.now
77
101
  if (end_time - start_time) > timeout
@@ -95,32 +119,51 @@ module OpenC3
95
119
  # ApplicationCable::Channel#authenticate_subscription! — ActionCable
96
120
  # ignores `data` on `subscribe` commands.
97
121
  @identifier['token'] = @authentication.token(include_bearer: false)
98
- json_hash = {}
99
- json_hash['command'] = 'subscribe'
100
- json_hash['identifier'] = JSON.generate(@identifier, allow_nan: true)
101
- @stream.write(JSON.generate(json_hash, allow_nan: true))
122
+ write_command('subscribe')
102
123
  @subscribed = true
124
+ wait_for_subscribed()
125
+ end
126
+ end
127
+
128
+ # Block until the server confirms the subscription. ActionCable / anycable-go
129
+ # process 'subscribe' and 'message' commands as independent RPCs, so an action
130
+ # (add/remove) written immediately after subscribe can reach
131
+ # StreamingChannel#add before the subscription's broadcaster exists, where it
132
+ # is silently dropped (a no-op) and no data ever streams. Waiting for
133
+ # confirm_subscription guarantees the broadcaster is ready before any action
134
+ # is written.
135
+ def wait_for_subscribed
136
+ while true
137
+ message = @stream.read
138
+ # Unlike #read, end-of-stream is fatal here rather than a nil return:
139
+ # a socket that closes mid-handshake leaves nothing to carry on with.
140
+ raise "WebSocket closed before subscription was confirmed" if message.nil? || message.empty?
141
+
142
+ json_hash = parse_message(message)
143
+ check_protocol_frame(json_hash)
144
+ # Ignore welcome / ping and keep waiting for confirmation
145
+ return if json_hash['type'] == 'confirm_subscription'
103
146
  end
104
147
  end
105
148
 
106
149
  # Will unsubscribe to the channel based on @identifier
107
150
  def unsubscribe
108
151
  if @subscribed
109
- json_hash = {}
110
- json_hash['command'] = 'unsubscribe'
111
- json_hash['identifier'] = JSON.generate(@identifier, allow_nan: true)
112
- @stream.write(JSON.generate(json_hash, allow_nan: true))
152
+ write_command('unsubscribe')
113
153
  @subscribed = false
114
154
  end
115
155
  end
116
156
 
117
157
  # Send an ActionCable command
118
158
  def write_action(data_hash)
119
- json_hash = {}
120
- json_hash['command'] = 'message'
121
- json_hash['identifier'] = JSON.generate(@identifier, allow_nan: true)
122
- json_hash['data'] = JSON.generate(data_hash, allow_nan: true)
123
- write(JSON.generate(json_hash, allow_nan: true))
159
+ # Subscribe first so the token is present in @identifier before we
160
+ # serialize it below. ActionCable matches a 'message' command to its
161
+ # subscription by the exact identifier string; if subscribe() injected the
162
+ # token only afterward, the message identifier (no token) would not match
163
+ # the subscription identifier (with token) and the server would silently
164
+ # ignore the action.
165
+ subscribe()
166
+ write_command('message', data_hash)
124
167
  end
125
168
 
126
169
  # General write to the websocket
@@ -158,12 +201,49 @@ module OpenC3
158
201
  rescue
159
202
  # Oh well, we tried
160
203
  end
204
+ # unsubscribe only clears this after a successful write. The stream is
205
+ # being closed regardless, so it cannot remain subscribed.
206
+ @subscribed = false
161
207
  @stream.disconnect
208
+ else
209
+ @subscribed = false
162
210
  end
163
211
  end
164
212
 
165
213
  # private
166
214
 
215
+ # Write an ActionCable command frame for the current @identifier. Writes
216
+ # straight to the stream because the callers have already subscribed (and
217
+ # subscribe itself must not recurse through write).
218
+ def write_command(command, data_hash = nil)
219
+ json_hash = {}
220
+ json_hash['command'] = command
221
+ json_hash['identifier'] = JSON.generate(@identifier, allow_nan: true)
222
+ json_hash['data'] = JSON.generate(data_hash, allow_nan: true) if data_hash
223
+ @stream.write(JSON.generate(json_hash, allow_nan: true))
224
+ end
225
+
226
+ # Parse a server frame. Kept in one place so the JSON options cannot drift
227
+ # between the two readers.
228
+ def parse_message(message)
229
+ return JSON.parse(message, allow_nan: true, create_additions: true)
230
+ end
231
+
232
+ # Apply the protocol rules shared by #read and #wait_for_subscribed
233
+ def check_protocol_frame(json_hash)
234
+ case json_hash['type']
235
+ when 'reject_subscription'
236
+ raise "Subscription Rejected"
237
+ when 'disconnect'
238
+ # Any other disconnect reason is not fatal; the caller keeps reading
239
+ raise "Unauthorized" if json_hash['reason'] == 'unauthorized'
240
+ else
241
+ # ping, welcome, confirm_subscription and anything new the server adds
242
+ # are informational only
243
+ nil
244
+ end
245
+ end
246
+
167
247
  # Generate the appropriate token for OpenC3
168
248
  def generate_auth
169
249
  if ENV['OPENC3_API_TOKEN'].nil? and ENV['OPENC3_API_USER'].nil?
@@ -178,62 +258,119 @@ module OpenC3
178
258
  end
179
259
  end
180
260
 
261
+ # Identifier for channels whose only parameter is the event history count.
262
+ # Including classes supply the channel name as a CHANNEL constant.
263
+ module HistoryCountIdentifier
264
+ def initialize(history_count: 0, **options)
265
+ @identifier = {
266
+ channel: self.class::CHANNEL,
267
+ history_count: history_count
268
+ }
269
+ super(**options)
270
+ end
271
+ end
272
+
181
273
  # Base class for cmd-tlm-api websockets - Do not use directly
182
274
  class CmdTlmWebSocketApi < WebSocketApi
183
- def initialize(url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
275
+ def initialize(url: nil, **options)
184
276
  url = generate_url() unless url
185
- super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
277
+ super(url: url, **options)
186
278
  end
187
279
 
188
280
  def generate_url
189
- schema = ENV['OPENC3_API_SCHEMA'] || 'http'
190
- hostname = ENV['OPENC3_API_HOSTNAME'] || (ENV['OPENC3_DEVEL'] ? '127.0.0.1' : 'openc3-cosmos-cmd-tlm-api')
191
- port = ENV['OPENC3_API_CABLE_PORT'] || ENV['OPENC3_API_PORT'] || '3901'
192
- port = port.to_i
193
- return "#{schema}://#{hostname}:#{port}/openc3-api/cable"
281
+ return WebSocketApi.cable_url(
282
+ env_prefix: 'OPENC3_API',
283
+ default_hostname: 'openc3-cosmos-cmd-tlm-api',
284
+ default_port: '3901',
285
+ path: '/openc3-api/cable'
286
+ )
194
287
  end
195
288
  end
196
289
 
197
290
  # Base class for script-runner-api websockets - Do not use directly
198
291
  class ScriptWebSocketApi < WebSocketApi
199
- def initialize(url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
292
+ def initialize(url: nil, **options)
200
293
  url = generate_url() unless url
201
- super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
294
+ super(url: url, **options)
202
295
  end
203
296
 
204
297
  def generate_url
205
- schema = ENV['OPENC3_SCRIPT_API_SCHEMA'] || 'http'
206
- hostname = ENV['OPENC3_SCRIPT_API_HOSTNAME'] || (ENV['OPENC3_DEVEL'] ? '127.0.0.1' : 'openc3-cosmos-script-runner-api')
207
- port = ENV['OPENC3_SCRIPT_API_CABLE_PORT'] || ENV['OPENC3_SCRIPT_API_PORT'] || '3902'
208
- port = port.to_i
209
- return "#{schema}://#{hostname}:#{port}/script-api/cable"
298
+ return WebSocketApi.cable_url(
299
+ env_prefix: 'OPENC3_SCRIPT_API',
300
+ default_hostname: 'openc3-cosmos-script-runner-api',
301
+ default_port: '3902',
302
+ path: '/script-api/cable'
303
+ )
210
304
  end
211
305
  end
212
306
 
213
307
  # Running Script WebSocket
214
308
  class RunningScriptWebSocketApi < ScriptWebSocketApi
215
- def initialize(id:, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
309
+ def initialize(id:, **options)
310
+ @pending_events = []
216
311
  @identifier = {
217
312
  channel: "RunningScriptChannel",
218
313
  id: id
219
314
  }
220
- super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
315
+ super(**options)
316
+ end
317
+
318
+ # The backlog of script events is transmitted with the subscription
319
+ # confirmation, but LIVE events only flow once the client reports that it is
320
+ # ready to stream events (see RunningScriptChannel#ready) -- a broadcast sent
321
+ # before the gateway has registered this subscription's stream would be
322
+ # silently dropped. subscribe() blocks until confirm_subscription, so the
323
+ # 'ready' action is guaranteed to arrive after the stream is registered.
324
+ def subscribe
325
+ was_subscribed = @subscribed
326
+ super
327
+ write_action({ 'action' => 'ready' }) unless was_subscribed
328
+ end
329
+
330
+ # RunningScriptChannel batches events to reduce ActionCable frame and
331
+ # client-dispatch overhead. Preserve the public API's historical contract:
332
+ # callers still receive one script event from each read.
333
+ def read(ignore_protocol_messages: true, timeout: nil)
334
+ return @pending_events.shift unless @pending_events.empty?
335
+
336
+ loop do
337
+ message = super
338
+ return message unless message.is_a?(Array)
339
+
340
+ @pending_events.concat(message)
341
+ return @pending_events.shift unless @pending_events.empty?
342
+ end
343
+ end
344
+
345
+ def connect
346
+ @pending_events.clear
347
+ super
348
+ end
349
+
350
+ def unsubscribe
351
+ @pending_events.clear
352
+ super
353
+ end
354
+
355
+ def disconnect
356
+ @pending_events.clear
357
+ super
221
358
  end
222
359
  end
223
360
 
224
361
  # All Scripts WebSocket
225
362
  class AllScriptsWebSocketApi < ScriptWebSocketApi
226
- def initialize(url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
363
+ def initialize(**options)
227
364
  @identifier = {
228
365
  channel: "AllScriptsChannel",
229
366
  }
230
- super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
367
+ super(**options)
231
368
  end
232
369
  end
233
370
 
234
371
  # Log Messages WebSocket
235
372
  class MessagesWebSocketApi < CmdTlmWebSocketApi
236
- def initialize(history_count: 0, start_time: nil, end_time: nil, level: nil, types: nil, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
373
+ def initialize(history_count: 0, start_time: nil, end_time: nil, level: nil, types: nil, **options)
237
374
  @identifier = {
238
375
  channel: "MessagesChannel",
239
376
  history_count: history_count
@@ -242,94 +379,59 @@ module OpenC3
242
379
  @identifier['end_time'] = end_time if end_time
243
380
  @identifier['level'] = level if level
244
381
  @identifier['types'] = types if types
245
- super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
382
+ super(**options)
246
383
  end
247
384
  end
248
385
 
249
386
  # Autonomic Events WebSocket (Enterprise Only)
250
387
  class AutonomicEventsWebSocketApi < CmdTlmWebSocketApi
251
- def initialize(history_count: 0, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
252
- @identifier = {
253
- channel: "AutonomicEventsChannel",
254
- history_count: history_count
255
- }
256
- super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
257
- end
388
+ CHANNEL = 'AutonomicEventsChannel'.freeze
389
+ include HistoryCountIdentifier
258
390
  end
259
391
 
260
392
  # Calendar Events WebSocket (Enterprise Only)
261
393
  class CalendarEventsWebSocketApi < CmdTlmWebSocketApi
262
- def initialize(history_count: 0, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
263
- @identifier = {
264
- channel: "CalendarEventsChannel",
265
- history_count: history_count
266
- }
267
- super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
268
- end
394
+ CHANNEL = 'CalendarEventsChannel'.freeze
395
+ include HistoryCountIdentifier
269
396
  end
270
397
 
271
398
  # Config Events WebSocket
272
399
  class ConfigEventsWebSocketApi < CmdTlmWebSocketApi
273
- def initialize(history_count: 0, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
274
- @identifier = {
275
- channel: "ConfigEventsChannel",
276
- history_count: history_count
277
- }
278
- super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
279
- end
400
+ CHANNEL = 'ConfigEventsChannel'.freeze
401
+ include HistoryCountIdentifier
280
402
  end
281
403
 
282
404
  # Limits Events WebSocket
283
405
  class LimitsEventsWebSocketApi < CmdTlmWebSocketApi
284
- def initialize(history_count: 0, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
285
- @identifier = {
286
- channel: "LimitsEventsChannel",
287
- history_count: history_count
288
- }
289
- super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
290
- end
406
+ CHANNEL = 'LimitsEventsChannel'.freeze
407
+ include HistoryCountIdentifier
291
408
  end
292
409
 
293
410
  # System Events WebSocket
294
411
  class SystemEventsWebSocketApi < CmdTlmWebSocketApi
295
- def initialize(history_count: 0, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
296
- @identifier = {
297
- channel: "SystemEventsChannel",
298
- history_count: history_count
299
- }
300
- super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
301
- end
412
+ CHANNEL = 'SystemEventsChannel'.freeze
413
+ include HistoryCountIdentifier
302
414
  end
303
415
 
304
416
  # Timeline WebSocket
305
417
  class TimelineEventsWebSocketApi < CmdTlmWebSocketApi
306
- def initialize(history_count: 0, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
307
- @identifier = {
308
- channel: "TimelineEventsChannel",
309
- history_count: history_count
310
- }
311
- super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
312
- end
418
+ CHANNEL = 'TimelineEventsChannel'.freeze
419
+ include HistoryCountIdentifier
313
420
  end
314
421
 
315
422
  # Queue WebSocket
316
423
  class QueueEventsWebSocketApi < CmdTlmWebSocketApi
317
- def initialize(history_count: 0, url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
318
- @identifier = {
319
- channel: "QueueEventsChannel",
320
- history_count: history_count
321
- }
322
- super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
323
- end
424
+ CHANNEL = 'QueueEventsChannel'.freeze
425
+ include HistoryCountIdentifier
324
426
  end
325
427
 
326
428
  # Streaming API WebSocket
327
429
  class StreamingWebSocketApi < CmdTlmWebSocketApi
328
- def initialize(url: nil, write_timeout: 10.0, read_timeout: 10.0, connect_timeout: 5.0, authentication: nil, scope: $openc3_scope)
430
+ def initialize(**options)
329
431
  @identifier = {
330
432
  channel: "StreamingChannel"
331
433
  }
332
- super(url: url, write_timeout: write_timeout, read_timeout: read_timeout, connect_timeout: connect_timeout, authentication: authentication, scope: scope)
434
+ super(**options)
333
435
  end
334
436
 
335
437
  # Request to add data to the stream
@@ -354,26 +456,11 @@ module OpenC3
354
456
  # PACKET - Packet name
355
457
  # VALUETYPE - RAW, CONVERTED, FORMATTED, or PURE (pure means all types as stored in log)
356
458
  #
357
- def add(items: nil, packets: nil, start_time: nil, end_time: nil, scope: $openc3_scope)
358
- data_hash = {}
359
- data_hash['action'] = 'add'
360
- if start_time
361
- if Time === start_time
362
- start_time = start_time.to_nsec_from_epoch
363
- end
364
- data_hash['start_time'] = start_time
365
- end
366
- if end_time
367
- if Time === end_time
368
- end_time = end_time.to_nsec_from_epoch
369
- end
370
- data_hash['end_time'] = end_time
371
- end
372
- data_hash['items'] = items if items
373
- data_hash['packets'] = packets if packets
374
- data_hash['scope'] = scope
375
- data_hash['token'] = @authentication.token(include_bearer: false)
376
- write_action(data_hash)
459
+ def add(items: nil, packets: nil, start_time: nil, end_time: nil, scope: nil)
460
+ times = {}
461
+ times['start_time'] = to_nsec(start_time) if start_time
462
+ times['end_time'] = to_nsec(end_time) if end_time
463
+ stream_action('add', items: items, packets: packets, scope: scope, extra: times)
377
464
  end
378
465
 
379
466
  # Request to remove data from the stream
@@ -395,26 +482,32 @@ module OpenC3
395
482
  # PACKET - Packet name
396
483
  # VALUETYPE - RAW, CONVERTED, FORMATTED, or PURE (pure means all types as stored in log)
397
484
  #
398
- def remove(items: nil, packets: nil, scope: $openc3_scope)
399
- data_hash = {}
400
- data_hash['action'] = 'remove'
401
- data_hash['items'] = items if items
402
- data_hash['packets'] = packets if packets
403
- data_hash['scope'] = scope
404
- data_hash['token'] = @authentication.token(include_bearer: false)
405
- write_action(data_hash)
485
+ def remove(items: nil, packets: nil, scope: nil)
486
+ stream_action('remove', items: items, packets: packets, scope: scope)
406
487
  end
407
488
 
408
489
  # Convenience method to read all data until end marker is received.
490
+ # Omitting end_time streams realtime and endlessly: no end marker is ever
491
+ # sent, so a timeout is the only way the collection ends on its own.
409
492
  # Warning: DATA IS STORED IN RAM. Do not use this with large queries
410
- def self.read_all(items: nil, packets: nil, start_time: nil, end_time:, scope: $openc3_scope, timeout: nil)
493
+ def self.read_all(items: nil, packets: nil, start_time: nil, end_time: nil, scope: nil, timeout: nil)
411
494
  read_all_start_time = Time.now
412
495
  data = []
413
496
  self.new do |api|
414
497
  api.add(items: items, packets: packets, start_time: start_time, end_time: end_time, scope: scope)
415
498
  while true
416
499
  batch = api.read
417
- if batch.length == 0
500
+ if batch.nil?
501
+ # A bounded query must receive its end marker; a truncated result
502
+ # returned as if complete is worse than an error. A realtime query
503
+ # never gets one, so a close is an ordinary way for it to end.
504
+ raise "WebSocket closed before end marker" if end_time
505
+
506
+ return data
507
+ end
508
+ # An empty batch is the explicit end marker sent after a historical
509
+ # query is complete.
510
+ if batch.empty?
418
511
  return data
419
512
  else
420
513
  data.concat(batch)
@@ -427,6 +520,26 @@ module OpenC3
427
520
  end
428
521
  end
429
522
  end
523
+
524
+ # private
525
+
526
+ # Accept either a Time or an already converted 64-bit nanosecond value
527
+ def to_nsec(value)
528
+ return Time === value ? value.to_nsec_from_epoch : value
529
+ end
530
+
531
+ # Build and write a StreamingChannel action. extra carries action specific
532
+ # keys (the times for 'add') and is merged first to preserve wire ordering.
533
+ def stream_action(action, items:, packets:, scope:, extra: {})
534
+ data_hash = {}
535
+ data_hash['action'] = action
536
+ data_hash.merge!(extra)
537
+ data_hash['items'] = items if items
538
+ data_hash['packets'] = packets if packets
539
+ data_hash['scope'] = scope || @scope
540
+ data_hash['token'] = @authentication.token(include_bearer: false)
541
+ write_action(data_hash)
542
+ end
430
543
  end
431
544
  end
432
545
 
data/lib/openc3/script.rb CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
data/lib/openc3/system.rb CHANGED
File without changes
File without changes
@@ -253,6 +253,13 @@ module OpenC3
253
253
 
254
254
  def connect
255
255
  Logger.info "Connecting to #{@interface.name}..."
256
+ # Interface connect implementations typically overwrite their stream / socket
257
+ # so cleanly close any existing connection rather than leaking it
258
+ begin
259
+ @interface.disconnect if @interface.connected?
260
+ rescue => err
261
+ Logger.error "Disconnect: #{@interface.name}: #{err.formatted}"
262
+ end
256
263
  @interface.connect
257
264
  if @connection_success_callback
258
265
  @connection_success_callback.call
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -503,7 +503,7 @@ end
503
503
  # New plugins should only use openc3 paths and module OpenC3
504
504
  unless ENV['OPENC3_NO_COSMOS_COMPATIBILITY']
505
505
  Cosmos = OpenC3
506
- ENV['COSMOS_SCOPE'] = ENV['OPENC3_SCOPE']
506
+ ENV['COSMOS_SCOPE'] = ENV.fetch('OPENC3_SCOPE', 'DEFAULT')
507
507
 
508
508
  # Don't apply the compatibility layer in test environments to avoid conflicts with RSpec
509
509
  unless defined?(RSpec) || ENV['RAILS_ENV'] == 'test' || ENV['RACK_ENV'] == 'test'
File without changes
File without changes
@@ -30,13 +30,28 @@ module OpenC3
30
30
  received_count: packet.received_count }
31
31
  # Read all RAW values at once - optimized by accessor
32
32
  json_hash = packet.read_items(packet.sorted_items)
33
+ # Items with a write conversion have already been transformed by the time the
34
+ # value lands in the buffer, so reading the buffer back does not recover what
35
+ # the user actually commanded. Use the given values (as passed to build_cmd)
36
+ # for those items. State items are the exception: the user can give either the
37
+ # state name or the state value, so they always read from the buffer to log the
38
+ # normalized state name. Raw commands bypass the write conversions entirely so
39
+ # their given values are raw and must not be logged as converted.
40
+ given_values = packet.raw ? nil : packet.given_values
41
+ given_values = given_values.transform_keys { |key| key.to_s.upcase } if given_values
33
42
  # Read additional value types using given_raw to avoid re-reading from buffer
34
43
  packet.sorted_items.each do |item|
35
44
  if item.hidden
36
45
  json_hash.delete(item.name)
37
46
  else
38
47
  given_raw = json_hash[item.name]
39
- json_hash[item.name + "__C"] = packet.read_item(item, :CONVERTED, packet.buffer, given_raw) if item.write_conversion or item.states
48
+ if item.write_conversion or item.states
49
+ if item.write_conversion and !item.states and given_values and given_values.key?(item.name)
50
+ json_hash[item.name + "__C"] = given_values[item.name]
51
+ else
52
+ json_hash[item.name + "__C"] = packet.read_item(item, :CONVERTED, packet.buffer, given_raw)
53
+ end
54
+ end
40
55
  json_hash[item.name + "__F"] = packet.read_item(item, :FORMATTED, packet.buffer, given_raw) if item.format_string
41
56
  end
42
57
  end
File without changes