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
@@ -106,6 +106,13 @@ module OpenC3
106
106
  when 4
107
107
  string_length = string_length.unpack('N')[0] # UINT32
108
108
  raise "Length value received larger than max_length: #{string_length} > #{@max_length}" if @max_length and string_length > @max_length
109
+
110
+ # Reject up front rather than buffering the declared number of bytes first
111
+ if string_length > @max_buffer_size
112
+ reset() # Drop the partial frame rather than holding it until disconnect
113
+ raise "Length value received of #{string_length} bytes exceeds maximum buffer size of #{@max_buffer_size} bytes. " \
114
+ "Increase OPENC3_PROTOCOL_MAX_BUFFER_SIZE."
115
+ end
109
116
  else
110
117
  raise "Unsupported length given to read_length_field_followed_by_string: #{length_num_bytes}"
111
118
  end
@@ -131,13 +131,13 @@ module OpenC3
131
131
  def write_details
132
132
  result = {'name' => self.class.name.to_s.split("::")[-1]}
133
133
  if @write_data_input_time
134
- result['write_data_input_time'] = @write_data_input_time.iso8601
134
+ result['write_data_input_time'] = @write_data_input_time.getutc.iso8601(6)
135
135
  else
136
136
  result['write_data_input_time'] = nil
137
137
  end
138
138
  result['write_data_input'] = @write_data_input
139
139
  if @write_data_output_time
140
- result['write_data_output_time'] = @write_data_output_time.iso8601
140
+ result['write_data_output_time'] = @write_data_output_time.getutc.iso8601(6)
141
141
  else
142
142
  result['write_data_output_time'] = nil
143
143
  end
@@ -148,13 +148,13 @@ module OpenC3
148
148
  def read_details
149
149
  result = {'name' => self.class.name.to_s.split("::")[-1]}
150
150
  if @read_data_input_time
151
- result['read_data_input_time'] = @read_data_input_time.iso8601
151
+ result['read_data_input_time'] = @read_data_input_time.getutc.iso8601(6)
152
152
  else
153
153
  result['read_data_input_time'] = nil
154
154
  end
155
155
  result['read_data_input'] = @read_data_input
156
156
  if @read_data_output_time
157
- result['read_data_output_time'] = @read_data_output_time.iso8601
157
+ result['read_data_output_time'] = @read_data_output_time.getutc.iso8601(6)
158
158
  else
159
159
  result['read_data_output_time'] = nil
160
160
  end
File without changes
@@ -266,7 +266,7 @@ module OpenC3
266
266
  result['response_timeout'] = @response_timeout
267
267
  result['response_polling_period'] = @response_polling_period
268
268
  if @connect_complete_time
269
- result['connect_complete_time'] = @connect_complete_time.iso8601
269
+ result['connect_complete_time'] = @connect_complete_time.getutc.iso8601(6)
270
270
  else
271
271
  result['connect_complete_time'] = nil
272
272
  end
@@ -285,7 +285,7 @@ module OpenC3
285
285
  result['response_timeout'] = @response_timeout
286
286
  result['response_polling_period'] = @response_polling_period
287
287
  if @connect_complete_time
288
- result['connect_complete_time'] = @connect_complete_time.iso8601
288
+ result['connect_complete_time'] = @connect_complete_time.getutc.iso8601(6)
289
289
  else
290
290
  result['connect_complete_time'] = nil
291
291
  end
@@ -77,6 +77,8 @@ module OpenC3
77
77
 
78
78
  # Creates a new {SerialStream} using the parameters passed in the constructor
79
79
  def connect
