openc3 5.0.11 → 5.1.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of openc3 might be problematic. Click here for more details.

Files changed (280) hide show
  1. checksums.yaml +4 -4
  2. data/Guardfile +3 -0
  3. data/LICENSE.txt +7 -5
  4. data/README.md +11 -9
  5. data/Rakefile +3 -0
  6. data/bin/cstol_converter +3 -0
  7. data/bin/openc3cli +29 -18
  8. data/bin/rubysloc +3 -0
  9. data/data/config/screen.yaml +10 -2
  10. data/data/config/target.yaml +1 -1
  11. data/data/config/widgets.yaml +6 -6
  12. data/ext/mkrf_conf.rb +3 -0
  13. data/ext/openc3/ext/array/array.c +3 -0
  14. data/ext/openc3/ext/buffered_file/buffered_file.c +3 -0
  15. data/ext/openc3/ext/config_parser/config_parser.c +3 -0
  16. data/ext/openc3/ext/crc/crc.c +3 -0
  17. data/ext/openc3/ext/openc3_io/openc3_io.c +3 -0
  18. data/ext/openc3/ext/packet/packet.c +3 -0
  19. data/ext/openc3/ext/platform/platform.c +3 -0
  20. data/ext/openc3/ext/polynomial_conversion/polynomial_conversion.c +3 -0
  21. data/ext/openc3/ext/string/string.c +3 -0
  22. data/ext/openc3/ext/structure/structure.c +3 -0
  23. data/ext/openc3/ext/tabbed_plots_config/tabbed_plots_config.c +3 -0
  24. data/ext/openc3/ext/telemetry/telemetry.c +3 -0
  25. data/lib/cosmos.rb +3 -0
  26. data/lib/cosmosc2.rb +3 -0
  27. data/lib/openc3/accessors/accessor.rb +3 -0
  28. data/lib/openc3/accessors/binary_accessor.rb +3 -0
  29. data/lib/openc3/accessors/cbor_accessor.rb +3 -0
  30. data/lib/openc3/accessors/html_accessor.rb +3 -0
  31. data/lib/openc3/accessors/json_accessor.rb +4 -1
  32. data/lib/openc3/accessors/xml_accessor.rb +3 -0
  33. data/lib/openc3/accessors.rb +3 -0
  34. data/lib/openc3/api/api.rb +3 -0
  35. data/lib/openc3/api/authorized_api.rb +3 -0
  36. data/lib/openc3/api/cmd_api.rb +6 -3
  37. data/lib/openc3/api/config_api.rb +3 -0
  38. data/lib/openc3/api/interface_api.rb +6 -2
  39. data/lib/openc3/api/limits_api.rb +54 -61
  40. data/lib/openc3/api/router_api.rb +6 -3
  41. data/lib/openc3/api/settings_api.rb +3 -0
  42. data/lib/openc3/api/target_api.rb +3 -0
  43. data/lib/openc3/api/tlm_api.rb +27 -32
  44. data/lib/openc3/bridge/bridge.rb +3 -0
  45. data/lib/openc3/bridge/bridge_config.rb +3 -0
  46. data/lib/openc3/bridge/bridge_interface_thread.rb +3 -0
  47. data/lib/openc3/bridge/bridge_router_thread.rb +3 -0
  48. data/lib/openc3/ccsds/ccsds_packet.rb +3 -0
  49. data/lib/openc3/ccsds/ccsds_parser.rb +3 -0
  50. data/lib/openc3/config/config_parser.rb +3 -0
  51. data/lib/openc3/config/meta_config_parser.rb +3 -0
  52. data/lib/openc3/conversions/conversion.rb +3 -0
  53. data/lib/openc3/conversions/generic_conversion.rb +3 -0
  54. data/lib/openc3/conversions/packet_time_formatted_conversion.rb +3 -0
  55. data/lib/openc3/conversions/packet_time_seconds_conversion.rb +3 -0
  56. data/lib/openc3/conversions/polynomial_conversion.rb +3 -0
  57. data/lib/openc3/conversions/processor_conversion.rb +3 -0
  58. data/lib/openc3/conversions/received_count_conversion.rb +3 -0
  59. data/lib/openc3/conversions/received_time_formatted_conversion.rb +3 -0
  60. data/lib/openc3/conversions/received_time_seconds_conversion.rb +3 -0
  61. data/lib/openc3/conversions/segmented_polynomial_conversion.rb +3 -0
  62. data/lib/openc3/conversions/unix_time_conversion.rb +3 -0
  63. data/lib/openc3/conversions/unix_time_formatted_conversion.rb +3 -0
  64. data/lib/openc3/conversions/unix_time_seconds_conversion.rb +3 -0
  65. data/lib/openc3/conversions.rb +3 -0
  66. data/lib/openc3/core_ext/array.rb +3 -0
  67. data/lib/openc3/core_ext/binding.rb +3 -0
  68. data/lib/openc3/core_ext/class.rb +3 -0
  69. data/lib/openc3/core_ext/exception.rb +3 -0
  70. data/lib/openc3/core_ext/file.rb +3 -0
  71. data/lib/openc3/core_ext/hash.rb +3 -0
  72. data/lib/openc3/core_ext/io.rb +3 -0
  73. data/lib/openc3/core_ext/kernel.rb +3 -0
  74. data/lib/openc3/core_ext/math.rb +3 -0
  75. data/lib/openc3/core_ext/matrix.rb +3 -0
  76. data/lib/openc3/core_ext/objectspace.rb +3 -0
  77. data/lib/openc3/core_ext/openc3_io.rb +3 -0
  78. data/lib/openc3/core_ext/range.rb +3 -0
  79. data/lib/openc3/core_ext/socket.rb +3 -0
  80. data/lib/openc3/core_ext/string.rb +3 -0
  81. data/lib/openc3/core_ext/stringio.rb +3 -0
  82. data/lib/openc3/core_ext/tempfile.rb +3 -0
  83. data/lib/openc3/core_ext/time.rb +3 -0
  84. data/lib/openc3/core_ext.rb +3 -0
  85. data/lib/openc3/interfaces/interface.rb +3 -0
  86. data/lib/openc3/interfaces/linc_interface.rb +3 -0
  87. data/lib/openc3/interfaces/protocols/burst_protocol.rb +3 -0
  88. data/lib/openc3/interfaces/protocols/crc_protocol.rb +3 -0
  89. data/lib/openc3/interfaces/protocols/fixed_protocol.rb +3 -0
  90. data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +3 -0
  91. data/lib/openc3/interfaces/protocols/length_protocol.rb +3 -0
  92. data/lib/openc3/interfaces/protocols/override_protocol.rb +3 -0
  93. data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +3 -0
  94. data/lib/openc3/interfaces/protocols/protocol.rb +3 -0
  95. data/lib/openc3/interfaces/protocols/template_protocol.rb +3 -0
  96. data/lib/openc3/interfaces/protocols/terminated_protocol.rb +3 -0
  97. data/lib/openc3/interfaces/serial_interface.rb +3 -0
  98. data/lib/openc3/interfaces/simulated_target_interface.rb +3 -0
  99. data/lib/openc3/interfaces/stream_interface.rb +3 -0
  100. data/lib/openc3/interfaces/tcpip_client_interface.rb +3 -0
  101. data/lib/openc3/interfaces/tcpip_server_interface.rb +3 -0
  102. data/lib/openc3/interfaces/udp_interface.rb +3 -0
  103. data/lib/openc3/interfaces.rb +3 -0
  104. data/lib/openc3/io/buffered_file.rb +3 -0
  105. data/lib/openc3/io/io_multiplexer.rb +8 -0
  106. data/lib/openc3/io/json_api_object.rb +5 -2
  107. data/lib/openc3/io/json_drb.rb +3 -0
  108. data/lib/openc3/io/json_drb_object.rb +5 -2
  109. data/lib/openc3/io/json_drb_rack.rb +3 -0
  110. data/lib/openc3/io/json_rpc.rb +8 -3
  111. data/lib/openc3/io/openc3_snmp.rb +3 -0
  112. data/lib/openc3/io/posix_serial_driver.rb +3 -0
  113. data/lib/openc3/io/raw_logger.rb +3 -0
  114. data/lib/openc3/io/raw_logger_pair.rb +3 -0
  115. data/lib/openc3/io/serial_driver.rb +3 -0
  116. data/lib/openc3/io/stderr.rb +3 -0
  117. data/lib/openc3/io/stdout.rb +3 -0
  118. data/lib/openc3/io/udp_sockets.rb +3 -0
  119. data/lib/openc3/io/win32_serial_driver.rb +3 -0
  120. data/lib/openc3/logs/buffered_packet_log_reader.rb +65 -0
  121. data/lib/openc3/logs/buffered_packet_log_writer.rb +120 -0
  122. data/lib/openc3/logs/log_writer.rb +95 -40
  123. data/lib/openc3/logs/packet_log_constants.rb +9 -0
  124. data/lib/openc3/logs/packet_log_reader.rb +34 -3
  125. data/lib/openc3/logs/packet_log_writer.rb +85 -18
  126. data/lib/openc3/logs/text_log_writer.rb +9 -5
  127. data/lib/openc3/logs.rb +8 -2
  128. data/lib/openc3/microservices/cleanup_microservice.rb +18 -18
  129. data/lib/openc3/microservices/decom_microservice.rb +30 -24
  130. data/lib/openc3/microservices/interface_microservice.rb +136 -91
  131. data/lib/openc3/microservices/log_microservice.rb +35 -13
  132. data/lib/openc3/microservices/microservice.rb +16 -14
  133. data/lib/openc3/microservices/plugin_microservice.rb +3 -1
  134. data/lib/openc3/microservices/reaction_microservice.rb +4 -1
  135. data/lib/openc3/microservices/reducer_microservice.rb +332 -149
  136. data/lib/openc3/microservices/router_microservice.rb +3 -0
  137. data/lib/openc3/microservices/text_log_microservice.rb +22 -7
  138. data/lib/openc3/microservices/timeline_microservice.rb +4 -1
  139. data/lib/openc3/microservices/trigger_group_microservice.rb +3 -0
  140. data/lib/openc3/models/activity_model.rb +3 -0
  141. data/lib/openc3/models/auth_model.rb +3 -0
  142. data/lib/openc3/models/cvt_model.rb +14 -5
  143. data/lib/openc3/models/environment_model.rb +3 -0
  144. data/lib/openc3/models/gem_model.rb +30 -51
  145. data/lib/openc3/models/info_model.rb +3 -0
  146. data/lib/openc3/models/interface_model.rb +3 -0
  147. data/lib/openc3/models/interface_status_model.rb +4 -1
  148. data/lib/openc3/models/metadata_model.rb +3 -0
  149. data/lib/openc3/models/metric_model.rb +3 -0
  150. data/lib/openc3/models/microservice_model.rb +9 -6
  151. data/lib/openc3/models/microservice_status_model.rb +4 -1
  152. data/lib/openc3/models/model.rb +3 -0
  153. data/lib/openc3/models/note_model.rb +3 -0
  154. data/lib/openc3/models/notification_model.rb +3 -0
  155. data/lib/openc3/models/ping_model.rb +3 -0
  156. data/lib/openc3/models/plugin_model.rb +20 -14
  157. data/lib/openc3/models/process_status_model.rb +4 -1
  158. data/lib/openc3/models/reaction_model.rb +3 -0
  159. data/lib/openc3/models/reducer_model.rb +31 -24
  160. data/lib/openc3/models/router_model.rb +3 -0
  161. data/lib/openc3/models/router_status_model.rb +3 -0
  162. data/lib/openc3/models/scope_model.rb +3 -4
  163. data/lib/openc3/models/settings_model.rb +3 -0
  164. data/lib/openc3/models/sorted_model.rb +3 -0
  165. data/lib/openc3/models/target_model.rb +61 -94
  166. data/lib/openc3/models/timeline_model.rb +4 -1
  167. data/lib/openc3/models/tool_config_model.rb +3 -0
  168. data/lib/openc3/models/tool_model.rb +11 -9
  169. data/lib/openc3/models/trigger_group_model.rb +3 -0
  170. data/lib/openc3/models/trigger_model.rb +3 -0
  171. data/lib/openc3/models/widget_model.rb +18 -11
  172. data/lib/openc3/operators/microservice_operator.rb +3 -0
  173. data/lib/openc3/operators/operator.rb +105 -34
  174. data/lib/openc3/packets/commands.rb +3 -0
  175. data/lib/openc3/packets/json_packet.rb +87 -14
  176. data/lib/openc3/packets/limits.rb +4 -1
  177. data/lib/openc3/packets/limits_response.rb +3 -0
  178. data/lib/openc3/packets/packet.rb +5 -1
  179. data/lib/openc3/packets/packet_config.rb +3 -0
  180. data/lib/openc3/packets/packet_item.rb +9 -3
  181. data/lib/openc3/packets/packet_item_limits.rb +3 -0
  182. data/lib/openc3/packets/parsers/format_string_parser.rb +3 -0
  183. data/lib/openc3/packets/parsers/limits_parser.rb +3 -0
  184. data/lib/openc3/packets/parsers/limits_response_parser.rb +3 -0
  185. data/lib/openc3/packets/parsers/packet_item_parser.rb +3 -0
  186. data/lib/openc3/packets/parsers/packet_parser.rb +3 -0
  187. data/lib/openc3/packets/parsers/processor_parser.rb +3 -0
  188. data/lib/openc3/packets/parsers/state_parser.rb +3 -0
  189. data/lib/openc3/packets/parsers/xtce_converter.rb +3 -0
  190. data/lib/openc3/packets/parsers/xtce_parser.rb +3 -0
  191. data/lib/openc3/packets/structure.rb +3 -0
  192. data/lib/openc3/packets/structure_item.rb +3 -0
  193. data/lib/openc3/packets/telemetry.rb +3 -0
  194. data/lib/openc3/processors/processor.rb +3 -0
  195. data/lib/openc3/processors/statistics_processor.rb +3 -0
  196. data/lib/openc3/processors/watermark_processor.rb +3 -0
  197. data/lib/openc3/processors.rb +3 -0
  198. data/lib/openc3/script/api_shared.rb +35 -6
  199. data/lib/openc3/script/calendar.rb +3 -0
  200. data/lib/openc3/script/commands.rb +3 -0
  201. data/lib/openc3/script/exceptions.rb +3 -0
  202. data/lib/openc3/script/extract.rb +3 -0
  203. data/lib/openc3/script/limits.rb +3 -24
  204. data/lib/openc3/script/script.rb +11 -7
  205. data/lib/openc3/script/script_runner.rb +3 -0
  206. data/lib/openc3/script/storage.rb +33 -16
  207. data/lib/openc3/script/suite.rb +3 -0
  208. data/lib/openc3/script/suite_results.rb +3 -0
  209. data/lib/openc3/script/suite_runner.rb +3 -0
  210. data/lib/openc3/script/telemetry.rb +43 -0
  211. data/lib/openc3/script.rb +3 -0
  212. data/lib/openc3/streams/serial_stream.rb +3 -0
  213. data/lib/openc3/streams/stream.rb +3 -0
  214. data/lib/openc3/streams/tcpip_client_stream.rb +3 -0
  215. data/lib/openc3/streams/tcpip_socket_stream.rb +3 -0
  216. data/lib/openc3/system/system.rb +23 -10
  217. data/lib/openc3/system/system_config.rb +3 -0
  218. data/lib/openc3/system/target.rb +3 -0
  219. data/lib/openc3/system.rb +3 -0
  220. data/lib/openc3/tools/cmd_tlm_server/api.rb +3 -0
  221. data/lib/openc3/tools/cmd_tlm_server/cmd_tlm_server_config.rb +3 -0
  222. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +3 -0
  223. data/lib/openc3/tools/table_manager/table.rb +3 -0
  224. data/lib/openc3/tools/table_manager/table_config.rb +3 -0
  225. data/lib/openc3/tools/table_manager/table_item.rb +3 -0
  226. data/lib/openc3/tools/table_manager/table_item_parser.rb +3 -0
  227. data/lib/openc3/tools/table_manager/table_manager_core.rb +3 -0
  228. data/lib/openc3/tools/table_manager/table_parser.rb +3 -0
  229. data/lib/openc3/tools/test_runner/test.rb +3 -0
  230. data/lib/openc3/top_level.rb +3 -0
  231. data/lib/openc3/topics/autonomic_topic.rb +3 -0
  232. data/lib/openc3/topics/calendar_topic.rb +3 -0
  233. data/lib/openc3/topics/command_decom_topic.rb +4 -1
  234. data/lib/openc3/topics/command_topic.rb +6 -1
  235. data/lib/openc3/topics/config_topic.rb +3 -0
  236. data/lib/openc3/topics/interface_topic.rb +9 -2
  237. data/lib/openc3/topics/limits_event_topic.rb +144 -10
  238. data/lib/openc3/topics/notifications_topic.rb +3 -0
  239. data/lib/openc3/topics/router_topic.rb +10 -3
  240. data/lib/openc3/topics/telemetry_decom_topic.rb +26 -20
  241. data/lib/openc3/topics/telemetry_reduced_topics.rb +92 -0
  242. data/lib/openc3/topics/telemetry_topic.rb +5 -2
  243. data/lib/openc3/topics/timeline_topic.rb +3 -0
  244. data/lib/openc3/topics/topic.rb +3 -0
  245. data/lib/openc3/utilities/authentication.rb +3 -0
  246. data/lib/openc3/utilities/authorization.rb +3 -0
  247. data/lib/openc3/utilities/aws_bucket.rb +199 -0
  248. data/lib/openc3/utilities/bucket.rb +82 -0
  249. data/lib/openc3/utilities/bucket_file_cache.rb +264 -0
  250. data/lib/openc3/utilities/bucket_utilities.rb +109 -0
  251. data/lib/openc3/utilities/crc.rb +3 -0
  252. data/lib/openc3/utilities/csv.rb +3 -0
  253. data/lib/openc3/utilities/local_bucket.rb +28 -0
  254. data/lib/openc3/utilities/local_mode.rb +47 -61
  255. data/lib/openc3/utilities/logger.rb +7 -1
  256. data/lib/openc3/utilities/message_log.rb +7 -4
  257. data/lib/openc3/utilities/metric.rb +4 -1
  258. data/lib/openc3/utilities/open_telemetry.rb +96 -0
  259. data/lib/openc3/utilities/process_manager.rb +3 -0
  260. data/lib/openc3/utilities/quaternion.rb +3 -0
  261. data/lib/openc3/utilities/ruby_lex_utils.rb +3 -0
  262. data/lib/openc3/utilities/s3_autoload.rb +3 -3
  263. data/lib/openc3/utilities/simulated_target.rb +3 -0
  264. data/lib/openc3/utilities/sleeper.rb +3 -0
  265. data/lib/openc3/utilities/store.rb +3 -0
  266. data/lib/openc3/utilities/store_autoload.rb +30 -23
  267. data/lib/openc3/utilities/target_file.rb +70 -83
  268. data/lib/openc3/utilities/zip.rb +3 -0
  269. data/lib/openc3/utilities.rb +3 -0
  270. data/lib/openc3/version.rb +6 -6
  271. data/lib/openc3/win32/excel.rb +3 -0
  272. data/lib/openc3/win32/win32.rb +3 -0
  273. data/lib/openc3/win32/win32_main.rb +3 -0
  274. data/lib/openc3.rb +3 -0
  275. data/tasks/gemfile_stats.rake +3 -0
  276. data/tasks/spec.rake +3 -0
  277. data/templates/plugin-template/plugin.gemspec +1 -1
  278. metadata +112 -6
  279. data/lib/openc3/utilities/s3.rb +0 -220
  280. data/lib/openc3/utilities/s3_file_cache.rb +0 -274
