openc3 6.10.4 → 7.0.0.pre.rc2

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 (350) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -1
  3. data/Guardfile +4 -9
  4. data/LICENSE.md +85 -0
  5. data/README.md +8 -8
  6. data/Rakefile +3 -9
  7. data/bin/cstol_converter +3 -8
  8. data/bin/openc3cli +64 -35
  9. data/data/config/command_modifiers.yaml +18 -1
  10. data/data/config/interface_modifiers.yaml +0 -2
  11. data/data/config/plugins.yaml +14 -18
  12. data/data/config/screen.yaml +1 -1
  13. data/data/config/target.yaml +26 -79
  14. data/data/config/telemetry_modifiers.yaml +15 -0
  15. data/data/config/widgets.yaml +32 -32
  16. data/ext/openc3/ext/array/array.c +4 -9
  17. data/ext/openc3/ext/buffered_file/buffered_file.c +4 -9
  18. data/ext/openc3/ext/burst_protocol/burst_protocol.c +3 -8
  19. data/ext/openc3/ext/config_parser/config_parser.c +2 -7
  20. data/ext/openc3/ext/crc/crc.c +3 -8
  21. data/ext/openc3/ext/openc3_io/openc3_io.c +4 -9
  22. data/ext/openc3/ext/packet/packet.c +7 -9
  23. data/ext/openc3/ext/platform/platform.c +3 -8
  24. data/ext/openc3/ext/polynomial_conversion/polynomial_conversion.c +16 -12
  25. data/ext/openc3/ext/string/string.c +4 -9
  26. data/ext/openc3/ext/structure/structure.c +5 -9
  27. data/ext/openc3/ext/tabbed_plots_config/tabbed_plots_config.c +4 -9
  28. data/ext/openc3/ext/telemetry/telemetry.c +8 -12
  29. data/lib/cosmos.rb +4 -9
  30. data/lib/cosmosc2.rb +4 -9
  31. data/lib/openc3/accessors/accessor.rb +10 -13
  32. data/lib/openc3/accessors/binary_accessor.rb +18 -17
  33. data/lib/openc3/accessors/cbor_accessor.rb +3 -8
  34. data/lib/openc3/accessors/form_accessor.rb +4 -9
  35. data/lib/openc3/accessors/html_accessor.rb +3 -8
  36. data/lib/openc3/accessors/http_accessor.rb +4 -9
  37. data/lib/openc3/accessors/json_accessor.rb +4 -9
  38. data/lib/openc3/accessors/template_accessor.rb +4 -9
  39. data/lib/openc3/accessors/xml_accessor.rb +4 -9
  40. data/lib/openc3/accessors.rb +3 -8
  41. data/lib/openc3/api/README.md +1 -1
  42. data/lib/openc3/api/api.rb +3 -8
  43. data/lib/openc3/api/authorized_api.rb +4 -9
  44. data/lib/openc3/api/cmd_api.rb +3 -8
  45. data/lib/openc3/api/config_api.rb +3 -8
  46. data/lib/openc3/api/interface_api.rb +4 -9
  47. data/lib/openc3/api/limits_api.rb +3 -8
  48. data/lib/openc3/api/metrics_api.rb +2 -19
  49. data/lib/openc3/api/offline_access_api.rb +2 -7
  50. data/lib/openc3/api/router_api.rb +5 -10
  51. data/lib/openc3/api/settings_api.rb +3 -8
  52. data/lib/openc3/api/stash_api.rb +2 -7
  53. data/lib/openc3/api/target_api.rb +3 -8
  54. data/lib/openc3/api/tlm_api.rb +15 -38
  55. data/lib/openc3/bridge/bridge.rb +3 -8
  56. data/lib/openc3/bridge/bridge_config.rb +5 -10
  57. data/lib/openc3/bridge/bridge_interface_thread.rb +4 -9
  58. data/lib/openc3/bridge/bridge_router_thread.rb +4 -9
  59. data/lib/openc3/ccsds/ccsds_packet.rb +4 -9
  60. data/lib/openc3/ccsds/ccsds_parser.rb +3 -8
  61. data/lib/openc3/config/config_parser.rb +3 -8
  62. data/lib/openc3/config/meta_config_parser.rb +3 -8
  63. data/lib/openc3/conversions/bit_reverse_conversion.rb +3 -8
  64. data/lib/openc3/conversions/conversion.rb +3 -8
  65. data/lib/openc3/conversions/generic_conversion.rb +3 -8
  66. data/lib/openc3/conversions/ip_read_conversion.rb +3 -8
  67. data/lib/openc3/conversions/ip_write_conversion.rb +3 -8
  68. data/lib/openc3/conversions/object_read_conversion.rb +3 -8
  69. data/lib/openc3/conversions/object_write_conversion.rb +3 -8
  70. data/lib/openc3/conversions/packet_time_formatted_conversion.rb +4 -9
  71. data/lib/openc3/conversions/packet_time_seconds_conversion.rb +4 -9
  72. data/lib/openc3/conversions/polynomial_conversion.rb +6 -8
  73. data/lib/openc3/conversions/processor_conversion.rb +3 -8
  74. data/lib/openc3/conversions/received_count_conversion.rb +4 -9
  75. data/lib/openc3/conversions/received_time_formatted_conversion.rb +4 -9
  76. data/lib/openc3/conversions/received_time_seconds_conversion.rb +4 -9
  77. data/lib/openc3/conversions/segmented_polynomial_conversion.rb +6 -8
  78. data/lib/openc3/conversions/unix_time_conversion.rb +3 -8
  79. data/lib/openc3/conversions/unix_time_formatted_conversion.rb +3 -8
  80. data/lib/openc3/conversions/unix_time_seconds_conversion.rb +3 -8
  81. data/lib/openc3/conversions.rb +3 -8
  82. data/lib/openc3/core_ext/array.rb +3 -8
  83. data/lib/openc3/core_ext/binding.rb +4 -9
  84. data/lib/openc3/core_ext/class.rb +4 -9
  85. data/lib/openc3/core_ext/exception.rb +3 -8
  86. data/lib/openc3/core_ext/file.rb +4 -9
  87. data/lib/openc3/core_ext/io.rb +4 -9
  88. data/lib/openc3/core_ext/kernel.rb +3 -8
  89. data/lib/openc3/core_ext/math.rb +4 -9
  90. data/lib/openc3/core_ext/matrix.rb +4 -9
  91. data/lib/openc3/core_ext/objectspace.rb +4 -9
  92. data/lib/openc3/core_ext/openc3_io.rb +4 -9
  93. data/lib/openc3/core_ext/range.rb +4 -9
  94. data/lib/openc3/core_ext/socket.rb +4 -9
  95. data/lib/openc3/core_ext/string.rb +3 -8
  96. data/lib/openc3/core_ext/stringio.rb +4 -9
  97. data/lib/openc3/core_ext/tempfile.rb +4 -9
  98. data/lib/openc3/core_ext/time.rb +3 -8
  99. data/lib/openc3/core_ext.rb +3 -8
  100. data/lib/openc3/interfaces/file_interface.rb +3 -8
  101. data/lib/openc3/interfaces/http_client_interface.rb +3 -8
  102. data/lib/openc3/interfaces/http_server_interface.rb +3 -8
  103. data/lib/openc3/interfaces/interface.rb +3 -8
  104. data/lib/openc3/interfaces/mqtt_interface.rb +3 -8
  105. data/lib/openc3/interfaces/mqtt_stream_interface.rb +3 -8
  106. data/lib/openc3/interfaces/protocols/burst_protocol.rb +3 -8
  107. data/lib/openc3/interfaces/protocols/cmd_response_protocol.rb +3 -8
  108. data/lib/openc3/interfaces/protocols/cobs_protocol.rb +3 -8
  109. data/lib/openc3/interfaces/protocols/crc_protocol.rb +3 -8
  110. data/lib/openc3/interfaces/protocols/fixed_protocol.rb +3 -8
  111. data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +3 -8
  112. data/lib/openc3/interfaces/protocols/length_protocol.rb +3 -8
  113. data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +3 -8
  114. data/lib/openc3/interfaces/protocols/protocol.rb +3 -8
  115. data/lib/openc3/interfaces/protocols/slip_protocol.rb +3 -8
  116. data/lib/openc3/interfaces/protocols/template_protocol.rb +3 -8
  117. data/lib/openc3/interfaces/protocols/terminated_protocol.rb +3 -8
  118. data/lib/openc3/interfaces/serial_interface.rb +3 -8
  119. data/lib/openc3/interfaces/simulated_target_interface.rb +3 -8
  120. data/lib/openc3/interfaces/stream_interface.rb +3 -8
  121. data/lib/openc3/interfaces/tcpip_client_interface.rb +3 -8
  122. data/lib/openc3/interfaces/tcpip_server_interface.rb +3 -8
  123. data/lib/openc3/interfaces/udp_interface.rb +3 -8
  124. data/lib/openc3/interfaces.rb +3 -8
  125. data/lib/openc3/io/buffered_file.rb +4 -9
  126. data/lib/openc3/io/io_multiplexer.rb +4 -9
  127. data/lib/openc3/io/json_api.rb +3 -8
  128. data/lib/openc3/io/json_api_object.rb +4 -9
  129. data/lib/openc3/io/json_drb.rb +3 -8
  130. data/lib/openc3/io/json_drb_object.rb +4 -9
  131. data/lib/openc3/io/json_drb_rack.rb +4 -9
  132. data/lib/openc3/io/json_rpc.rb +3 -8
  133. data/lib/openc3/io/posix_serial_driver.rb +3 -8
  134. data/lib/openc3/io/serial_driver.rb +3 -8
  135. data/lib/openc3/io/stderr.rb +4 -9
  136. data/lib/openc3/io/stdout.rb +4 -9
  137. data/lib/openc3/io/udp_sockets.rb +3 -8
  138. data/lib/openc3/io/win32_serial_driver.rb +4 -9
  139. data/lib/openc3/logs/buffered_packet_log_reader.rb +3 -8
  140. data/lib/openc3/logs/buffered_packet_log_writer.rb +3 -8
  141. data/lib/openc3/logs/log_writer.rb +3 -8
  142. data/lib/openc3/logs/packet_log_constants.rb +3 -8
  143. data/lib/openc3/logs/packet_log_reader.rb +3 -8
  144. data/lib/openc3/logs/packet_log_writer.rb +3 -8
  145. data/lib/openc3/logs/stream_log.rb +3 -8
  146. data/lib/openc3/logs/stream_log_pair.rb +3 -8
  147. data/lib/openc3/logs/text_log_writer.rb +3 -8
  148. data/lib/openc3/logs.rb +3 -8
  149. data/lib/openc3/microservices/cleanup_microservice.rb +4 -14
  150. data/lib/openc3/microservices/decom_microservice.rb +3 -8
  151. data/lib/openc3/microservices/interface_decom_common.rb +3 -8
  152. data/lib/openc3/microservices/interface_microservice.rb +5 -9
  153. data/lib/openc3/microservices/log_microservice.rb +3 -8
  154. data/lib/openc3/microservices/microservice.rb +24 -12
  155. data/lib/openc3/microservices/multi_microservice.rb +2 -7
  156. data/lib/openc3/microservices/periodic_microservice.rb +3 -8
  157. data/lib/openc3/microservices/plugin_microservice.rb +3 -8
  158. data/lib/openc3/microservices/queue_microservice.rb +3 -8
  159. data/lib/openc3/microservices/router_microservice.rb +3 -8
  160. data/lib/openc3/microservices/scope_cleanup_microservice.rb +3 -8
  161. data/lib/openc3/microservices/text_log_microservice.rb +3 -8
  162. data/lib/openc3/migrations/20251213120000_reinstall_plugins.rb +45 -0
  163. data/lib/openc3/migrations/20260204000000_remove_decom_reducer.rb +60 -0
  164. data/lib/openc3/models/activity_model.rb +4 -8
  165. data/lib/openc3/models/auth_model.rb +57 -38
  166. data/lib/openc3/models/cvt_model.rb +203 -62
  167. data/lib/openc3/models/environment_model.rb +4 -9
  168. data/lib/openc3/models/gem_model.rb +3 -8
  169. data/lib/openc3/models/info_model.rb +4 -9
  170. data/lib/openc3/models/interface_model.rb +6 -10
  171. data/lib/openc3/models/interface_status_model.rb +4 -9
  172. data/lib/openc3/models/metadata_model.rb +3 -8
  173. data/lib/openc3/models/metric_model.rb +3 -8
  174. data/lib/openc3/models/microservice_model.rb +24 -10
  175. data/lib/openc3/models/microservice_status_model.rb +3 -8
  176. data/lib/openc3/models/migration_model.rb +3 -8
  177. data/lib/openc3/models/model.rb +3 -8
  178. data/lib/openc3/models/news_model.rb +3 -8
  179. data/lib/openc3/models/note_model.rb +3 -8
  180. data/lib/openc3/models/offline_access_model.rb +3 -8
  181. data/lib/openc3/models/ping_model.rb +4 -9
  182. data/lib/openc3/models/plugin_model.rb +14 -9
  183. data/lib/openc3/models/plugin_store_model.rb +21 -17
  184. data/lib/openc3/models/process_status_model.rb +4 -9
  185. data/lib/openc3/models/python_package_model.rb +3 -8
  186. data/lib/openc3/models/queue_model.rb +3 -8
  187. data/lib/openc3/models/reaction_model.rb +9 -10
  188. data/lib/openc3/models/router_model.rb +4 -9
  189. data/lib/openc3/models/router_status_model.rb +4 -9
  190. data/lib/openc3/models/scope_model.rb +4 -9
  191. data/lib/openc3/models/script_engine_model.rb +3 -8
  192. data/lib/openc3/models/script_status_model.rb +3 -8
  193. data/lib/openc3/models/secret_model.rb +3 -8
  194. data/lib/openc3/models/setting_model.rb +3 -8
  195. data/lib/openc3/models/sorted_model.rb +3 -8
  196. data/lib/openc3/models/stash_model.rb +3 -8
  197. data/lib/openc3/models/target_model.rb +99 -229
  198. data/lib/openc3/models/timeline_model.rb +3 -8
  199. data/lib/openc3/models/tool_config_model.rb +3 -8
  200. data/lib/openc3/models/tool_model.rb +10 -10
  201. data/lib/openc3/models/trigger_group_model.rb +3 -8
  202. data/lib/openc3/models/trigger_model.rb +3 -8
  203. data/lib/openc3/models/widget_model.rb +3 -8
  204. data/lib/openc3/operators/microservice_operator.rb +7 -8
  205. data/lib/openc3/operators/operator.rb +3 -8
  206. data/lib/openc3/packets/command_validator.rb +3 -8
  207. data/lib/openc3/packets/commands.rb +32 -14
  208. data/lib/openc3/packets/json_packet.rb +7 -19
  209. data/lib/openc3/packets/limits.rb +3 -8
  210. data/lib/openc3/packets/limits_response.rb +3 -8
  211. data/lib/openc3/packets/packet.rb +38 -32
  212. data/lib/openc3/packets/packet_config.rb +23 -13
  213. data/lib/openc3/packets/packet_item.rb +3 -8
  214. data/lib/openc3/packets/packet_item_limits.rb +3 -8
  215. data/lib/openc3/packets/parsers/format_string_parser.rb +3 -8
  216. data/lib/openc3/packets/parsers/limits_parser.rb +3 -8
  217. data/lib/openc3/packets/parsers/limits_response_parser.rb +3 -8
  218. data/lib/openc3/packets/parsers/packet_item_parser.rb +7 -8
  219. data/lib/openc3/packets/parsers/packet_parser.rb +3 -8
  220. data/lib/openc3/packets/parsers/processor_parser.rb +3 -8
  221. data/lib/openc3/packets/parsers/state_parser.rb +3 -8
  222. data/lib/openc3/packets/parsers/xtce_converter.rb +12 -9
  223. data/lib/openc3/packets/parsers/xtce_parser.rb +3 -8
  224. data/lib/openc3/packets/structure.rb +14 -11
  225. data/lib/openc3/packets/structure_item.rb +4 -9
  226. data/lib/openc3/packets/telemetry.rb +7 -11
  227. data/lib/openc3/processors/processor.rb +5 -10
  228. data/lib/openc3/processors/statistics_processor.rb +4 -9
  229. data/lib/openc3/processors/watermark_processor.rb +4 -9
  230. data/lib/openc3/processors.rb +4 -9
  231. data/lib/openc3/script/api_shared.rb +8 -37
  232. data/lib/openc3/script/autonomic.rb +3 -8
  233. data/lib/openc3/script/calendar.rb +3 -8
  234. data/lib/openc3/script/commands.rb +3 -8
  235. data/lib/openc3/script/critical_cmd.rb +3 -8
  236. data/lib/openc3/script/exceptions.rb +4 -9
  237. data/lib/openc3/script/extract.rb +3 -8
  238. data/lib/openc3/script/limits.rb +3 -8
  239. data/lib/openc3/script/metadata.rb +3 -8
  240. data/lib/openc3/script/packages.rb +3 -8
  241. data/lib/openc3/script/plugins.rb +3 -8
  242. data/lib/openc3/script/queue.rb +3 -8
  243. data/lib/openc3/script/screen.rb +7 -8
  244. data/lib/openc3/script/script.rb +3 -8
  245. data/lib/openc3/script/script_runner.rb +3 -8
  246. data/lib/openc3/script/storage.rb +6 -10
  247. data/lib/openc3/script/suite.rb +3 -8
  248. data/lib/openc3/script/suite_results.rb +3 -8
  249. data/lib/openc3/script/suite_runner.rb +3 -8
  250. data/lib/openc3/script/tables.rb +3 -8
  251. data/lib/openc3/script/telemetry.rb +3 -8
  252. data/lib/openc3/script/web_socket_api.rb +8 -13
  253. data/lib/openc3/script.rb +4 -9
  254. data/lib/openc3/script_engines/script_engine.rb +3 -8
  255. data/lib/openc3/streams/mqtt_stream.rb +3 -8
  256. data/lib/openc3/streams/serial_stream.rb +3 -8
  257. data/lib/openc3/streams/stream.rb +3 -8
  258. data/lib/openc3/streams/tcpip_client_stream.rb +3 -8
  259. data/lib/openc3/streams/tcpip_socket_stream.rb +3 -8
  260. data/lib/openc3/streams/web_socket_client_stream.rb +3 -8
  261. data/lib/openc3/system/system.rb +3 -8
  262. data/lib/openc3/system/target.rb +3 -8
  263. data/lib/openc3/system.rb +3 -8
  264. data/lib/openc3/tools/cmd_tlm_server/api.rb +4 -9
  265. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +3 -8
  266. data/lib/openc3/tools/table_manager/table.rb +3 -8
  267. data/lib/openc3/tools/table_manager/table_config.rb +3 -8
  268. data/lib/openc3/tools/table_manager/table_item.rb +4 -9
  269. data/lib/openc3/tools/table_manager/table_item_parser.rb +3 -8
  270. data/lib/openc3/tools/table_manager/table_manager_core.rb +3 -8
  271. data/lib/openc3/tools/table_manager/table_parser.rb +3 -8
  272. data/lib/openc3/tools/test_runner/test.rb +4 -9
  273. data/lib/openc3/top_level.rb +3 -8
  274. data/lib/openc3/topics/autonomic_topic.rb +4 -9
  275. data/lib/openc3/topics/calendar_topic.rb +4 -9
  276. data/lib/openc3/topics/command_decom_topic.rb +14 -17
  277. data/lib/openc3/topics/command_topic.rb +14 -10
  278. data/lib/openc3/topics/config_topic.rb +3 -8
  279. data/lib/openc3/topics/decom_interface_topic.rb +3 -8
  280. data/lib/openc3/topics/interface_topic.rb +3 -8
  281. data/lib/openc3/topics/limits_event_topic.rb +3 -8
  282. data/lib/openc3/topics/notebook_topic.rb +32 -0
  283. data/lib/openc3/topics/queue_topic.rb +3 -8
  284. data/lib/openc3/topics/router_topic.rb +3 -8
  285. data/lib/openc3/topics/system_events_topic.rb +3 -8
  286. data/lib/openc3/topics/telemetry_decom_topic.rb +12 -12
  287. data/lib/openc3/topics/telemetry_topic.rb +3 -8
  288. data/lib/openc3/topics/timeline_topic.rb +4 -9
  289. data/lib/openc3/topics/topic.rb +3 -8
  290. data/lib/openc3/utilities/authentication.rb +29 -10
  291. data/lib/openc3/utilities/authorization.rb +4 -9
  292. data/lib/openc3/utilities/aws_bucket.rb +126 -16
  293. data/lib/openc3/utilities/bucket.rb +9 -9
  294. data/lib/openc3/utilities/bucket_file_cache.rb +3 -13
  295. data/lib/openc3/utilities/bucket_require.rb +3 -8
  296. data/lib/openc3/utilities/bucket_utilities.rb +3 -10
  297. data/lib/openc3/utilities/cli_generator.rb +11 -16
  298. data/lib/openc3/utilities/cmd_log.rb +3 -8
  299. data/lib/openc3/utilities/crc.rb +3 -8
  300. data/lib/openc3/utilities/csv.rb +4 -9
  301. data/lib/openc3/utilities/local_bucket.rb +5 -10
  302. data/lib/openc3/utilities/local_mode.rb +14 -18
  303. data/lib/openc3/utilities/logger.rb +3 -8
  304. data/lib/openc3/utilities/message_log.rb +3 -8
  305. data/lib/openc3/utilities/metric.rb +3 -8
  306. data/lib/openc3/utilities/migration.rb +3 -8
  307. data/lib/openc3/utilities/open_telemetry.rb +3 -8
  308. data/lib/openc3/utilities/process_manager.rb +3 -8
  309. data/lib/openc3/utilities/python_proxy.rb +3 -8
  310. data/lib/openc3/utilities/quaternion.rb +3 -8
  311. data/lib/openc3/utilities/questdb_client.rb +210 -0
  312. data/lib/openc3/utilities/redis_secrets.rb +3 -8
  313. data/lib/openc3/utilities/ruby_lex_utils.rb +3 -8
  314. data/lib/openc3/utilities/running_script.rb +13 -11
  315. data/lib/openc3/utilities/s3_autoload.rb +9 -2
  316. data/lib/openc3/utilities/secrets.rb +8 -9
  317. data/lib/openc3/utilities/simulated_target.rb +4 -9
  318. data/lib/openc3/utilities/sleeper.rb +3 -8
  319. data/lib/openc3/utilities/store.rb +4 -9
  320. data/lib/openc3/utilities/store_autoload.rb +7 -14
  321. data/lib/openc3/utilities/store_queued.rb +6 -9
  322. data/lib/openc3/utilities/target_file.rb +3 -8
  323. data/lib/openc3/utilities/thread_manager.rb +3 -8
  324. data/lib/openc3/utilities/throttle.rb +3 -8
  325. data/lib/openc3/utilities/zip.rb +4 -9
  326. data/lib/openc3/utilities.rb +4 -9
  327. data/lib/openc3/version.rb +8 -8
  328. data/lib/openc3/win32/excel.rb +4 -9
  329. data/lib/openc3/win32/win32.rb +4 -9
  330. data/lib/openc3/win32/win32_main.rb +3 -8
  331. data/lib/openc3.rb +3 -12
  332. data/tasks/gemfile_stats.rake +4 -9
  333. data/tasks/spec.rake +4 -9
  334. data/templates/plugin/README.md +2 -2
  335. data/templates/plugin/plugin.gemspec +1 -1
  336. data/templates/tool_angular/package.json +1 -1
  337. data/templates/tool_vue/package.json +3 -3
  338. data/templates/tool_vue/vite.config.js +2 -1
  339. data/templates/widget/package.json +3 -3
  340. data/templates/widget/vite.config.js +1 -1
  341. metadata +49 -26
  342. data/LICENSE.txt +0 -729
  343. data/bin/rubysloc +0 -142
  344. data/ext/openc3/ext/reducer_microservice/extconf.rb +0 -13
  345. data/ext/openc3/ext/reducer_microservice/reducer_microservice.c +0 -165
  346. data/lib/openc3/microservices/reducer_microservice.rb +0 -640
  347. data/lib/openc3/models/reducer_model.rb +0 -72
  348. data/lib/openc3/topics/telemetry_reduced_topics.rb +0 -80
  349. /data/templates/plugin/{LICENSE.txt → LICENSE.md} +0 -0
  350. /data/templates/widget/{LICENSE.txt → LICENSE.md} +0 -0