80
+ # Cleanly close any existing stream rather than leaving it open
81
+ @stream.disconnect if @stream
80
82
  @stream = SerialStream.new(
81
83
  @write_port_name,
82
84
  @read_port_name,
File without changes
File without changes
@@ -76,6 +76,8 @@ module OpenC3
76
76
  # Connects the {TcpipClientStream} by passing the
77
77
  # initialization parameters to the {TcpipClientStream}.
78
78
  def connect
79
+ # Cleanly close any existing stream rather than leaving it open
80
+ @stream.disconnect if @stream
79
81
  @stream = TcpipClientStream.new(
80
82
  @hostname,
81
83
  @write_port,
File without changes
@@ -23,6 +23,7 @@ module OpenC3
23
23
  # Base class for interfaces that send and receive messages over UDP
24
24
  class UdpInterface < Interface
25
25
  HOST_127_0_0_1 = '127.0.0.1'
26
+ HOST_0_0_0_0 = '0.0.0.0'
26
27
 
27
28
  # @param hostname [String] Machine to connect to
28
29
  # @param write_dest_port [Integer] Port to write commands to
@@ -77,8 +78,11 @@ module OpenC3
77
78
  @read_timeout = ConfigParser.handle_nil(read_timeout)
78
79
  @read_timeout = @read_timeout.to_f if @read_timeout
79
80
  @bind_address = ConfigParser.handle_nil(bind_address)
80
- if @bind_address && @bind_address.casecmp('LOCALHOST').zero?
81
- @bind_address = HOST_127_0_0_1
81
+ if @bind_address
82
+ @bind_address = HOST_127_0_0_1 if @bind_address.casecmp('LOCALHOST').zero?
83
+ else
84
+ # nil means all local addresses which is what 0.0.0.0 means
85
+ @bind_address = HOST_0_0_0_0
82
86
  end
83
87
  @write_socket = nil
84
88
  @read_socket = nil
@@ -91,9 +95,9 @@ module OpenC3
91
95
  result = ''
92
96
  result += " #{@hostname}:#{@write_dest_port} (write dest port)" if @write_dest_port
93
97
  result += " #{@write_src_port} (write src port)" if @write_src_port
94
- result += " #{@hostname}:#{@read_port} (read)" if @read_port
98
+ result += " #{@bind_address}:#{@read_port} (read)" if @read_port
95
99
  result += " #{@interface_address} (interface addr)" if @interface_address
96
- result += " #{@bind_address} (bind addr)" if @bind_address != '0.0.0.0'
100
+ result += " #{@bind_address} (bind addr)" if @bind_address != HOST_0_0_0_0
97
101
  return result.strip
98
102
  end
99
103
 
@@ -102,13 +106,20 @@ module OpenC3
102
106
  # the constructor.
103
107
  def connect
104
108
  if @read_port and @write_dest_port and @write_src_port and (@read_port == @write_src_port)
109
+ # read_multicast is false because this socket is not connected and thus
110
+ # doesn't filter by peer address. Joining the multicast group we write to
111
+ # would deliver our own commands back to us as telemetry (multicast
112
+ # loopback is enabled by default).
105
113
  @read_socket = UdpReadWriteSocket.new(
106
114
  @read_port,
107
115
  @bind_address,
108
116
  @write_dest_port,
109
117
  @hostname,
110
118
  @interface_address,
111
- @ttl
119
+ @ttl,
120
+ false, # read_multicast
121
+ true, # write_multicast
122
+ false # connect_socket
112
123
  )
113
124
  @write_socket = @read_socket
114
125
  else
File without changes
File without changes
File without changes
@@ -35,8 +35,9 @@ module OpenC3
35
35
  # pull openc3-cosmos-script-runner-api url from environment variables
36
36
  def _generate_url(microservice_name:, prefix:, schema: 'http', hostname: nil, port:, scope: $openc3_scope)
37
37
  prefix = '/' + prefix unless prefix[0] == '/'
38
- if ENV['OPENC3_OPERATOR_HOSTNAME']
39
- hostname = ENV['OPENC3_OPERATOR_HOSTNAME'] unless hostname
38
+ operator_hostname = ENV.fetch('OPENC3_OPERATOR_HOSTNAME', nil)
39
+ if operator_hostname
40
+ hostname = operator_hostname unless hostname
40
41
  return "#{schema}://#{hostname}:#{port.to_i}#{prefix}"
41
42
  else
42
43
  if ENV['KUBERNETES_SERVICE_HOST']
File without changes
@@ -17,6 +17,7 @@
17
17
 
18
18
  require 'socket'
19
19
  require 'json'
20
+ require 'set'
20
21
  # require 'drb/acl'
21
22
  require 'drb/drb'
22
23
  require 'openc3/io/json_rpc'
@@ -47,8 +48,9 @@ module OpenC3
47
48
  # JsonDRb implements the JSON-RPC 2.0 Specification to provide an interface
48
49
  # for both internal and external tools to access the OpenC3 server. It
49
50
  # provides methods to install an access control list to control access to the
50
- # API. It also limits the available methods to a known list of allowable API
51
- # methods.
51
+ # API. Access is controlled by a required method_whitelist which names every
52
+ # method that may be invoked. There is no permissive fallback: any method not
53
+ # explicitly listed is rejected.
52
54
  class JsonDRb
53
55
  # Minimum amount of time in seconds to receive the JSON request,
54
56
  # process it, and send the response. Requests for less than this amount
@@ -62,18 +64,21 @@ module OpenC3
62
64
 
63
65
  # @return [Integer] The number of JSON-RPC requests processed
64
66
  attr_accessor :request_count
65
- # @return [Array<String>] List of methods that should be allowed
66
- attr_accessor :method_whitelist
67
+ # @return [Set<String>] Set of downcased method names which may be called
68
+ attr_reader :method_whitelist
67
69
  # @return [ACL] The access control list
68
70
  # attr_accessor :acl
69
71
 
70
72
  attr_accessor :object
71
73
 
72
- def initialize
74
+ # @param method_whitelist [Enumerable<String>] Every method name which may
75
+ # be called over JSON-RPC. Required and must not be empty since there is
76
+ # no permissive fallback.
77
+ def initialize(method_whitelist:)
78
+ self.method_whitelist = method_whitelist
73
79
  @thread = nil
74
80
  # @acl = nil
75
81
  @object = nil
76
- @method_whitelist = nil
77
82
  @request_count = 0
78
83
  @request_times = []
79
84
  @request_times_index = 0
@@ -82,6 +87,18 @@ module OpenC3
82
87
  @server_mutex = Mutex.new
83
88
  end
84
89
 
90
+ # Replaces the set of methods which may be called over JSON-RPC. Names are
91
+ # downcased to match the dispatch in {#process_request}.
92
+ #
93
+ # @param whitelist [Enumerable<String>] Every method name which may be called
94
+ def method_whitelist=(whitelist)
95
+ if whitelist.nil? or whitelist.to_a.empty?
96
+ raise ArgumentError, "method_whitelist is required and must not be empty"
97
+ end
98
+
99
+ @method_whitelist = whitelist.map { |name| name.to_s.downcase }.to_set
100
+ end
101
+
85
102
  # Returns the number of connected clients
86
103
  # @return [Integer] The number of connected clients
87
104
  def num_clients
@@ -254,8 +271,7 @@ module OpenC3
254
271
  error_code = nil
255
272
  response_data = nil
256
273
 
257
- if (@method_whitelist and @method_whitelist.include?(request.method.downcase())) or
258
- (!@method_whitelist and !JsonRpcRequest::DANGEROUS_METHODS.include?(request.method.downcase()))
274
+ if @method_whitelist.include?(request.method.downcase())
259
275
  begin
260
276
  if request.keyword_params
261
277
  result = @object.public_send(request.method.downcase().intern, *request.params, **request.keyword_params)
@@ -266,10 +282,26 @@ module OpenC3
266
282
  response = JsonRpcSuccessResponse.new(result, request.id)
267
283
  end
268
284
  rescue Exception => e
269
- # Filter out the framework stack trace (rails, rack, puma etc)
270
- lines = e.formatted.split("\n")
271
- i = lines.find_index { |row| row.include?('actionpack') || row.include?('activesupport') }
272
- Logger.error lines[0...i].join("\n")
285
+ error_name = e.class.name.split('::')[-1]
286
+ method_name = request.method.downcase()
287
+ case e
288
+ when AuthError, ForbiddenError
289
+ # Auth failures are expected (e.g. browser requests before login or with an
290
+ # expired session token) so log a single line without the stack trace
291
+ Logger.warn "#{error_name} : #{e.message} calling #{method_name}"
292
+ when HazardousError, CriticalCmdError
293
+ # Not errors at all: these are the signal that the UI should raise an
294
+ # operator confirmation dialog, and the user retries with the hazardous
295
+ # or critical flag set. A stack trace per prompt is pure noise, and
296
+ # HazardousError#to_s embeds the formatted command with a newline,
297
+ # so log just the error name and the method that raised it.
298
+ Logger.info "#{error_name} calling #{method_name}"
299
+ else
300
+ # Filter out the framework stack trace (rails, rack, puma etc)
301
+ lines = e.formatted.split("\n")
302
+ i = lines.find_index { |row| row.include?('actionpack') || row.include?('activesupport') }
303
+ Logger.error lines[0...(i || lines.length)].join("\n")
304
+ end
273
305
 
274
306
  if request.id
275
307
  if NoMethodError === e
File without changes
File without changes
@@ -227,8 +227,6 @@ module OpenC3
227
227
 
228
228
  # Represents a JSON Remote Procedure Call Request
229
229
  class JsonRpcRequest < JsonRpc
230
- DANGEROUS_METHODS = Set['__send__', 'send', 'instance_eval', 'instance_exec']
231
-
232
230
  # @param method_name [String] The name of the method to call
233
231
  # @param method_params [Array<String>] Array of strings which represent the
234
232
  # parameters to send to the method
File without changes
File without changes
File without changes
File without changes
@@ -27,6 +27,9 @@ module OpenC3
27
27
  class UdpReadWriteSocket
28
28
  HOST_0_0_0_0 = '0.0.0.0'
29
29
 
30
+ # MSG_DONTWAIT is not defined on Windows where sends are left blocking
31
+ WRITE_FLAGS = Socket.const_defined?('MSG_DONTWAIT') ? Socket::MSG_DONTWAIT : 0
32
+
30
33
  # @param bind_port [Integer[ Port to write data out from and receive data on (0 = randomly assigned)
31
34
  # @param bind_address [String] Local address to bind to (0.0.0.0 = All local addresses)
32
35
  # @param external_port [Integer] External port to write to
@@ -35,6 +38,8 @@ module OpenC3
35
38
  # @param ttl [Integer] Time To Live for outgoing multicast packets
36
39
  # @param read_multicast [Boolean] Whether or not to try to read from the external address as multicast
37
40
  # @param write_multicast [Boolean] Whether or not to write to the external address as multicast
41
+ # @param connect_socket [Boolean] Whether to connect the socket to the external address. If false,
42
+ # writes explicitly address each datagram so reads can accept datagrams from any source.
38
43
  def initialize(
39
44
  bind_port = 0,
40
45
  bind_address = HOST_0_0_0_0,
@@ -43,10 +48,23 @@ module OpenC3
43
48
  multicast_interface_address = nil,
44
49
  ttl = 1,
45
50
  read_multicast = true,
46
- write_multicast = true
51
+ write_multicast = true,
52
+ connect_socket = true
47
53
  )
48
54
 
49
55
  @socket = UDPSocket.new
56
+ @external_address = external_address
57
+ @external_port = external_port
58
+ @connect_socket = connect_socket
59
+ # Resolve the destination once at creation time so writes don't pay for a
60
+ # (potentially blocking) name lookup on every datagram
61
+ @external_sockaddr = nil
62
+ if !connect_socket and external_address and external_port
63
+ # Explicitly resolve IPv4 because UDPSocket is an AF_INET socket and
64
+ # sockaddr_in would otherwise hand back an IPv6 address for names like localhost
65
+ ip_address = Socket.getaddrinfo(external_address, nil, Socket::AF_INET, Socket::SOCK_DGRAM)[0][3]
66
+ @external_sockaddr = Socket.sockaddr_in(external_port, ip_address)
67
+ end
50
68
 
51
69
  # Basic setup to reuse address
52
70
  @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
@@ -55,10 +73,10 @@ module OpenC3
55
73
  @socket.bind(bind_address, bind_port) if bind_address and bind_port
56
74
 
57
75
  # Default send to the specified address and port
58
- @socket.connect(external_address, external_port) if external_address and external_port
76
+ @socket.connect(external_address, external_port) if connect_socket and external_address and external_port
59
77
 
60
78
  # Handle multicast
61
- if UdpReadWriteSocket.multicast?(external_address, external_port)
79
+ if UdpReadWriteSocket.multicast?(external_address)
62
80
  if write_multicast
63
81
  # Basic setup set time to live
64
82
  @socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_MULTICAST_TTL, ttl.to_i)
@@ -90,7 +108,13 @@ module OpenC3
90
108
 
91
109
  loop do
92
110
  begin
93
- bytes_sent = @socket.write_nonblock(data_to_send)
111
+ if @external_sockaddr
112
+ # send is used rather than sendmsg_nonblock because sendmsg is not
113
+ # implemented on Windows
114
+ bytes_sent = @socket.send(data_to_send, WRITE_FLAGS, @external_sockaddr)
115
+ else
116
+ bytes_sent = @socket.write_nonblock(data_to_send)
117
+ end
94
118
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK
95
119
  result = IO.fast_select(nil, [@socket], nil, write_timeout)
96
120
  if result
@@ -129,12 +153,18 @@ module OpenC3
129
153
  end
130
154
 
131
155
  # @param host [String] Machine name or IP address
132
- # @param port [String] Port
156
+ # @param port [String] Port (unused, kept for backwards compatibility)
133
157
  # @return [Boolean] Whether the hostname is multicast
134
- def self.multicast?(host, port)
135
- return false if host.nil? || port.nil?
158
+ def self.multicast?(host, port = nil)
159
+ return false if host.nil?
136
160
 
137
- Addrinfo.udp(host, port).ipv4_multicast?
161
+ begin
162
+ Addrinfo.udp(host, 0).ipv4_multicast?
163
+ rescue SocketError, ArgumentError
164
+ # Hostname isn't resolvable so it can't be a multicast address we handle.
165
+ # Reads don't need the hostname at all so don't fail creating the socket.
166
+ false
167
+ end
138
168
  end
139
169
  end
140
170
 
@@ -16,6 +16,7 @@
16
16
  # if purchased from OpenC3, Inc.
17
17
 
18
18
  require 'openc3/win32/win32'
19
+ require 'openc3/io/serial_driver'
19
20
  require 'timeout' # For Timeout::Error
20
21
 
21
22
  module OpenC3
File without changes
File without changes
@@ -113,6 +113,7 @@ module OpenC3
113
113
  @cycle_minute = Integer(@cycle_minute) if @cycle_minute
114
114
  @enforce_time_order = ConfigParser.handle_true_false(enforce_time_order)
115
115
  @out_of_order = false
116
+ @bad_time = false
116
117
  @mutex = Mutex.new
117
118
  @file = nil
118
119
  @file_size = 0
@@ -125,6 +126,13 @@ module OpenC3
125
126
  @cleanup_offsets = []
126
127
  @cleanup_times = []
127
128
  @previous_time_nsec_since_epoch = nil
129
+ # Stored packets older than 20 years are treated as invalid (e.g. 1970 before
130
+ # clock/GPS sync). They never drive file segmentation nor become the ordering
131
+ # baseline, so a late-syncing clock doesn't thrash log files.
132
+ @min_valid_time_nsec = (@start_time - (20 * 365 * 24 * 60 * 60)).to_nsec_from_epoch
133
+ # Validity (valid/invalid time) of the packets in the currently open file. Used to
134
+ # segregate invalid-time (1970) packets into their own file. nil = no packets yet.
135
+ @current_file_valid = nil
128
136
  @tmp_dir = Dir.mktmpdir
129
137
  @wait_threads = []
130
138
 
@@ -280,6 +288,7 @@ module OpenC3
280
288
  @first_time = nil
281
289
  @last_time = nil
282
290
  @previous_time_nsec_since_epoch = nil
291
+ @current_file_valid = nil
283
292
  Logger.debug "Log File Opened : #{@filename}"
284
293
  rescue => e
285
294
  Logger.error "Error starting new log file: #{e.formatted}"
@@ -289,7 +298,14 @@ module OpenC3
289
298
 
290
299
  # @enforce_time_order requires the timestamps on each write to be greater than the previous
291
300
  # process_out_of_order ignores the timestamps for the current entry (used to ignore timestamps on metadata entries, vs actual packets)
292
- def prepare_write(time_nsec_since_epoch, data_length, redis_topic = nil, redis_offset = nil, allow_new_file: true, process_out_of_order: true)
301
+ # stored: stored packets roll a new file when GOOD times move backward (a new /
302
+ # overlapping replay run). Packets with an invalid time (e.g. 1970 before clock/GPS
303
+ # sync) - realtime or stored - are segregated into their own file: the first valid
304
+ # packet after a run of 1970s rolls a fresh, properly-named file (and vice-versa).
305
+ def prepare_write(time_nsec_since_epoch, data_length, redis_topic = nil, redis_offset = nil, allow_new_file: true, process_out_of_order: true, stored: false)
306
+ # An invalid time is a packet from before the clock/GPS synced (e.g. 1970). This
307
+ # applies to both realtime and stored packets so neither taints a file's filename.
308
+ packet_valid = !(process_out_of_order and time_nsec_since_epoch < @min_valid_time_nsec)
293
309
  # This check includes logging_enabled again because it might have changed since we acquired the mutex
294
310
  # Ensures new files based on size, and ensures always increasing time order in files
295
311
  if @logging_enabled
@@ -299,17 +315,37 @@ module OpenC3
299
315
  elsif @cycle_size and ((@file_size + data_length) > @cycle_size)
300
316
  Logger.debug("Log writer start new file due to cycle size #{@cycle_size}")
301
317
  start_new_file() if allow_new_file
318
+ elsif process_out_of_order and !@current_file_valid.nil? and packet_valid != @current_file_valid and allow_new_file
319
+ # Time validity changed (1970 <-> valid): segregate invalid-time packets into
320
+ # their own file so valid files keep a clean, correctly-timed filename.
321
+ Logger.debug("Log writer start new file due to time validity change")
322
+ start_new_file()
302
323
  elsif process_out_of_order and @enforce_time_order and @previous_time_nsec_since_epoch and (@previous_time_nsec_since_epoch > time_nsec_since_epoch)
303
- # Warning: Creating new files here can cause lots of files to be created if packets make it through out of order
304
- # Changed to just a error to prevent file thrashing
305
- unless @out_of_order
306
- Logger.error("Log writer out of order time detected (increase buffer depth?): #{Time.from_nsec_from_epoch(@previous_time_nsec_since_epoch)} #{Time.from_nsec_from_epoch(time_nsec_since_epoch)}")
307
- @out_of_order = true
324
+ if stored and packet_valid and allow_new_file
325
+ # Good stored time went backward => new/overlapping replay run. Roll a new
326
+ # file so each stored file stays monotonic (start_new_file resets @previous_time).
327
+ Logger.debug("Log writer start new file due to out of order stored time")
328
+ start_new_file()
329
+ elsif !stored
330
+ # Warning: Creating new files here can cause lots of files to be created if packets make it through out of order
331
+ # Changed to just a warning to prevent file thrashing
332
+ unless @out_of_order
333
+ Logger.warn("Log writer out of order time detected (increase buffer depth?): #{Time.from_nsec_from_epoch(@previous_time_nsec_since_epoch)} #{Time.from_nsec_from_epoch(time_nsec_since_epoch)}")
334
+ @out_of_order = true
335
+ end
308
336
  end
309
337
  end
310
338
  end
311
339
  @last_offsets[redis_topic] = redis_offset if redis_topic and redis_offset # This is needed for the redis offset marker entry at the end of the log file
312
- @previous_time_nsec_since_epoch = time_nsec_since_epoch if process_out_of_order
340
+ if process_out_of_order
341
+ @current_file_valid = packet_valid
342
+ if packet_valid
343
+ @previous_time_nsec_since_epoch = time_nsec_since_epoch
344
+ elsif !@bad_time
345
+ Logger.warn("Log writer segregating invalid packet time (before clock sync?): #{Time.from_nsec_from_epoch(time_nsec_since_epoch)}")
346
+ @bad_time = true
347
+ end
348
+ end
313
349
  end
314
350
 
315
351
  # Closing a log file isn't critical so we just log an error. NOTE: This also trims the Redis stream
File without changes
@@ -142,7 +142,7 @@ module OpenC3
142
142
  end
143
143
  elsif flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_RAW_PACKET_ENTRY_TYPE_MASK
144
144
  packet_index, time_nsec_since_epoch = entry[2..11].unpack('nQ>')
145
- received_time_nsec_since_epoch, _extra, packet_data = handle_received_time_extra_and_data(entry, time_nsec_since_epoch, includes_received_time, includes_extra, cbor)
145
+ received_time_nsec_since_epoch, extra, packet_data = handle_received_time_extra_and_data(entry, time_nsec_since_epoch, includes_received_time, includes_extra, cbor)
146
146
  lookup_cmd_or_tlm, target_name, packet_name, _id = @packets[packet_index]
147
147
  if cmd_or_tlm != lookup_cmd_or_tlm
148
148
  raise "Packet type mismatch, packet:#{cmd_or_tlm}, lookup:#{lookup_cmd_or_tlm}"
@@ -159,6 +159,10 @@ module OpenC3
159
159
  packet.set_received_time_fast(received_time)
160
160
  packet.cmd_or_tlm = cmd_or_tlm
161
161
  packet.stored = stored
162
+ # Assigned unconditionally because identify_and_define_packet_data
163
+ # returns the shared System packet, so a nil extra here must clear
164
+ # the extra left over from a previous read of the same packet
165
+ packet.extra = extra
162
166
  packet.received_count += 1
163
167
  return packet
164
168
  elsif flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_TARGET_DECLARATION_ENTRY_TYPE_MASK
@@ -304,7 +308,24 @@ module OpenC3
304
308
  if cbor
305
309
  extra = CBOR.decode(extra_encoded)
306
310
  else
307
- extra = JSON.parse(extra_encoded, allow_nan: true, create_additions: true)
311
+ begin
312
+ extra = JSON.parse(extra_encoded, allow_nan: true, create_additions: true)
313
+ rescue JSON::ParserError => e
314
+ # PacketLogWriter up to version 7.3.0 CBOR encoded the extra on
315
+ # RAW_PACKET entries without setting OPENC3_CBOR_FLAG_MASK, so the
316
+ # flag claims JSON while the bytes are CBOR. Retry as CBOR so those
317
+ # existing files can still be read. extra is always a Hash, so a
318
+ # decode to anything else means these bytes aren't the legacy
319
+ # encoding either and the original JSON error is the useful one.
320
+ decoded = begin
321
+ CBOR.decode(extra_encoded)
322
+ rescue StandardError
323
+ raise e
324
+ end
325
+ raise e unless Hash === decoded
326
+
327
+ extra = decoded
328
+ end
308
329
  end
309
330
  end
310
331
  data = entry[next_offset..-1]
@@ -96,13 +96,12 @@ module OpenC3
96
96
  @mutex.lock if take_mutex
97
97
  begin
98
98
  if entry_type == :RAW_PACKET or entry_type == :JSON_PACKET
99
- # Only care about the timestamps on the real packets being in order
100
99
  process_out_of_order = true
101
100
  else
102
101
  # Metadata timestamps don't matter
103
102
  process_out_of_order = false
104
103
  end
105
- prepare_write(time_nsec_since_epoch, data.length, redis_topic, redis_offset, allow_new_file: allow_new_file, process_out_of_order: process_out_of_order)
104
+ prepare_write(time_nsec_since_epoch, data.length, redis_topic, redis_offset, allow_new_file: allow_new_file, process_out_of_order: process_out_of_order, stored: stored)
106
105
  write_entry(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id, received_time_nsec_since_epoch: received_time_nsec_since_epoch, extra: extra) if @file
107
106
  ensure
108
107
  @mutex.unlock if take_mutex
@@ -312,6 +311,11 @@ module OpenC3
312
311
  extra = JSON.parse(extra, allow_nan: true, create_additions: true) if String === extra
313
312
  length += OPENC3_EXTRA_LENGTH_FIXED_SIZE
314
313
  if @data_format == :CBOR
314
+ # The reader uses this same flag to pick the extra decoder, so it
315
+ # must be set here as well as in the JSON_PACKET data branch above.
316
+ # RAW_PACKET entries never reach that branch and would otherwise
317
+ # write CBOR extra that the reader tries to JSON.parse.
318
+ flags |= OPENC3_CBOR_FLAG_MASK
315
319
  extra_encoded = extra.as_json.to_cbor
316
320
  else
317
321
  extra_encoded = JSON.generate(extra.as_json, allow_nan: true)
@@ -47,7 +47,7 @@ module OpenC3
47
47
  raise "log_type must be :READ or :WRITE" unless LOG_TYPES.include? log_type
48
48
 
49
49
  super(
50
- "#{ENV['OPENC3_SCOPE']}/stream_logs/",
50
+ "#{ENV.fetch('OPENC3_SCOPE', 'DEFAULT')}/stream_logs/",
51
51
  true, # Start with logging enabled
52
52
  cycle_time,
53
53
  cycle_size,
File without changes
File without changes
data/lib/openc3/logs.rb CHANGED
File without changes
File without changes
File without changes
File without changes
@@ -33,7 +33,8 @@ module OpenC3
33
33
  end
34
34
  stored = inject_tlm_hash.fetch('stored', false)
35
35
  packet.stored = stored.to_s.downcase == 'true'
36
- packet.received_time = Time.now.sys
36
+ received_time = inject_tlm_hash['received_time']
37
+ packet.received_time = received_time.nil? ? Time.now.sys : Time.from_nsec_from_epoch(received_time).sys
37
38
  packet.received_count = TargetModel.increment_telemetry_count(packet.target_name, packet.packet_name, 1, scope: @scope)
38
39
  TelemetryTopic.write_packet(packet, scope: @scope)
39
40
  end