@@ -16,15 +16,62 @@
16
16
  # Modified by OpenC3, Inc.
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
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.
19
22
 
20
23
  require 'openc3/microservices/microservice'
21
24
  require 'openc3/topics/topic'
25
+ require 'openc3/topics/telemetry_reduced_topics'
22
26
  require 'openc3/packets/json_packet'
23
- require 'openc3/utilities/s3_file_cache'
27
+ require 'openc3/utilities/bucket_file_cache'
24
28
  require 'openc3/models/reducer_model'
29
+ require 'openc3/logs/buffered_packet_log_writer'
25
30
  require 'rufus-scheduler'
26
31
 
27
32
  module OpenC3
33
+ class ReducerState
34
+ attr_accessor :reduced
35
+ attr_accessor :raw_keys
36
+ attr_accessor :converted_keys
37
+ attr_accessor :entry_time
38
+ attr_accessor :entry_samples
39
+ attr_accessor :current_time
40
+ attr_accessor :previous_time
41
+ attr_accessor :raw_values
42
+ attr_accessor :raw_max_values
43
+ attr_accessor :raw_min_values
44
+ attr_accessor :raw_avg_values
45
+ attr_accessor :raw_stddev_values
46
+ attr_accessor :converted_values
47
+ attr_accessor :converted_max_values
48
+ attr_accessor :converted_min_values
49
+ attr_accessor :converted_avg_values
50
+ attr_accessor :converted_stddev_values
51
+ attr_accessor :first
52
+
53
+ def initialize
54
+ @reduced = {}
55
+ @raw_keys = nil
56
+ @converted_keys = nil
57
+ @entry_time = nil
58
+ @entry_samples = nil
59
+ @current_time = nil
60
+ @previous_time = nil
61
+ @raw_values = nil
62
+ @raw_max_values = nil
63
+ @raw_min_values = nil
64
+ @raw_avg_values = nil
65
+ @raw_stddev_values = nil
66
+ @converted_values = nil
67
+ @converted_max_values = nil
68
+ @converted_min_values = nil
69
+ @converted_avg_values = nil
70
+ @converted_stddev_values = nil
71
+ @first = true
72
+ end
73
+ end
74
+
28
75
  class ReducerMicroservice < Microservice