@@ -1,640 +0,0 @@
1
- # encoding: ascii-8bit
2
-
3
- # Copyright 2022 Ball Aerospace & Technologies Corp.
4
- # All Rights Reserved.
5
- #
6
- # This program is free software; you can modify and/or redistribute it
7
- # under the terms of the GNU Affero General Public License
8
- # as published by the Free Software Foundation; version 3 with
9
- # attribution addendums as found in the LICENSE.txt
10
- #
11
- # This program is distributed in the hope that it will be useful,
12
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
- # GNU Affero General Public License for more details.
15
-
16
- # Modified by OpenC3, Inc.
17
- # All changes Copyright 2022, OpenC3, Inc.
18
- # All Rights Reserved
19
- #
20
- # This file may also be used under the terms of a commercial license
21
- # if purchased from OpenC3, Inc.
22
-
23
- require 'openc3/microservices/microservice'
24
- require 'openc3/topics/topic'
25
- require 'openc3/topics/telemetry_reduced_topics'
26
- require 'openc3/packets/json_packet'
27
- require 'openc3/utilities/bucket_file_cache'
28
- require 'openc3/utilities/throttle'
29
- require 'openc3/models/reducer_model'
30
- require 'openc3/logs/buffered_packet_log_writer'
31
- require 'openc3/ext/reducer_microservice' if RUBY_ENGINE == 'ruby' and !ENV['OPENC3_NO_EXT']
32
- require 'rufus-scheduler'
33
- require 'thread'
34
-
35
- module OpenC3
36
- class ReducerState
37
- attr_accessor :reduced
38
- attr_accessor :raw_keys
39
- attr_accessor :converted_keys
40
- attr_accessor :entry_time
41
- attr_accessor :entry_samples
42
- attr_accessor :current_time
43
- attr_accessor :previous_time
44
- attr_accessor :raw_values
45
- attr_accessor :raw_max_values
46
- attr_accessor :raw_min_values
47
- attr_accessor :raw_avg_values
48
- attr_accessor :raw_stddev_values
49
- attr_accessor :converted_values
50
- attr_accessor :converted_max_values
51
- attr_accessor :converted_min_values
52
- attr_accessor :converted_avg_values
53
- attr_accessor :converted_stddev_values
54
- attr_accessor :first
55
-
56
- def initialize
57
- @reduced = {}
58
- @raw_keys = nil
59
- @converted_keys = nil
60
- @entry_time = nil
61
- @entry_samples = nil
62
- @current_time = nil
63
- @previous_time = nil
64
- @raw_values = nil
65
- @raw_max_values = nil
66
- @raw_min_values = nil
67
- @raw_avg_values = nil
68
- @raw_stddev_values = nil
69
- @converted_values = nil
70
- @converted_max_values = nil
71
- @converted_min_values = nil
72
- @converted_avg_values = nil
73
- @converted_stddev_values = nil
74
- @first = true
75
- end
76
- end
77
-
78
- class ReducerMicroservice < Microservice
79
- MINUTE_METRIC = 'reducer_minute_processing'
80
- HOUR_METRIC = 'reducer_hour_processing'
81
- DAY_METRIC = 'reducer_day_processing'
82
-
83
- # How long to wait for any currently running jobs to complete before killing them
84
- SHUTDOWN_DELAY_SECS = 5
85
- MINUTE_ENTRY_NSECS = 60 * 1_000_000_000
86
- MINUTE_FILE_NSECS = 3600 * 1_000_000_000
87
- HOUR_ENTRY_NSECS = 3600 * 1_000_000_000
88
- HOUR_FILE_NSECS = 3600 * 24 * 1_000_000_000
89
- DAY_ENTRY_NSECS = 3600 * 24 * 1_000_000_000
90
- DAY_FILE_NSECS = 3600 * 24 * 5 * 1_000_000_000
91
-
92
- # @param name [String] Microservice name formatted as <SCOPE>__REDUCER__<TARGET>
93
- # where <SCOPE> and <TARGET> are variables representing the scope name and target name
94
- def initialize(name)
95
- super(name, is_plugin: false)
96
-
97
- if @config['options']
98
- @config['options'].each do |option|
99
- case option[0].upcase
100
- when 'BUFFER_DEPTH' # Buffer depth to write in time order
101
- @buffer_depth = option[1].to_i
102
- when 'MAX_CPU_UTILIZATION'
103
- @max_cpu_utilization = Float(option[1])
104
- else
105
- @logger.error("Unknown option passed to microservice #{@name}: #{option}")
106
- end
107
- end
108
- end
109
-
110
- @buffer_depth = 60 unless @buffer_depth
111
- @max_cpu_utilization = 30.0 unless @max_cpu_utilization
112
- @target_name = name.split('__')[-1]
113
- @packet_logs = {}
114
- @mutex = Mutex.new
115
- @previous_metrics = {}
116
-
117
- @error_count = 0
118
-
119
- # Initialize metrics
120
- @metric.set(name: 'reducer_minute_total', value: 0, type: 'counter')
121
- @metric.set(name: 'reducer_hour_total', value: 0, type: 'counter')
122
- @metric.set(name: 'reducer_day_total', value: 0, type: 'counter')
123
- @metric.set(name: 'reducer_minute_error_total', value: 0, type: 'counter')
124
- @metric.set(name: 'reducer_hour_error_total', value: 0, type: 'counter')
125
- @metric.set(name: 'reducer_day_error_total', value: 0, type: 'counter')
126
- @metric.set(name: 'reducer_minute_processing_sample_seconds', value: 0.0, type: 'gauge', unit: 'seconds')
127
- @metric.set(name: 'reducer_hour_processing_sample_seconds', value: 0.0, type: 'gauge', unit: 'seconds')
128
- @metric.set(name: 'reducer_day_processing_sample_seconds', value: 0.0, type: 'gauge', unit: 'seconds')
129
- @metric.set(name: 'reducer_minute_processing_max_seconds', value: 0.0, type: 'gauge', unit: 'seconds')
130
- @metric.set(name: 'reducer_hour_processing_max_seconds', value: 0.0, type: 'gauge', unit: 'seconds')
131
- @metric.set(name: 'reducer_day_processing_max_seconds', value: 0.0, type: 'gauge', unit: 'seconds')
132
- end
133
-
134
- def run
135
- # Note it takes several seconds to create the scheduler
136
- @scheduler = Rufus::Scheduler.new
137
- # Run every minute
138
- @scheduler.cron '* * * * *', first: :now do
139
- reduce_minute
140
- end
141
- # Run every 15 minutes
142
- @scheduler.cron '*/15 * * * *', first: :now do
143
- reduce_hour
144
- end
145
- # Run hourly at minute 5 to allow the hour reducer to finish
146
- @scheduler.cron '5 * * * *', first: :now do
147
- reduce_day
148
- end
149
-
150
- # Let the current thread join the scheduler thread and
151
- # block until shutdown is called
152
- @scheduler.join
153
- end
154
-
155
- def shutdown
156
- @logger.info("Shutting down reducer microservice: #{@name}")
157
- @scheduler.shutdown(wait: SHUTDOWN_DELAY_SECS) if @scheduler
158
-
159
- # Make sure all the existing logs are properly closed down
160
- threads = []
161
- @packet_logs.each do |name, log|
162
- threads.concat(log.shutdown)
163
- end
164
- # Wait for all the logging threads to move files to buckets
165
- threads.flatten.compact.each do |thread|
166
- thread.join
167
- end
168
- super()
169
- end
170
-
171
- def metric(name)
172
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
173
- processed = yield
174
- elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start # seconds as a float
175
- if processed
176
- sample_name = name + '_sample_seconds'
177
- @metric.set(name: sample_name, value: elapsed, type: 'gauge', unit: 'seconds')
178
- max_name = name + '_max_seconds'
179
- previous_max = @previous_metrics[max_name] || 0.0
180
- if elapsed > previous_max
181
- @metric.set(name: max_name, value: elapsed, type: 'gauge', unit: 'seconds')
182
- @previous_metrics[max_name] = elapsed
183
- end
184
- end
185
- end
186
-
187
- def reduce_minute
188
- @mutex.synchronize do
189
- metric(MINUTE_METRIC) do
190
- processed = false
191
- ReducerModel
192
- .all_files(type: :DECOM, target: @target_name, scope: @scope)
193
- .each do |file|
194
- process_file(file, 'minute', MINUTE_ENTRY_NSECS, MINUTE_FILE_NSECS)
195
- ReducerModel.rm_file(file)
196
- processed = true
197
- end
198
- processed # return to yield
199
- end
200
- end
201
- end
202
-
203
- def reduce_hour
204
- @mutex.synchronize do
205
- metric(HOUR_METRIC) do
206
- processed = false
207
- ReducerModel
208
- .all_files(type: :MINUTE, target: @target_name, scope: @scope)
209
- .each do |file|
210
- process_file(file, 'hour', HOUR_ENTRY_NSECS, HOUR_FILE_NSECS)
211
- ReducerModel.rm_file(file)
212
- processed = true
213
- end
214
- processed # return to yield
215
- end
216
- end
217
- end
218
-
219
- def reduce_day
220
- @mutex.synchronize do
221
- metric(DAY_METRIC) do
222
- processed = false
223
- ReducerModel
224
- .all_files(type: :HOUR, target: @target_name, scope: @scope)
225
- .each do |file|
226
- process_file(file, 'day', DAY_ENTRY_NSECS, DAY_FILE_NSECS)
227
- ReducerModel.rm_file(file)
228
- processed = true
229
- end
230
- processed # return to yield
231
- end
232
- end
233
- end
234
-
235
- def process_file(filename, type, entry_nanoseconds, file_nanoseconds)
236
- throttle = OpenC3::Throttle.new(@max_cpu_utilization)
237
- file = BucketFile.new(filename)
238
- file.retrieve
239
- unless File.exist?(file.local_path)
240
- @logger.warn("Reducer Warning: #{file.local_path}: Does not exist")
241
- return
242
- end
243
- unless File.size(file.local_path) > 0
244
- @logger.warn("Reducer Warning: #{file.local_path}: Is zero bytes")
245
- return
246
- end
247
- throttle.throttle_sleep
248
-
249
- # Determine if we already have a PacketLogWriter created
250
- _, _, scope, target_name, _, rt_or_stored, _ = File.basename(filename).split('__')
251
- stored = (rt_or_stored == "stored")
252
-
253
- if @target_name != target_name
254
- raise "Target name in file #{filename} does not match microservice target name #{@target_name}"
255
- end
256
- plw = @packet_logs["#{scope}__#{target_name}__#{rt_or_stored}__#{type}"]
257
- unless plw
258
- # Create a new PacketLogWriter for this reduced data
259
- # e.g. DEFAULT/reduced_minute_logs/tlm/INST/20220101/
260
- # 20220101204857274290500__20220101205857276524900__DEFAULT__INST__ALL__rt__reduced_minute.bin
261
- remote_log_directory = "#{scope}/reduced_#{type}_logs/tlm/#{target_name}"
262
- label = "#{scope}__#{target_name}__ALL__#{rt_or_stored}__reduced_#{type}"
263
- plw = BufferedPacketLogWriter.new(remote_log_directory, label, true, nil, 1_000_000_000, nil, nil, true, @buffer_depth)
264
- @packet_logs["#{scope}__#{target_name}__#{rt_or_stored}__#{type}"] = plw
265
- end
266
-
267
- # The lifetime of all these variables is a single file - single target / multiple packets
268
- reducer_state = {}
269
- plr = OpenC3::PacketLogReader.new
270
- throttle.throttle_sleep
271
- plr.each(file.local_path) do |packet|
272
- # Check to see if we should start a new log file before processing this packet
273
- current_time = packet.packet_time.to_nsec_from_epoch
274
- check_new_file(reducer_state, plw, type, target_name, stored, current_time, file_nanoseconds)
275
- state = setup_state(reducer_state, packet, current_time)
276
-
277
- # Determine if we've rolled over a entry boundary
278
- # We have to use current % entry_nanoseconds < previous % entry_nanoseconds because
279
- # we don't know the data rates. We also have to check for current - previous >= entry_nanoseconds
280
- # in case the data rate is so slow we don't have multiple samples per entry
281
- if state.previous_time &&
282
- (
283
- (state.current_time % entry_nanoseconds < state.previous_time % entry_nanoseconds) || # Try to create at perfect intervals
284
- (state.current_time - state.previous_time >= entry_nanoseconds) # Handle big gaps
285
- )
286
- write_entry(state, plw, type, target_name, packet.packet_name, stored)
287
- if check_new_file(reducer_state, plw, type, target_name, stored, current_time, file_nanoseconds)
288
- state = setup_state(reducer_state, packet, current_time)
289
- end
290
- end
291
-
292
- if type == 'minute'
293
- get_min_samples(packet, state)
294
- else
295
- get_hour_day_samples(packet, state)
296
- end
297
-
298
- reduced = state.reduced
299
- if type == 'minute'
300
- update_min_stats(reduced, state)
301
- else
302
- update_raw_hour_day_stats(reduced, state)
303
- update_converted_hour_day_stats(packet, reduced, state)
304
- end
305
- state.first = false
306
-
307
- throttle.throttle_sleep
308
- end
309
- file.delete # Remove the local copy
310
-
311
- write_all_entries(reducer_state, plw, type, target_name, stored, throttle)
312
-
313
- @logger.debug("Reducer Throttle: #{filename}: total_time: #{Time.now - throttle.reset_time}, sleep_time: #{throttle.total_sleep_time}")
314
-
315
- @count += 1
316
- if type == 'minute'
317
- metric_name = 'reducer_minute_total'
318
- elsif type == 'hour'
319
- metric_name = 'reducer_hour_total'
320
- else
321
- metric_name = 'reducer_day_total'
322
- end
323
- @previous_metrics[metric_name] ||= 0
324
- @previous_metrics[metric_name] += 1
325
- @metric.set(name: metric_name, value: @previous_metrics[metric_name], type: 'counter')
326
-
327
- true
328
- rescue => e
329
- if file.local_path and File.exist?(file.local_path)
330
- @logger.error("Reducer Error: #{file.local_path}: #{File.size(file.local_path)} bytes: \n#{e.formatted}")
331
- else
332
- @logger.error("Reducer Error: #{filename}: \n#{e.formatted}")
333
- end
334
-
335
- @error_count += 1
336
- if type == 'minute'
337
- metric_name = 'reducer_minute_error_total'
338
- elsif type == 'hour'
339
- metric_name = 'reducer_hour_error_total'
340
- else
341
- metric_name = 'reducer_day_error_total'
342
- end
343
- @previous_metrics[metric_name] ||= 0
344
- @previous_metrics[metric_name] += 1
345
- @metric.set(name: metric_name, value: @previous_metrics[metric_name], type: 'counter')
346
-
347
- file.delete
348
- false
349
- end
350
-
351
- def get_min_samples(packet, state)
352
- state.entry_samples ||= packet.json_hash.dup # Grab all the samples from the first packet
353
- if state.first
354
- state.raw_values = packet.read_all(:RAW, nil, packet.read_all_names(:RAW)).select { |key, value| value.is_a?(Numeric) }
355
- state.raw_keys ||= state.raw_values.keys
356
- state.converted_values = packet.read_all(:CONVERTED, nil, packet.read_all_names(:CONVERTED)).select { |key, value| value.is_a?(Numeric) }
357
- state.converted_keys ||= state.converted_values.keys
358
- else
359
- state.raw_values = packet.read_all(:RAW, nil, state.raw_keys).select { |key, value| value.is_a?(Numeric) }
360
- state.converted_values = packet.read_all(:CONVERTED, nil, state.converted_keys).select { |key, value| value.is_a?(Numeric) }
361
- end
362
- end
363
-
364
- def get_hour_day_samples(packet, state)
365
- # Hour or Day
366
- state.entry_samples ||= extract_entry_samples(packet)
367
- if state.first
368
- state.raw_max_values = packet.read_all(:RAW, :MAX, packet.read_all_names(:RAW, :MAX))
369
- state.raw_keys = state.raw_max_values.keys
370
- state.converted_max_values = packet.read_all(:CONVERTED, :MAX, packet.read_all_names(:CONVERTED, :MAX))
371
- state.converted_keys = state.converted_max_values.keys
372
- else
373
- state.raw_max_values = packet.read_all(:RAW, :MAX, state.raw_keys)
374
- state.converted_max_values = packet.read_all(:CONVERTED, :MAX, state.converted_keys)
375
- end
376
- state.raw_min_values = packet.read_all(:RAW, :MIN, state.raw_keys)
377
- state.raw_avg_values = packet.read_all(:RAW, :AVG, state.raw_keys)
378
- state.raw_stddev_values = packet.read_all(:RAW, :STDDEV, state.raw_keys)
379
- state.converted_min_values = packet.read_all(:CONVERTED, :MIN, state.converted_keys)
380
- state.converted_avg_values = packet.read_all(:CONVERTED, :AVG, state.converted_keys)
381
- state.converted_stddev_values = packet.read_all(:CONVERTED, :STDDEV, state.converted_keys)
382
- end
383
-
384
- if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
385
- def update_min_stats(reduced, state)
386
- # Update statistics for this packet's raw values
387
- state.raw_values.each do |key, value|
388
- if value
389
- vals_key = "#{key}__VALS"
390
- reduced[vals_key] ||= []
391
- reduced[vals_key] << value
392
- n_key = "#{key}__N"
393
- reduced[n_key] ||= value
394
- reduced[n_key] = value if value < reduced[n_key]
395
- x_key = "#{key}__X"
396
- reduced[x_key] ||= value
397
- reduced[x_key] = value if value > reduced[x_key]
398
- end
399
- end
400
-
401
- # Update statistics for this packet's converted values
402
- state.converted_values.each do |key, value|
403
- if value
404
- cvals_key = "#{key}__CVALS"
405
- reduced[cvals_key] ||= []
406
- reduced[cvals_key] << value
407
- cn_key = "#{key}__CN"
408
- reduced[cn_key] ||= value
409
- reduced[cn_key] = value if value < reduced[cn_key]
410
- cx_key = "#{key}__CX"
411
- reduced[cx_key] ||= value
412
- reduced[cx_key] = value if value > reduced[cx_key]
413
- end
414
- end
415
- end
416
- end
417
-
418
- def update_raw_hour_day_stats(reduced, state)
419
- # Update statistics for this packet's raw values
420
- state.raw_max_values.each do |key, value|
421
- if value
422
- max_key = "#{key}__X"
423
- reduced[max_key] ||= value
424
- reduced[max_key] = value if value > reduced[max_key]
425
- end
426
- end
427
- state.raw_min_values.each do |key, value|
428
- if value
429
- min_key = "#{key}__N"
430
- reduced[min_key] ||= value
431
- reduced[min_key] = value if value < reduced[min_key]
432
- end
433
- end
434
- state.raw_avg_values.each do |key, value|
435
- if value
436
- avg_values_key = "#{key}__AVGVALS"
437
- reduced[avg_values_key] ||= []
438
- reduced[avg_values_key] << value
439
- end
440
- end
441
- state.raw_stddev_values.each do |key, value|
442
- if value
443
- stddev_values_key = "#{key}__STDDEVVALS"
444
- reduced[stddev_values_key] ||= []
445
- reduced[stddev_values_key] << value
446
- end
447
- end
448
- end
449
-
450
- def update_converted_hour_day_stats(packet, reduced, state)
451
- # Update statistics for this packet's converted values
452
- state.converted_max_values.each do |key, value|
453
- if value
454
- max_key = "#{key}__CX"
455
- reduced[max_key] ||= value
456
- reduced[max_key] = value if value > reduced[max_key]
457
- end
458
- end
459
- state.converted_min_values.each do |key, value|
460
- if value
461
- min_key = "#{key}__CN"
462
- reduced[min_key] ||= value
463
- reduced[min_key] = value if value < reduced[min_key]
464
- end
465
- end
466
- state.converted_avg_values.each do |key, value|
467
- if value
468
- avg_values_key = "#{key}__CAVGVALS"
469
- reduced[avg_values_key] ||= []
470
- reduced[avg_values_key] << value
471
- end
472
- end
473
- state.converted_stddev_values.each do |key, value|
474
- if value
475
- stddev_values_key = "#{key}__CSTDDEVVALS"
476
- reduced[stddev_values_key] ||= []
477
- reduced[stddev_values_key] << value
478
- end
479
- end
480
-
481
- reduced["_NUM_SAMPLES__VALS"] ||= []
482
- reduced["_NUM_SAMPLES__VALS"] << packet.read('_NUM_SAMPLES')
483
- end
484
-
485
- def check_new_file(reducer_state, plw, type, target_name, stored, current_time, file_nanoseconds)
486
- plw_first_time_nsec = plw.buffered_first_time_nsec
487
- if plw_first_time_nsec && ((current_time - plw_first_time_nsec) >= file_nanoseconds)
488
- # Write out all entries in progress
489
- write_all_entries(reducer_state, plw, type, target_name, stored)
490
- reducer_state.clear
491
- plw.close_file
492
- return true
493
- else
494
- return false
495
- end
496
- end
497
-
498
- def setup_state(reducer_state, packet, current_time)
499
- # Get state for this packet
500
- state = reducer_state[packet.packet_name]
501
- unless state
502
- state = ReducerState.new
503
- reducer_state[packet.packet_name] = state
504
- end
505
-
506
- # Update state timestamps
507
- state.previous_time = state.current_time # Will be nil first packet
508
- state.current_time = current_time
509
- state.entry_time ||= state.current_time # Sets the entry time from the first packet
510
- return state
511
- end
512
-
513
- def write_all_entries(reducer_state, plw, type, target_name, stored, throttle = nil)
514
- reducer_state.each do |packet_name, state|
515
- write_entry(state, plw, type, target_name, packet_name, stored)
516
- throttle.throttle_sleep if throttle
517
- end
518
- end
519
-
520
- def write_entry(state, plw, type, target_name, packet_name, stored)
521
- return unless state.reduced.length > 0
522
- reduce(type, state.raw_keys, state.converted_keys, state.reduced)
523
- state.reduced.merge!(state.entry_samples)
524
- time = state.entry_time
525
- data = JSON.generate(state.reduced.as_json, allow_nan: true)
526
- if type == "minute"
527
- redis_topic, redis_offset = TelemetryReducedMinuteTopic.write(target_name: target_name, packet_name: packet_name, stored: stored, time: time, data: data, scope: @scope)
528
- elsif type == "hour"
529
- redis_topic, redis_offset = TelemetryReducedHourTopic.write(target_name: target_name, packet_name: packet_name, stored: stored, time: time, data: data, scope: @scope)
530
- else
531
- redis_topic, redis_offset = TelemetryReducedDayTopic.write(target_name: target_name, packet_name: packet_name, stored: stored, time: time, data: data, scope: @scope)
532
- end
533
- plw.buffered_write(
534
- :JSON_PACKET,
535
- :TLM,
536
- target_name,
537
- packet_name,
538
- time,
539
- stored,
540
- data,
541
- nil,
542
- redis_topic,
543
- redis_offset
544
- )
545
-
546
- # Reset necessary state variables
547
- state.entry_time = state.current_time # This packet starts the next entry
548
- state.entry_samples = nil
549
- state.reduced = {}
550
- end
551
-
552
- def reduce(type, raw_keys, converted_keys, reduced)
553
- # We've collected all the values so calculate the AVG and STDDEV
554
- if type == 'minute'
555
- raw_keys.each do |key|
556
- if reduced["#{key}__VALS"]
557
- reduced["_NUM_SAMPLES"] ||= reduced["#{key}__VALS"].length # Keep a single sample count per packet
558
- reduced["#{key}__A"], reduced["#{key}__S"] =
559
- Math.stddev_population(reduced["#{key}__VALS"])
560
- # Remove the raw values as they're only used for AVG / STDDEV calculation
561
- reduced.delete("#{key}__VALS")
562
- end
563
- end
564
-
565
- converted_keys.each do |key|
566
- if reduced["#{key}__CVALS"]
567
- reduced["_NUM_SAMPLES"] ||= reduced["#{key}__CVALS"].length # Keep a single sample count per packet
568
- reduced["#{key}__CA"], reduced["#{key}__CS"] =
569
- Math.stddev_population(reduced["#{key}__CVALS"])
570
-
571
- # Remove the converted values as they're only used for AVG / STDDEV calculation
572
- reduced.delete("#{key}__CVALS")
573
- end
574
- end
575
- else
576
- samples = reduced["_NUM_SAMPLES__VALS"]
577
- samples_sum = samples.sum
578
- reduced["_NUM_SAMPLES"] = samples_sum
579
- reduced.delete("_NUM_SAMPLES__VALS")
580
-
581
- raw_keys.each { |key| reduce_running(key, reduced, samples, samples_sum, "__A", "__S", "__AVGVALS", "__STDDEVVALS") }
582
- converted_keys.each { |key| reduce_running(key, reduced, samples, samples_sum, "__CA", "__CS", "__CAVGVALS", "__CSTDDEVVALS") }
583
- end
584
- end
585
-
586
- def reduce_running(key, reduced, samples, samples_sum, avg_key, stddev_key, avgvals_key, stddevvals_key)
587
- # Calculate Average
588
- weighted_sum = 0
589
- avg = reduced["#{key}#{avgvals_key}"]
590
- if avg
591
- avg.each_with_index do |val, i|
592
- weighted_sum += (val * samples[i])
593
- end
594
- reduced["#{key}#{avg_key}"] = weighted_sum / samples_sum
595
- end
596
-
597
- # Do the STDDEV calc last so we can use the previously calculated AVG
598
- # See https://math.stackexchange.com/questions/1547141/aggregating-standard-deviation-to-a-summary-point
599
- s2 = 0
600
- stddev = reduced["#{key}#{stddevvals_key}"]
601
- if stddev
602
- stddev.each_with_index do |val, i|
603
- # puts "i:#{i} val:#{val} samples[i]:#{samples[i]} avg[i]:#{avg[i]}"
604
- s2 += (samples[i] * avg[i]**2 + val**2)
605
- end
606
-
607
- # Note: For very large numbers with very small deviations this sqrt can fail.
608
- # If so then just set the stddev to 0.
609
- begin
610
- reduced["#{key}#{stddev_key}"] =
611
- Math.sqrt(s2 / samples_sum - reduced["#{key}#{avg_key}"])
612
- rescue Exception
613
- reduced["#{key}#{stddev_key}"] = 0.0
614
- end
615
- end
616
-
617
- reduced.delete("#{key}#{avgvals_key}")
618
- reduced.delete("#{key}#{stddevvals_key}")
619
- end
620
-
621
- # Extract just the not reduced fields from a JsonPacket
622
- def extract_entry_samples(packet)
623
- result = {}
624
- packet.json_hash.each do |key, value|
625
- key_split = key.split('__')
626
- if (not key_split[1] or not ['N', 'X', 'A', 'S'].include?(key_split[1][-1])) and key != '_NUM_SAMPLES'
627
- result[key] = value
628
- end
629
- end
630
- return result
631
- end
632
-
633
- end
634
- end
635
-
636
- if __FILE__ == $0
637
- OpenC3::ReducerMicroservice.run
638
- OpenC3::ThreadManager.instance.shutdown
639
- OpenC3::ThreadManager.instance.join
640
- end