29
76
  MINUTE_METRIC = 'reducer_minute_duration'
30
77
  HOUR_METRIC = 'reducer_hour_duration'
@@ -32,17 +79,30 @@ module OpenC3
32
79
 
33
80
  # How long to wait for any currently running jobs to complete before killing them
34
81
  SHUTDOWN_DELAY_SECS = 5
35
- MINUTE_ENTRY_SECS = 60
36
- MINUTE_FILE_SECS = 3600
37
- HOUR_ENTRY_SECS = 3600
38
- HOUR_FILE_SECS = 3600 * 24
39
- DAY_ENTRY_SECS = 3600 * 24
40
- DAY_FILE_SECS = 3600 * 24 * 30
82
+ MINUTE_ENTRY_NSECS = 60 * 1_000_000_000
83
+ MINUTE_FILE_NSECS = 3600 * 1_000_000_000
84
+ HOUR_ENTRY_NSECS = 3600 * 1_000_000_000
85
+ HOUR_FILE_NSECS = 3600 * 24 * 1_000_000_000
86
+ DAY_ENTRY_NSECS = 3600 * 24 * 1_000_000_000
87
+ DAY_FILE_NSECS = 3600 * 24 * 5 * 1_000_000_000
41
88
 
42
89
  # @param name [String] Microservice name formatted as <SCOPE>__REDUCER__<TARGET>
43
90
  # where <SCOPE> and <TARGET> are variables representing the scope name and target name
44
91
  def initialize(name)
45
92
  super(name, is_plugin: false)
93
+
94
+ if @config['options']
95
+ @config['options'].each do |option|
96
+ case option[0].upcase
97
+ when 'BUFFER_DEPTH' # Buffer depth to write in time order
98
+ @buffer_depth = option[1].to_i
99
+ else
100
+ Logger.error("Unknown option passed to microservice #{@name}: #{option}")
101
+ end
102
+ end
103
+ end
104
+
105
+ @buffer_depth = 10 unless @buffer_depth
46
106
  @target_name = name.split('__')[-1]
47
107
  @packet_logs = {}
48
108
  end
@@ -69,11 +129,17 @@ module OpenC3
69
129
  end
70
130
 
71
131
  def shutdown
132
+ Logger.info("Shutting down reducer microservice: #{@name}")
72
133
  @scheduler.shutdown(wait: SHUTDOWN_DELAY_SECS) if @scheduler
73
134
 
74
135
  # Make sure all the existing logs are properly closed down
136
+ threads = []
75
137
  @packet_logs.each do |name, log|
76
- log.shutdown
138
+ threads.concat(log.shutdown)
139
+ end
140
+ # Wait for all the logging threads to move files to buckets
141
+ threads.flatten.compact.each do |thread|
142
+ thread.join
77
143
  end
78
144
  super()
79
145
  end
@@ -96,7 +162,7 @@ module OpenC3
96
162
  ReducerModel
97
163
  .all_files(type: :DECOM, target: @target_name, scope: @scope)
98
164
  .each do |file|
99
- process_file(file, 'minute', MINUTE_ENTRY_SECS, MINUTE_FILE_SECS)
165
+ process_file(file, 'minute', MINUTE_ENTRY_NSECS, MINUTE_FILE_NSECS)
100
166
  ReducerModel.rm_file(file)
101
167
  end
102
168
  end
@@ -107,7 +173,7 @@ module OpenC3
107
173
  ReducerModel
108
174
  .all_files(type: :MINUTE, target: @target_name, scope: @scope)
109
175
  .each do |file|
110
- process_file(file, 'hour', HOUR_ENTRY_SECS, HOUR_FILE_SECS)
176
+ process_file(file, 'hour', HOUR_ENTRY_NSECS, HOUR_FILE_NSECS)
111
177
  ReducerModel.rm_file(file)
112
178
  end
113
179
  end
@@ -118,195 +184,312 @@ module OpenC3
118
184
  ReducerModel
119
185
  .all_files(type: :HOUR, target: @target_name, scope: @scope)
120
186
  .each do |file|
121
- process_file(file, 'day', DAY_ENTRY_SECS, DAY_FILE_SECS)
187
+ process_file(file, 'day', DAY_ENTRY_NSECS, DAY_FILE_NSECS)
122
188
  ReducerModel.rm_file(file)
123
189
  end
124
190
  end
125
191
  end
126
192
 
127
- def process_file(filename, type, entry_seconds, file_seconds)
128
- file = S3File.new(filename)
193
+ def process_file(filename, type, entry_nanoseconds, file_nanoseconds)
194
+ file = BucketFile.new(filename)
129
195
  file.retrieve
130
196
 
131
197
  # Determine if we already have a PacketLogWriter created
132
- start_time, end_time, scope, target_name, packet_name, _ =
133
- filename.split('__')
198
+ _, _, scope, target_name, _, rt_or_stored, _ = File.basename(filename).split('__')
199
+ stored = (rt_or_stored == "stored")
200
+
134
201
  if @target_name != target_name
135
202
  raise "Target name in file #{filename} does not match microservice target name #{@target_name}"
136
203
  end
137
- plw = @packet_logs["#{scope}__#{target_name}__#{packet_name}__#{type}"]
204
+ plw = @packet_logs["#{scope}__#{target_name}__#{rt_or_stored}__#{type}"]
138
205
  unless plw
139
206
  # Create a new PacketLogWriter for this reduced data
140
- # e.g. DEFAULT/reduced_minute_logs/tlm/INST/HEALTH_STATUS/20220101/
141
- # 20220101204857274290500__20220101205857276524900__DEFAULT__INST__HEALTH_STATUS__reduced__minute.bin
142
- remote_log_directory = "#{scope}/reduced_#{type}_logs/tlm/#{target_name}/#{packet_name}"
143
- rt_label = "#{scope}__#{target_name}__#{packet_name}__reduced__#{type}"
144
- plw = PacketLogWriter.new(remote_log_directory, rt_label)
145
- @packet_logs["#{scope}__#{target_name}__#{packet_name}__#{type}"] = plw
207
+ # e.g. DEFAULT/reduced_minute_logs/tlm/INST/20220101/
208
+ # 20220101204857274290500__20220101205857276524900__DEFAULT__INST__ALL__rt__reduced_minute.bin
209
+ remote_log_directory = "#{scope}/reduced_#{type}_logs/tlm/#{target_name}"
210
+ label = "#{scope}__#{target_name}__ALL__#{rt_or_stored}__reduced_#{type}"
211
+ plw = BufferedPacketLogWriter.new(remote_log_directory, label, true, nil, 1_000_000_000, nil, nil, true, @buffer_depth)
212
+ @packet_logs["#{scope}__#{target_name}__#{rt_or_stored}__#{type}"] = plw
146
213
  end
147
214
 
148
- reduced = {}
149
- data_keys = nil
150
- entry_time = nil
151
- current_time = nil
152
- previous_time = nil
215
+ # The lifetime of all these variables is a single file - single target / multiple packets
216
+ reducer_state = {}
153
217
  plr = OpenC3::PacketLogReader.new
154
218
  plr.each(file.local_path) do |packet|
155
- # Ignore anything except numbers like STRING or BLOCK items
156
- data = packet.read_all(:RAW).select { |key, value| value.is_a?(Numeric) }
157
- converted_data = packet.read_all(:CONVERTED).select { |key, value| value.is_a?(Numeric) }
158
- # Merge in the converted data which overwrites the raw
159
- data.merge!(converted_data)
160
-
161
- previous_time = current_time
162
- current_time = packet.packet_time.to_f
163
- entry_time ||= current_time
164
- data_keys ||= data.keys
219
+ # Check to see if we should start a new log file before processing this packet
220
+ current_time = packet.packet_time.to_nsec_from_epoch
221
+ check_new_file(reducer_state, plw, type, target_name, stored, current_time, file_nanoseconds)
222
+ state = setup_state(reducer_state, packet, current_time)
165
223
 
166
224
  # Determine if we've rolled over a entry boundary
167
- # We have to use current % entry_seconds < previous % entry_seconds because
168
- # we don't know the data rates. We also have to check for current - previous >= entry_seconds
225
+ # We have to use current % entry_nanoseconds < previous % entry_nanoseconds because
226
+ # we don't know the data rates. We also have to check for current - previous >= entry_nanoseconds
169
227
  # in case the data rate is so slow we don't have multiple samples per entry
170
- if previous_time &&
171
- (
172
- (current_time % entry_seconds < previous_time % entry_seconds) ||
173
- (current_time - previous_time >= entry_seconds)
174
- )
175
- Logger.debug("Reducer: Roll over entry boundary cur_time:#{current_time}")
176
-
177
- reduce(type, data_keys, reduced)
178
- plw.write(
179
- :JSON_PACKET,
180
- :TLM,
181
- target_name,
182
- packet_name,
183
- entry_time * Time::NSEC_PER_SECOND,
184
- false,
185
- JSON.generate(reduced.as_json(:allow_nan => true)),
228
+ if state.previous_time &&
229
+ (
230
+ (state.current_time % entry_nanoseconds < state.previous_time % entry_nanoseconds) || # Try to create at perfect intervals
231
+ (state.current_time - state.previous_time >= entry_nanoseconds) # Handle big gaps
186
232
  )
187
- # Reset all our sample variables
188
- entry_time = current_time
189
- reduced = {}
190
-
191
- # Check to see if we should start a new log file
192
- # We compare the current entry_time to see if it will push us over
193
- if plw.first_time &&
194
- (entry_time - plw.first_time.to_f) >= file_seconds
195
- Logger.debug("Reducer: (1) start new file! old filename: #{plw.filename}")
196
- plw.start_new_file # Automatically closes the current file
233
+ write_entry(state, plw, type, target_name, packet.packet_name, stored)
234
+ if check_new_file(reducer_state, plw, type, target_name, stored, current_time, file_nanoseconds)
235
+ state = setup_state(reducer_state, packet, current_time)
236
+ end
237
+ end
238
+
239
+ if type == 'minute'
240
+ state.entry_samples ||= packet.json_hash.dup # Grab all the samples from the first packet
241
+ if state.first
242
+ state.raw_values = packet.read_all(:RAW, nil, packet.read_all_names(:RAW)).select { |key, value| value.is_a?(Numeric) }
243
+ state.raw_keys ||= state.raw_values.keys
244
+ state.converted_values = packet.read_all(:CONVERTED, nil, packet.read_all_names(:CONVERTED)).select { |key, value| value.is_a?(Numeric) }
245
+ state.converted_keys ||= state.converted_values.keys
246
+ else
247
+ state.raw_values = packet.read_all(:RAW, nil, state.raw_keys).select { |key, value| value.is_a?(Numeric) }
248
+ state.converted_values = packet.read_all(:CONVERTED, nil, state.converted_keys).select { |key, value| value.is_a?(Numeric) }
197
249
  end
250
+ else
251
+ # Hour or Day
252
+ state.entry_samples ||= extract_entry_samples(packet)
253
+ if state.first
254
+ state.raw_max_values = packet.read_all(:RAW, :MAX, packet.read_all_names(:RAW, :MAX))
255
+ state.raw_keys = state.raw_max_values.keys
256
+ state.converted_max_values = packet.read_all(:CONVERTED, :MAX, packet.read_all_names(:CONVERTED, :MAX))
257
+ state.converted_keys = state.converted_max_values.keys
258
+ else
259
+ state.raw_max_values = packet.read_all(:RAW, :MAX, state.raw_keys)
260
+ state.converted_max_values = packet.read_all(:CONVERTED, :MAX, state.converted_keys)
261
+ end
262
+ state.raw_min_values = packet.read_all(:RAW, :MIN, state.raw_keys)
263
+ state.raw_avg_values = packet.read_all(:RAW, :AVG, state.raw_keys)
264
+ state.raw_stddev_values = packet.read_all(:RAW, :STDDEV, state.raw_keys)
265
+ state.converted_min_values = packet.read_all(:CONVERTED, :MIN, state.converted_keys)
266
+ state.converted_avg_values = packet.read_all(:CONVERTED, :AVG, state.converted_keys)
267
+ state.converted_stddev_values = packet.read_all(:CONVERTED, :STDDEV, state.converted_keys)
198
268
  end
199
269
 
200
- # Update statistics for this packet's values
201
- data.each do |key, value|
202
- if type == 'minute'
270
+ reduced = state.reduced
271
+ if type == 'minute'
272
+ # Update statistics for this packet's raw values
273
+ state.raw_values.each do |key, value|
203
274
  reduced["#{key}__VALS"] ||= []
204
275
  reduced["#{key}__VALS"] << value
205
- reduced["#{key}_MIN"] ||= value
206
- reduced["#{key}_MIN"] = value if value < reduced["#{key}_MIN"]
207
- reduced["#{key}_MAX"] ||= value
208
- reduced["#{key}_MAX"] = value if value > reduced["#{key}_MAX"]
209
- else
210
- reduced[key] ||= value
211
- reduced[key] = value if key.match(/_MIN$/) && value < reduced[key]
212
- reduced[key] = value if key.match(/_MAX$/) && value > reduced[key]
213
- if key.match(/_AVG$/)
214
- reduced["#{key}__VALS"] ||= []
215
- reduced["#{key}__VALS"] << value
216
- end
217
- if key.match(/_STDDEV$/)
218
- reduced["#{key}__VALS"] ||= []
219
- reduced["#{key}__VALS"] << value
220
- end
221
- if key.match(/_SAMPLES$/)
222
- reduced["#{key}__VALS"] ||= []
223
- reduced["#{key}__VALS"] << value
224
- end
276
+ reduced["#{key}__N"] ||= value
277
+ reduced["#{key}__N"] = value if value < reduced["#{key}__N"]
278
+ reduced["#{key}__X"] ||= value
279
+ reduced["#{key}__X"] = value if value > reduced["#{key}__X"]
225
280
  end
281
+
282
+ # Update statistics for this packet's converted values
283
+ state.converted_values.each do |key, value|
284
+ reduced["#{key}__CVALS"] ||= []
285
+ reduced["#{key}__CVALS"] << value
286
+ reduced["#{key}__CN"] ||= value
287
+ reduced["#{key}__CN"] = value if value < reduced["#{key}__CN"]
288
+ reduced["#{key}__CX"] ||= value
289
+ reduced["#{key}__CX"] = value if value > reduced["#{key}__CX"]
290
+ end
291
+ else
292
+ # Update statistics for this packet's raw values
293
+ state.raw_max_values.each do |key, value|
294
+ max_key = "#{key}__X"
295
+ reduced[max_key] ||= value
296
+ reduced[max_key] = value if value > reduced[max_key]
297
+ end
298
+ state.raw_min_values.each do |key, value|
299
+ min_key = "#{key}__N"
300
+ reduced[min_key] ||= value
301
+ reduced[min_key] = value if value < reduced[min_key]
302
+ end
303
+ state.raw_avg_values.each do |key, value|
304
+ avg_values_key = "#{key}__AVGVALS"
305
+ reduced[avg_values_key] ||= []
306
+ reduced[avg_values_key] << value
307
+ end
308
+ state.raw_stddev_values.each do |key, value|
309
+ stddev_values_key = "#{key}__STDDEVVALS"
310
+ reduced[stddev_values_key] ||= []
311
+ reduced[stddev_values_key] << value
312
+ end
313
+
314
+ # Update statistics for this packet's converted values
315
+ state.converted_max_values.each do |key, value|
316
+ max_key = "#{key}__CX"
317
+ reduced[max_key] ||= value
318
+ reduced[max_key] = value if value > reduced[max_key]
319
+ end
320
+ state.converted_min_values.each do |key, value|
321
+ min_key = "#{key}__CN"
322
+ reduced[min_key] ||= value
323
+ reduced[min_key] = value if value < reduced[min_key]
324
+ end
325
+ state.converted_avg_values.each do |key, value|
326
+ avg_values_key = "#{key}__CAVGVALS"
327
+ reduced[avg_values_key] ||= []
328
+ reduced[avg_values_key] << value
329
+ end
330
+ state.converted_stddev_values.each do |key, value|
331
+ stddev_values_key = "#{key}__CSTDDEVVALS"
332
+ reduced[stddev_values_key] ||= []
333
+ reduced[stddev_values_key] << value
334
+ end
335
+
336
+ reduced["_NUM_SAMPLES__VALS"] ||= []
337
+ reduced["_NUM_SAMPLES__VALS"] << packet.read('_NUM_SAMPLES')
226
338
  end
339
+
340
+ state.first = false
227
341
  end
228
342
  file.delete # Remove the local copy
229
343
 
230
- # See if this last entry should go in a new file
231
- if plw.first_time &&
232
- (entry_time - plw.first_time.to_f) >= file_seconds
233
- Logger.debug("Reducer: (2) start new file! old filename: #{plw.filename}")
234
- plw.start_new_file # Automatically closes the current file
344
+ write_all_entries(reducer_state, plw, type, target_name, stored)
345
+ true
346
+ rescue => e
347
+ if file.local_path and File.exist?(file.local_path)
348
+ Logger.error("Reducer Error: #{filename}: #{File.size(file.local_path)} bytes: \n#{e.formatted}")
349
+ else
350
+ Logger.error("Reducer Error: #{filename}: \n#{e.formatted}")
235
351
  end
352
+ false
353
+ end
236
354
 
237
- # Write out the final data now that the file is done
238
- reduce(type, data_keys, reduced)
239
- plw.write(
355
+ def check_new_file(reducer_state, plw, type, target_name, stored, current_time, file_nanoseconds)
356
+ plw_first_time_nsec = plw.buffered_first_time_nsec
357
+ if plw_first_time_nsec && ((current_time - plw_first_time_nsec) >= file_nanoseconds)
358
+ # Write out all entries in progress
359
+ write_all_entries(reducer_state, plw, type, target_name, stored)
360
+ reducer_state.clear
361
+ plw.start_new_file(true) # Automatically closes the current file
362
+ return true
363
+ else
364
+ return false
365
+ end
366
+ end
367
+
368
+ def setup_state(reducer_state, packet, current_time)
369
+ # Get state for this packet
370
+ state = reducer_state[packet.packet_name]
371
+ unless state
372
+ state = ReducerState.new
373
+ reducer_state[packet.packet_name] = state
374
+ end
375
+
376
+ # Update state timestamps
377
+ state.previous_time = state.current_time # Will be nil first packet
378
+ state.current_time = current_time
379
+ state.entry_time ||= state.current_time # Sets the entry time from the first packet
380
+ return state
381
+ end
382
+
383
+ def write_all_entries(reducer_state, plw, type, target_name, stored)
384
+ reducer_state.each do |packet_name, state|
385
+ write_entry(state, plw, type, target_name, packet_name, stored)
386
+ end
387
+ end
388
+
389
+ def write_entry(state, plw, type, target_name, packet_name, stored)
390
+ return unless state.reduced.length > 0
391
+ reduce(type, state.raw_keys, state.converted_keys, state.reduced)
392
+ state.reduced.merge!(state.entry_samples)
393
+ time = state.entry_time
394
+ data = JSON.generate(state.reduced.as_json(:allow_nan => true))
395
+ if type == "minute"
396
+ redis_topic, redis_offset = TelemetryReducedMinuteTopic.write(target_name: target_name, packet_name: packet_name, stored: stored, time: time, data: data, scope: @scope)
397
+ elsif type == "hour"
398
+ redis_topic, redis_offset = TelemetryReducedHourTopic.write(target_name: target_name, packet_name: packet_name, stored: stored, time: time, data: data, scope: @scope)
399
+ else
400
+ redis_topic, redis_offset = TelemetryReducedDayTopic.write(target_name: target_name, packet_name: packet_name, stored: stored, time: time, data: data, scope: @scope)
401
+ end
402
+ plw.buffered_write(
240
403
  :JSON_PACKET,
241
404
  :TLM,
242
405
  target_name,
243
406
  packet_name,
244
- entry_time * Time::NSEC_PER_SECOND,
245
- false,
246
- JSON.generate(reduced.as_json(:allow_nan => true)),
407
+ time,
408
+ stored,
409
+ data,
410
+ nil,
411
+ redis_topic,
412
+ redis_offset
247
413
  )
248
- true
249
- rescue => e
250
- if file.local_path and File.exist?(file.local_path)
251
- Logger.error("Reducer Error: #{filename}:#{File.size(file.local_path)} bytes: \n#{e.formatted}")
252
- else
253
- Logger.error("Reducer Error: #{filename}:(Not Retrieved): \n#{e.formatted}")
254
- end
255
- false
414
+
415
+ # Reset necessary state variables
416
+ state.entry_time = state.current_time # This packet starts the next entry
417
+ state.entry_samples = nil
418
+ state.reduced = {}
256
419
  end
257
420
 
258
- def reduce(type, data_keys, reduced)
421
+ def reduce(type, raw_keys, converted_keys, reduced)
259
422
  # We've collected all the values so calculate the AVG and STDDEV
260
423
  if type == 'minute'
261
- data_keys.each do |key|
262
- reduced["#{key}_SAMPLES"] = reduced["#{key}__VALS"].length
263
- reduced["#{key}_AVG"], reduced["#{key}_STDDEV"] =
424
+ raw_keys.each do |key|
425
+ reduced["_NUM_SAMPLES"] ||= reduced["#{key}__VALS"].length # Keep a single sample count per packet
426
+ reduced["#{key}__A"], reduced["#{key}__S"] =
264
427
  Math.stddev_population(reduced["#{key}__VALS"])
265
-
266
428
  # Remove the raw values as they're only used for AVG / STDDEV calculation
267
429
  reduced.delete("#{key}__VALS")
268
430
  end
269
- else
270
- # Sort so we calculate the average first, then samples, then stddev
271
- data_keys.sort.each do |key|
272
- base_name = key.split('_')[0..-2].join('_')
273
- case key
274
- when /_AVG$/
275
- weighted_sum = 0
276
- samples = reduced["#{base_name}_SAMPLES__VALS"]
277
- reduced["#{key}__VALS"].each_with_index do |val, i|
278
- weighted_sum += (val * samples[i])
279
- end
280
- reduced[key] = weighted_sum / samples.sum
281
- when /_SAMPLES$/
282
- reduced[key] = reduced["#{base_name}_SAMPLES__VALS"].sum
283
- when /_STDDEV$/
284
- # Do the STDDEV calc last so we can use the previously calculated AVG
285
- # See https://math.stackexchange.com/questions/1547141/aggregating-standard-deviation-to-a-summary-point
286
- samples = reduced["#{base_name}_SAMPLES__VALS"]
287
- avg = reduced["#{base_name}_AVG__VALS"]
288
- s2 = 0
289
- reduced["#{key}__VALS"].each_with_index do |val, i|
290
- # puts "i:#{i} val:#{val} samples[i]:#{samples[i]} avg[i]:#{avg[i]}"
291
- s2 += (samples[i] * avg[i]**2 + val**2)
292
- end
293
-
294
- # Note: For very large numbers with very small deviations this sqrt can fail.
295
- # If so then just set the stddev to 0.
296
- begin
297
- reduced[key] =
298
- Math.sqrt(s2 / samples.sum - reduced["#{base_name}_AVG"])
299
- rescue Exception
300
- reduced[key] = 0.0
301
- end
302
- end
431
+
432
+ converted_keys.each do |key|
433
+ reduced["_NUM_SAMPLES"] ||= reduced["#{key}__CVALS"].length # Keep a single sample count per packet
434
+ reduced["#{key}__CA"], reduced["#{key}__CS"] =
435
+ Math.stddev_population(reduced["#{key}__CVALS"])
436
+
437
+ # Remove the converted values as they're only used for AVG / STDDEV calculation
438
+ reduced.delete("#{key}__CVALS")
303
439
  end
304
- data_keys.each do |key|
305
- # Remove the raw values as they're only used for AVG / STDDEV calculation
306
- reduced.delete("#{key}__VALS")
440
+ else
441
+ samples = reduced["_NUM_SAMPLES__VALS"]
442
+ samples_sum = samples.sum
443
+ reduced["_NUM_SAMPLES"] = samples_sum
444
+ reduced.delete("_NUM_SAMPLES__VALS")
445
+
446
+ raw_keys.each { |key| reduce_running(key, reduced, samples, samples_sum, "__A", "__S", "__AVGVALS", "__STDDEVVALS") }
447
+ converted_keys.each { |key| reduce_running(key, reduced, samples, samples_sum, "__CA", "__CS", "__CAVGVALS", "__CSTDDEVVALS") }
448
+ end
449
+ end
450
+
451
+ def reduce_running(key, reduced, samples, samples_sum, avg_key, stddev_key, avgvals_key, stddevvals_key)
452
+ # Calculate Average
453
+ weighted_sum = 0
454
+ avg = reduced["#{key}#{avgvals_key}"]
455
+ avg.each_with_index do |val, i|
456
+ weighted_sum += (val * samples[i])
457
+ end
458
+ reduced["#{key}#{avg_key}"] = weighted_sum / samples_sum
459
+
460
+ # Do the STDDEV calc last so we can use the previously calculated AVG
461
+ # See https://math.stackexchange.com/questions/1547141/aggregating-standard-deviation-to-a-summary-point
462
+ s2 = 0
463
+ reduced["#{key}#{stddevvals_key}"].each_with_index do |val, i|
464
+ # puts "i:#{i} val:#{val} samples[i]:#{samples[i]} avg[i]:#{avg[i]}"
465
+ s2 += (samples[i] * avg[i]**2 + val**2)
466
+ end
467
+
468
+ # Note: For very large numbers with very small deviations this sqrt can fail.
469
+ # If so then just set the stddev to 0.
470
+ begin
471
+ reduced["#{key}#{stddev_key}"] =
472
+ Math.sqrt(s2 / samples_sum - reduced["#{key}#{avg_key}"])
473
+ rescue Exception
474
+ reduced["#{key}#{stddev_key}"] = 0.0
475
+ end
476
+
477
+ reduced.delete("#{key}#{avgvals_key}")
478
+ reduced.delete("#{key}#{stddevvals_key}")
479
+ end
480
+
481
+ # Extract just the not reduced fields from a JsonPacket
482
+ def extract_entry_samples(packet)
483
+ result = {}
484
+ packet.json_hash.each do |key, value|
485
+ key_split = key.split('__')
486
+ if (not key_split[1] or not ['N', 'X', 'A', 'S'].include?(key_split[1][-1])) and key != '_NUM_SAMPLES'
487
+ result[key] = value
307
488
  end
308
489
  end
490
+ return result
309
491
  end
492
+
310
493
  end
311
494
  end
312
495
 
@@ -16,6 +16,9 @@
16
16
  # Modified by OpenC3, Inc.
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
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.
19
22
 
20
23
  require 'openc3/microservices/interface_microservice'
21
24
 
@@ -16,6 +16,9 @@
16
16
  # Modified by OpenC3, Inc.
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
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.
19
22
 
20
23
  require 'openc3/microservices/microservice'
21
24
  require 'openc3/topics/topic'
@@ -41,36 +44,35 @@ module OpenC3
41
44
  end
42
45
 
43
46
  def run
44
- tlws = setup_tlws
47
+ setup_tlws()
45
48
  while true
46
49
  break if @cancel_thread
47
50
 
48
51
  Topic.read_topics(@topics) do |topic, msg_id, msg_hash, redis|
49
52
  break if @cancel_thread
50
53
 
51
- log_data(tlws, topic, msg_id, msg_hash, redis)
54
+ log_data(topic, msg_id, msg_hash, redis)
52
55
  end
53
56
  end
54
57
  end
55
58
 
56
59
  def setup_tlws
57
- tlws = {}
60
+ @tlws = {}
58
61
  @topics.each do |topic|
59
62
  topic_split = topic.gsub(/{|}/, '').split("__") # Remove the redis hashtag curly braces
60
63
  scope = topic_split[0]
61
64
  log_name = topic_split[1]
62
65
  remote_log_directory = "#{scope}/text_logs/#{log_name}"
63
- tlws[topic] = TextLogWriter.new(remote_log_directory, true, @cycle_time, @cycle_size, redis_topic: topic)
66
+ @tlws[topic] = TextLogWriter.new(remote_log_directory, true, @cycle_time, @cycle_size, nil, nil, false)
64
67
  end
65
- return tlws
66
68
  end
67
69
 
68
- def log_data(tlws, topic, msg_id, msg_hash, redis)
70
+ def log_data(topic, msg_id, msg_hash, redis)
69
71
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
70
72
  keys = msg_hash.keys
71
73
  keys.delete("time")
72
74
  entry = keys.reduce("") { |data, key| data + "#{key}: #{msg_hash[key]}\t" }
73
- tlws[topic].write(msg_hash["time"].to_i, entry, msg_id)
75
+ @tlws[topic].write(msg_hash["time"].to_i, entry, topic, msg_id)
74
76
  @count += 1
75
77
  diff = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start # seconds as a float
76
78
  @metric.add_sample(name: "log_duration_seconds", value: diff, labels: {})
@@ -78,6 +80,19 @@ module OpenC3
78
80
  @error = err
79
81
  Logger.error("#{@name} error: #{err.formatted}")
80
82
  end
83
+
84
+ def shutdown
85
+ # Make sure all the existing logs are properly closed down
86
+ threads = []
87
+ @tlws.each do |topic, tlw|
88
+ threads.concat(tlw.shutdown)
89
+ end
90
+ # Wait for all the logging threads to move files to buckets
91
+ threads.flatten.compact.each do |thread|
92
+ thread.join
93
+ end
94
+ super()
95
+ end
81
96
  end
82
97
  end
83
98