openc3 5.0.6

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 (307) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +18 -0
  3. data/Guardfile +35 -0
  4. data/LICENSE.txt +727 -0
  5. data/README.md +37 -0
  6. data/Rakefile +131 -0
  7. data/bin/cstol_converter +1178 -0
  8. data/bin/openc3cli +531 -0
  9. data/bin/rubysloc +139 -0
  10. data/data/config/_array_params.yaml +23 -0
  11. data/data/config/_id_items.yaml +24 -0
  12. data/data/config/_id_params.yaml +58 -0
  13. data/data/config/_interfaces.yaml +214 -0
  14. data/data/config/_interfaces.yaml.err +1017 -0
  15. data/data/config/_items.yaml +20 -0
  16. data/data/config/_params.yaml +60 -0
  17. data/data/config/cmd_tlm_server.yaml +136 -0
  18. data/data/config/command.yaml +44 -0
  19. data/data/config/command_modifiers.yaml +160 -0
  20. data/data/config/command_telemetry.yaml +3 -0
  21. data/data/config/interface_modifiers.yaml +104 -0
  22. data/data/config/item_modifiers.yaml +221 -0
  23. data/data/config/microservice.yaml +78 -0
  24. data/data/config/param_item_modifiers.yaml +52 -0
  25. data/data/config/parameter_modifiers.yaml +200 -0
  26. data/data/config/plugins.yaml +80 -0
  27. data/data/config/protocols.yaml +290 -0
  28. data/data/config/screen.yaml +147 -0
  29. data/data/config/table_manager.yaml +89 -0
  30. data/data/config/table_parameter_modifiers.yaml +9 -0
  31. data/data/config/target.yaml +142 -0
  32. data/data/config/target_config.yaml +94 -0
  33. data/data/config/telemetry.yaml +87 -0
  34. data/data/config/telemetry_modifiers.yaml +159 -0
  35. data/data/config/tool.yaml +63 -0
  36. data/data/config/unknown.yaml +3 -0
  37. data/data/config/widgets.yaml +1505 -0
  38. data/ext/mkrf_conf.rb +49 -0
  39. data/ext/openc3/ext/array/array.c +122 -0
  40. data/ext/openc3/ext/array/extconf.rb +13 -0
  41. data/ext/openc3/ext/buffered_file/buffered_file.c +198 -0
  42. data/ext/openc3/ext/buffered_file/extconf.rb +13 -0
  43. data/ext/openc3/ext/config_parser/config_parser.c +280 -0
  44. data/ext/openc3/ext/config_parser/extconf.rb +13 -0
  45. data/ext/openc3/ext/crc/crc.c +351 -0
  46. data/ext/openc3/ext/crc/extconf.rb +13 -0
  47. data/ext/openc3/ext/openc3_io/extconf.rb +13 -0
  48. data/ext/openc3/ext/openc3_io/openc3_io.c +158 -0
  49. data/ext/openc3/ext/packet/extconf.rb +13 -0
  50. data/ext/openc3/ext/packet/packet.c +318 -0
  51. data/ext/openc3/ext/platform/extconf.rb +13 -0
  52. data/ext/openc3/ext/platform/platform.c +134 -0
  53. data/ext/openc3/ext/polynomial_conversion/extconf.rb +13 -0
  54. data/ext/openc3/ext/polynomial_conversion/polynomial_conversion.c +79 -0
  55. data/ext/openc3/ext/string/extconf.rb +13 -0
  56. data/ext/openc3/ext/string/string.c +63 -0
  57. data/ext/openc3/ext/structure/structure.c +1719 -0
  58. data/ext/openc3/ext/tabbed_plots_config/extconf.rb +13 -0
  59. data/ext/openc3/ext/tabbed_plots_config/tabbed_plots_config.c +62 -0
  60. data/ext/openc3/ext/telemetry/extconf.rb +13 -0
  61. data/ext/openc3/ext/telemetry/telemetry.c +336 -0
  62. data/lib/cosmos.rb +20 -0
  63. data/lib/cosmosc2.rb +20 -0
  64. data/lib/openc3/api/api.rb +39 -0
  65. data/lib/openc3/api/authorized_api.rb +30 -0
  66. data/lib/openc3/api/cmd_api.rb +451 -0
  67. data/lib/openc3/api/config_api.rb +58 -0
  68. data/lib/openc3/api/interface_api.rb +117 -0
  69. data/lib/openc3/api/limits_api.rb +375 -0
  70. data/lib/openc3/api/router_api.rb +117 -0
  71. data/lib/openc3/api/settings_api.rb +70 -0
  72. data/lib/openc3/api/target_api.rb +78 -0
  73. data/lib/openc3/api/tlm_api.rb +455 -0
  74. data/lib/openc3/bridge/bridge.rb +54 -0
  75. data/lib/openc3/bridge/bridge_config.rb +167 -0
  76. data/lib/openc3/bridge/bridge_interface_thread.rb +42 -0
  77. data/lib/openc3/bridge/bridge_router_thread.rb +42 -0
  78. data/lib/openc3/ccsds/ccsds_packet.rb +68 -0
  79. data/lib/openc3/ccsds/ccsds_parser.rb +148 -0
  80. data/lib/openc3/config/config_parser.rb +549 -0
  81. data/lib/openc3/config/meta_config_parser.rb +74 -0
  82. data/lib/openc3/conversions/conversion.rb +70 -0
  83. data/lib/openc3/conversions/generic_conversion.rb +83 -0
  84. data/lib/openc3/conversions/packet_time_formatted_conversion.rb +43 -0
  85. data/lib/openc3/conversions/packet_time_seconds_conversion.rb +43 -0
  86. data/lib/openc3/conversions/polynomial_conversion.rb +87 -0
  87. data/lib/openc3/conversions/processor_conversion.rb +70 -0
  88. data/lib/openc3/conversions/received_count_conversion.rb +38 -0
  89. data/lib/openc3/conversions/received_time_formatted_conversion.rb +42 -0
  90. data/lib/openc3/conversions/received_time_seconds_conversion.rb +42 -0
  91. data/lib/openc3/conversions/segmented_polynomial_conversion.rb +171 -0
  92. data/lib/openc3/conversions/unix_time_conversion.rb +68 -0
  93. data/lib/openc3/conversions/unix_time_formatted_conversion.rb +49 -0
  94. data/lib/openc3/conversions/unix_time_seconds_conversion.rb +49 -0
  95. data/lib/openc3/conversions.rb +34 -0
  96. data/lib/openc3/core_ext/array.rb +416 -0
  97. data/lib/openc3/core_ext/binding.rb +29 -0
  98. data/lib/openc3/core_ext/class.rb +72 -0
  99. data/lib/openc3/core_ext/exception.rb +61 -0
  100. data/lib/openc3/core_ext/file.rb +83 -0
  101. data/lib/openc3/core_ext/hash.rb +37 -0
  102. data/lib/openc3/core_ext/io.rb +134 -0
  103. data/lib/openc3/core_ext/kernel.rb +42 -0
  104. data/lib/openc3/core_ext/math.rb +128 -0
  105. data/lib/openc3/core_ext/matrix.rb +156 -0
  106. data/lib/openc3/core_ext/objectspace.rb +36 -0
  107. data/lib/openc3/core_ext/openc3_io.rb +57 -0
  108. data/lib/openc3/core_ext/range.rb +27 -0
  109. data/lib/openc3/core_ext/socket.rb +38 -0
  110. data/lib/openc3/core_ext/string.rb +389 -0
  111. data/lib/openc3/core_ext/stringio.rb +33 -0
  112. data/lib/openc3/core_ext/time.rb +508 -0
  113. data/lib/openc3/core_ext.rb +36 -0
  114. data/lib/openc3/interfaces/interface.rb +498 -0
  115. data/lib/openc3/interfaces/linc_interface.rb +475 -0
  116. data/lib/openc3/interfaces/protocols/burst_protocol.rb +192 -0
  117. data/lib/openc3/interfaces/protocols/crc_protocol.rb +193 -0
  118. data/lib/openc3/interfaces/protocols/fixed_protocol.rb +155 -0
  119. data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +56 -0
  120. data/lib/openc3/interfaces/protocols/length_protocol.rb +165 -0
  121. data/lib/openc3/interfaces/protocols/override_protocol.rb +60 -0
  122. data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +206 -0
  123. data/lib/openc3/interfaces/protocols/protocol.rb +82 -0
  124. data/lib/openc3/interfaces/protocols/template_protocol.rb +261 -0
  125. data/lib/openc3/interfaces/protocols/terminated_protocol.rb +93 -0
  126. data/lib/openc3/interfaces/serial_interface.rb +94 -0
  127. data/lib/openc3/interfaces/simulated_target_interface.rb +168 -0
  128. data/lib/openc3/interfaces/stream_interface.rb +81 -0
  129. data/lib/openc3/interfaces/tcpip_client_interface.rb +69 -0
  130. data/lib/openc3/interfaces/tcpip_server_interface.rb +629 -0
  131. data/lib/openc3/interfaces/udp_interface.rb +169 -0
  132. data/lib/openc3/interfaces.rb +44 -0
  133. data/lib/openc3/io/buffered_file.rb +109 -0
  134. data/lib/openc3/io/io_multiplexer.rb +80 -0
  135. data/lib/openc3/io/json_api_object.rb +208 -0
  136. data/lib/openc3/io/json_drb.rb +335 -0
  137. data/lib/openc3/io/json_drb_object.rb +114 -0
  138. data/lib/openc3/io/json_drb_rack.rb +84 -0
  139. data/lib/openc3/io/json_rpc.rb +420 -0
  140. data/lib/openc3/io/openc3_snmp.rb +58 -0
  141. data/lib/openc3/io/posix_serial_driver.rb +156 -0
  142. data/lib/openc3/io/raw_logger.rb +167 -0
  143. data/lib/openc3/io/raw_logger_pair.rb +77 -0
  144. data/lib/openc3/io/serial_driver.rb +105 -0
  145. data/lib/openc3/io/stderr.rb +43 -0
  146. data/lib/openc3/io/stdout.rb +43 -0
  147. data/lib/openc3/io/udp_sockets.rb +194 -0
  148. data/lib/openc3/io/win32_serial_driver.rb +196 -0
  149. data/lib/openc3/logs/log_writer.rb +302 -0
  150. data/lib/openc3/logs/packet_log_constants.rb +62 -0
  151. data/lib/openc3/logs/packet_log_reader.rb +345 -0
  152. data/lib/openc3/logs/packet_log_writer.rb +299 -0
  153. data/lib/openc3/logs/text_log_writer.rb +68 -0
  154. data/lib/openc3/logs.rb +25 -0
  155. data/lib/openc3/microservices/cleanup_microservice.rb +68 -0
  156. data/lib/openc3/microservices/decom_microservice.rb +136 -0
  157. data/lib/openc3/microservices/interface_microservice.rb +532 -0
  158. data/lib/openc3/microservices/log_microservice.rb +108 -0
  159. data/lib/openc3/microservices/microservice.rb +204 -0
  160. data/lib/openc3/microservices/plugin_microservice.rb +43 -0
  161. data/lib/openc3/microservices/reaction_microservice.rb +541 -0
  162. data/lib/openc3/microservices/reducer_microservice.rb +313 -0
  163. data/lib/openc3/microservices/router_microservice.rb +44 -0
  164. data/lib/openc3/microservices/text_log_microservice.rb +84 -0
  165. data/lib/openc3/microservices/timeline_microservice.rb +363 -0
  166. data/lib/openc3/microservices/trigger_group_microservice.rb +638 -0
  167. data/lib/openc3/models/activity_model.rb +319 -0
  168. data/lib/openc3/models/auth_model.rb +65 -0
  169. data/lib/openc3/models/cvt_model.rb +185 -0
  170. data/lib/openc3/models/environment_model.rb +58 -0
  171. data/lib/openc3/models/gem_model.rb +137 -0
  172. data/lib/openc3/models/info_model.rb +31 -0
  173. data/lib/openc3/models/interface_model.rb +281 -0
  174. data/lib/openc3/models/interface_status_model.rb +117 -0
  175. data/lib/openc3/models/metadata_model.rb +139 -0
  176. data/lib/openc3/models/metric_model.rb +59 -0
  177. data/lib/openc3/models/microservice_model.rb +206 -0
  178. data/lib/openc3/models/microservice_status_model.rb +74 -0
  179. data/lib/openc3/models/model.rb +204 -0
  180. data/lib/openc3/models/note_model.rb +122 -0
  181. data/lib/openc3/models/notification_model.rb +40 -0
  182. data/lib/openc3/models/ping_model.rb +35 -0
  183. data/lib/openc3/models/plugin_model.rb +292 -0
  184. data/lib/openc3/models/process_status_model.rb +76 -0
  185. data/lib/openc3/models/reaction_model.rb +322 -0
  186. data/lib/openc3/models/reducer_model.rb +65 -0
  187. data/lib/openc3/models/router_model.rb +35 -0
  188. data/lib/openc3/models/router_status_model.rb +27 -0
  189. data/lib/openc3/models/scope_model.rb +153 -0
  190. data/lib/openc3/models/settings_model.rb +55 -0
  191. data/lib/openc3/models/sorted_model.rb +167 -0
  192. data/lib/openc3/models/target_model.rb +759 -0
  193. data/lib/openc3/models/timeline_model.rb +154 -0
  194. data/lib/openc3/models/tool_config_model.rb +38 -0
  195. data/lib/openc3/models/tool_model.rb +262 -0
  196. data/lib/openc3/models/trigger_group_model.rb +186 -0
  197. data/lib/openc3/models/trigger_model.rb +330 -0
  198. data/lib/openc3/models/widget_model.rb +138 -0
  199. data/lib/openc3/operators/microservice_operator.rb +128 -0
  200. data/lib/openc3/operators/operator.rb +277 -0
  201. data/lib/openc3/packets/binary_accessor.rb +1207 -0
  202. data/lib/openc3/packets/commands.rb +373 -0
  203. data/lib/openc3/packets/json_packet.rb +134 -0
  204. data/lib/openc3/packets/limits.rb +271 -0
  205. data/lib/openc3/packets/limits_response.rb +53 -0
  206. data/lib/openc3/packets/packet.rb +1168 -0
  207. data/lib/openc3/packets/packet_config.rb +625 -0
  208. data/lib/openc3/packets/packet_item.rb +586 -0
  209. data/lib/openc3/packets/packet_item_limits.rb +162 -0
  210. data/lib/openc3/packets/parsers/format_string_parser.rb +65 -0
  211. data/lib/openc3/packets/parsers/limits_parser.rb +159 -0
  212. data/lib/openc3/packets/parsers/limits_response_parser.rb +61 -0
  213. data/lib/openc3/packets/parsers/packet_item_parser.rb +272 -0
  214. data/lib/openc3/packets/parsers/packet_parser.rb +134 -0
  215. data/lib/openc3/packets/parsers/processor_parser.rb +73 -0
  216. data/lib/openc3/packets/parsers/state_parser.rb +127 -0
  217. data/lib/openc3/packets/parsers/xtce_converter.rb +442 -0
  218. data/lib/openc3/packets/parsers/xtce_parser.rb +722 -0
  219. data/lib/openc3/packets/structure.rb +553 -0
  220. data/lib/openc3/packets/structure_item.rb +365 -0
  221. data/lib/openc3/packets/telemetry.rb +487 -0
  222. data/lib/openc3/processors/processor.rb +86 -0
  223. data/lib/openc3/processors/statistics_processor.rb +82 -0
  224. data/lib/openc3/processors/watermark_processor.rb +58 -0
  225. data/lib/openc3/processors.rb +24 -0
  226. data/lib/openc3/script/api_shared.rb +828 -0
  227. data/lib/openc3/script/calendar.rb +89 -0
  228. data/lib/openc3/script/commands.rb +227 -0
  229. data/lib/openc3/script/exceptions.rb +29 -0
  230. data/lib/openc3/script/extract.rb +161 -0
  231. data/lib/openc3/script/limits.rb +60 -0
  232. data/lib/openc3/script/script.rb +299 -0
  233. data/lib/openc3/script/script_runner.rb +238 -0
  234. data/lib/openc3/script/storage.rb +146 -0
  235. data/lib/openc3/script/suite.rb +542 -0
  236. data/lib/openc3/script/suite_results.rb +196 -0
  237. data/lib/openc3/script/suite_runner.rb +217 -0
  238. data/lib/openc3/script.rb +21 -0
  239. data/lib/openc3/streams/serial_stream.rb +167 -0
  240. data/lib/openc3/streams/stream.rb +63 -0
  241. data/lib/openc3/streams/tcpip_client_stream.rb +116 -0
  242. data/lib/openc3/streams/tcpip_socket_stream.rb +195 -0
  243. data/lib/openc3/system/system.rb +127 -0
  244. data/lib/openc3/system/system_config.rb +411 -0
  245. data/lib/openc3/system/target.rb +269 -0
  246. data/lib/openc3/system.rb +24 -0
  247. data/lib/openc3/tools/cmd_tlm_server/api.rb +20 -0
  248. data/lib/openc3/tools/cmd_tlm_server/cmd_tlm_server_config.rb +320 -0
  249. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +294 -0
  250. data/lib/openc3/tools/table_manager/table.rb +77 -0
  251. data/lib/openc3/tools/table_manager/table_config.rb +273 -0
  252. data/lib/openc3/tools/table_manager/table_item.rb +90 -0
  253. data/lib/openc3/tools/table_manager/table_item_parser.rb +66 -0
  254. data/lib/openc3/tools/table_manager/table_manager_core.rb +333 -0
  255. data/lib/openc3/tools/table_manager/table_parser.rb +93 -0
  256. data/lib/openc3/tools/test_runner/test.rb +67 -0
  257. data/lib/openc3/top_level.rb +595 -0
  258. data/lib/openc3/topics/autonomic_topic.rb +52 -0
  259. data/lib/openc3/topics/calendar_topic.rb +44 -0
  260. data/lib/openc3/topics/command_decom_topic.rb +76 -0
  261. data/lib/openc3/topics/command_topic.rb +83 -0
  262. data/lib/openc3/topics/config_topic.rb +68 -0
  263. data/lib/openc3/topics/interface_topic.rb +73 -0
  264. data/lib/openc3/topics/limits_event_topic.rb +109 -0
  265. data/lib/openc3/topics/notifications_topic.rb +28 -0
  266. data/lib/openc3/topics/router_topic.rb +85 -0
  267. data/lib/openc3/topics/telemetry_decom_topic.rb +54 -0
  268. data/lib/openc3/topics/telemetry_topic.rb +36 -0
  269. data/lib/openc3/topics/timeline_topic.rb +45 -0
  270. data/lib/openc3/topics/topic.rb +53 -0
  271. data/lib/openc3/utilities/authentication.rb +141 -0
  272. data/lib/openc3/utilities/authorization.rb +51 -0
  273. data/lib/openc3/utilities/crc.rb +278 -0
  274. data/lib/openc3/utilities/csv.rb +153 -0
  275. data/lib/openc3/utilities/logger.rb +187 -0
  276. data/lib/openc3/utilities/message_log.rb +91 -0
  277. data/lib/openc3/utilities/metric.rb +141 -0
  278. data/lib/openc3/utilities/process_manager.rb +139 -0
  279. data/lib/openc3/utilities/quaternion.rb +257 -0
  280. data/lib/openc3/utilities/ruby_lex_utils.rb +568 -0
  281. data/lib/openc3/utilities/s3.rb +202 -0
  282. data/lib/openc3/utilities/s3_autoload.rb +9 -0
  283. data/lib/openc3/utilities/s3_file_cache.rb +274 -0
  284. data/lib/openc3/utilities/simulated_target.rb +117 -0
  285. data/lib/openc3/utilities/sleeper.rb +51 -0
  286. data/lib/openc3/utilities/store.rb +23 -0
  287. data/lib/openc3/utilities/store_autoload.rb +237 -0
  288. data/lib/openc3/utilities/zip.rb +21 -0
  289. data/lib/openc3/utilities.rb +35 -0
  290. data/lib/openc3/version.rb +14 -0
  291. data/lib/openc3/win32/excel.rb +132 -0
  292. data/lib/openc3/win32/win32.rb +402 -0
  293. data/lib/openc3/win32/win32_main.rb +333 -0
  294. data/lib/openc3.rb +49 -0
  295. data/tasks/gemfile_stats.rake +113 -0
  296. data/tasks/spec.rake +30 -0
  297. data/templates/plugin-template/README.md +15 -0
  298. data/templates/plugin-template/Rakefile +12 -0
  299. data/templates/plugin-template/plugin.gemspec +23 -0
  300. data/templates/plugin-template/plugin.txt +9 -0
  301. data/templates/plugin-template/targets/TARGET/cmd_tlm/cmd.txt +8 -0
  302. data/templates/plugin-template/targets/TARGET/cmd_tlm/tlm.txt +8 -0
  303. data/templates/plugin-template/targets/TARGET/lib/target.rb +10 -0
  304. data/templates/plugin-template/targets/TARGET/procedures/procedure.rb +3 -0
  305. data/templates/plugin-template/targets/TARGET/screens/status.txt +9 -0
  306. data/templates/plugin-template/targets/TARGET/target.txt +5 -0
  307. metadata +849 -0
@@ -0,0 +1,345 @@
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
+ require 'openc3/core_ext/io'
21
+ require 'openc3/packets/packet'
22
+ require 'openc3/packets/json_packet'
23
+ require 'openc3/io/buffered_file'
24
+ require 'openc3/logs/packet_log_constants'
25
+
26
+ module OpenC3
27
+ # Reads a packet log of either commands or telemetry.
28
+ class PacketLogReader
29
+ include PacketLogConstants
30
+
31
+ attr_reader :redis_offset
32
+
33
+ MAX_READ_SIZE = 1000000000
34
+
35
+ # Create a new log file reader
36
+ def initialize
37
+ reset()
38
+ end
39
+
40
+ # Yields back each packet as it is found in the log file.
41
+ #
42
+ # @param filename [String] The log file to read
43
+ # @param identify_and_define [Boolean] Once the packet has been read from
44
+ # the log file, whether to both identify the packet by setting the target
45
+ # and packet name, and define the packet by populating all the items.
46
+ # @param start_time [Time|nil] Time at which to start returning packets.
47
+ # Packets found with a timestamp before this time are ignored. Pass nil
48
+ # to return all packets.
49
+ # @param end_time [Time|nil] Time at which to stop returning packets.
50
+ # Packets found with a timestamp after this time are ignored. Pass nil
51
+ # to return all packets.
52
+ # @yieldparam packet [Packet]
53
+ # @return [Boolean] Whether we reached the end_time while reading
54
+ def each(filename, identify_and_define = true, start_time = nil, end_time = nil)
55
+ reached_end_time = false
56
+ open(filename)
57
+
58
+ # seek_to_time(start_time) if start_time
59
+
60
+ while true
61
+ packet = read(identify_and_define)
62
+ break unless packet
63
+
64
+ time = packet.packet_time
65
+ if time
66
+ next if start_time and time < start_time
67
+ # If we reach the end_time that means we found all the packets we asked for
68
+ # This can be used by callers to know they are done reading
69
+ if end_time and time > end_time
70
+ reached_end_time = true
71
+ break
72
+ end
73
+ end
74
+ yield packet
75
+ end
76
+ reached_end_time
77
+ ensure # No implicit return value in the ensure block
78
+ close()
79
+ end
80
+
81
+ # @param filename [String] The log filename to open
82
+ # @return [Boolean, Exception] Returns true if successfully changed to configuration specified in log,
83
+ # otherwise returns false and potentially an Exception class if an error occurred. If no error occurred
84
+ # false indicates that the requested configuration was simply not found.
85
+ def open(filename)
86
+ close()
87
+ reset()
88
+ @filename = filename
89
+ @file = BufferedFile.open(@filename, 'rb')
90
+ @max_read_size = @file.size
91
+ @max_read_size = MAX_READ_SIZE if @max_read_size > MAX_READ_SIZE
92
+ return read_file_header()
93
+ rescue => err
94
+ close()
95
+ raise err
96
+ end
97
+
98
+ # Closes the current log file
99
+ def close
100
+ @file.close if @file and !@file.closed?
101
+ end
102
+
103
+ # Read a packet from the log file
104
+ #
105
+ # @param identify_and_define (see #each)
106
+ # @return [Packet]
107
+ def read(identify_and_define = true)
108
+ # Read entry length
109
+ length = @file.read(4)
110
+ return nil if !length or length.length <= 0
111
+
112
+ length = length.unpack('N')[0]
113
+ entry = @file.read(length)
114
+ flags = entry[0..1].unpack('n')[0]
115
+
116
+ cmd_or_tlm = :TLM
117
+ cmd_or_tlm = :CMD if flags & OPENC3_CMD_FLAG_MASK == OPENC3_CMD_FLAG_MASK
118
+ stored = false
119
+ stored = true if flags & OPENC3_STORED_FLAG_MASK == OPENC3_STORED_FLAG_MASK
120
+ id = false
121
+ id = true if flags & OPENC3_ID_FLAG_MASK == OPENC3_ID_FLAG_MASK
122
+
123
+ if flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_JSON_PACKET_ENTRY_TYPE_MASK
124
+ packet_index, time_nsec_since_epoch = entry[2..11].unpack('nQ>')
125
+ json_data = entry[12..-1]
126
+ lookup_cmd_or_tlm, target_name, packet_name, id = @packets[packet_index]
127
+ if cmd_or_tlm != lookup_cmd_or_tlm
128
+ raise "Packet type mismatch, packet:#{cmd_or_tlm}, lookup:#{lookup_cmd_or_tlm}"
129
+ end
130
+
131
+ return JsonPacket.new(cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, json_data)
132
+ elsif flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_RAW_PACKET_ENTRY_TYPE_MASK
133
+ packet_index, time_nsec_since_epoch = entry[2..11].unpack('nQ>')
134
+ packet_data = entry[12..-1]
135
+ lookup_cmd_or_tlm, target_name, packet_name, id = @packets[packet_index]
136
+ if cmd_or_tlm != lookup_cmd_or_tlm
137
+ raise "Packet type mismatch, packet:#{cmd_or_tlm}, lookup:#{lookup_cmd_or_tlm}"
138
+ end
139
+
140
+ received_time = Time.from_nsec_from_epoch(time_nsec_since_epoch)
141
+ if identify_and_define
142
+ packet = identify_and_define_packet_data(cmd_or_tlm, target_name, packet_name, received_time, packet_data)
143
+ else
144
+ # Build Packet
145
+ packet = Packet.new(target_name, packet_name, :BIG_ENDIAN, nil, packet_data)
146
+ end
147
+ packet.set_received_time_fast(received_time)
148
+ packet.cmd_or_tlm = cmd_or_tlm
149
+ packet.stored = stored
150
+ packet.received_count += 1
151
+ return packet
152
+ elsif flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_TARGET_DECLARATION_ENTRY_TYPE_MASK
153
+ target_name_length = length - OPENC3_PRIMARY_FIXED_SIZE - OPENC3_TARGET_DECLARATION_SECONDARY_FIXED_SIZE
154
+ target_name_length -= OPENC3_ID_FIXED_SIZE if id
155
+ target_name = entry[2..(target_name_length + 1)]
156
+ if id
157
+ id = entry[(target_name_length + 3)..(target_name_length + 34)]
158
+ @target_ids << id
159
+ end
160
+ @target_names << target_name
161
+ return read(identify_and_define)
162
+ elsif flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_PACKET_DECLARATION_ENTRY_TYPE_MASK
163
+ target_index = entry[2..3].unpack('n')[0]
164
+ target_name = @target_names[target_index]
165
+ packet_name_length = length - OPENC3_PRIMARY_FIXED_SIZE - OPENC3_PACKET_DECLARATION_SECONDARY_FIXED_SIZE
166
+ packet_name_length -= OPENC3_ID_FIXED_SIZE if id
167
+ packet_name = entry[4..(packet_name_length + 3)]
168
+ if id
169
+ id = entry[(packet_name_length + 4)..-1]
170
+ @packet_ids << id
171
+ end
172
+ @packets << [cmd_or_tlm, target_name, packet_name, id]
173
+ return read(identify_and_define)
174
+ elsif flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_OFFSET_MARKER_ENTRY_TYPE_MASK
175
+ @redis_offset = entry[2..-1]
176
+ return read(identify_and_define)
177
+ else
178
+ raise "Invalid Entry Flags: #{flags}"
179
+ end
180
+ rescue => err
181
+ close()
182
+ raise err
183
+ end
184
+
185
+ # TODO: Currently not used
186
+ # Returns an analysis of the log file by reading all the packets and
187
+ # returning information about each packet. This information maps directly
188
+ # to the parameters need by the {#read_at_offset} method and thus should be
189
+ # called before using {#read_at_offset}.
190
+ #
191
+ # @param filename [String] The filename to analyze
192
+ # @param progress_callback [Proc] Callback that should receive a single
193
+ # floating point parameter which is the percentage done
194
+ # @return [Array<Array<Integer, Integer, String, String, Time, Time>] Array
195
+ # of arrays for each packet found in the log file consisting of:
196
+ # [File position, length, target name, packet name, time formatted,
197
+ # received time].
198
+ # def packet_offsets(filename, progress_callback = nil)
199
+ # open(filename)
200
+ # offsets = []
201
+ # filesize = size().to_f
202
+
203
+ # while true
204
+ # current_pos = @file.pos
205
+ # packet = read(false)
206
+ # break unless packet
207
+ # offsets << current_pos
208
+ # if progress_callback
209
+ # break if progress_callback.call(current_pos / filesize)
210
+ # end
211
+ # end
212
+
213
+ # return offsets
214
+ # ensure
215
+ # close()
216
+ # end
217
+
218
+ # TODO: Currently not used
219
+ # Reads a packet from the opened log file. Should only be used in
220
+ # conjunction with {#packet_offsets}.
221
+ #
222
+ # @param file_offset [Integer] Byte offset into the log file to start
223
+ # reading
224
+ # @param identify_and_define (see #each)
225
+ # @return [Packet]
226
+ # def read_at_offset(file_offset, identify_and_define = true)
227
+ # @file.seek(file_offset, IO::SEEK_SET)
228
+ # return read(identify_and_define)
229
+ # rescue => err
230
+ # close()
231
+ # raise err
232
+ # end
233
+
234
+ # TODO: Currently not used
235
+ # Read the first packet from the log file and reset the file position back
236
+ # to the current position. This allows the client to call read multiple
237
+ # times to return packets, call first, and continue calling read which will
238
+ # return the next packet in the file.
239
+ #
240
+ # @return [Packet]
241
+ # def first
242
+ # original_position = @file.pos
243
+ # @file.seek(0, IO::SEEK_SET)
244
+ # read_file_header()
245
+ # packet = read()
246
+ # raise "No first packet found" unless packet
247
+ # @file.seek(original_position, IO::SEEK_SET)
248
+ # packet.clone
249
+ # rescue => err
250
+ # close()
251
+ # raise err
252
+ # end
253
+
254
+ # TODO: Currently not used
255
+ # Read the last packet from the log file and reset the file position back
256
+ # to the current position. This allows the client to call read multiple
257
+ # times to return packets, call last, and continue calling read which will
258
+ # return the next packet in the file.
259
+ #
260
+ # @return [Packet]
261
+ # def last
262
+ # raise "TODO: Implement me - Need to add end of file entry to support"
263
+ # original_position = @file.pos
264
+ # @file.seek(-1, IO::SEEK_END)
265
+ # packet = search(-1)
266
+ # raise "No last packet found" unless packet
267
+ # @file.seek(original_position, IO::SEEK_SET)
268
+ # packet.clone
269
+ # rescue => err
270
+ # close()
271
+ # raise err
272
+ # end
273
+
274
+ # @return [Integer] The size of the log file being processed
275
+ def size
276
+ @file.stat.size
277
+ end
278
+
279
+ # @return [Integer] The current file position in the log file
280
+ def bytes_read
281
+ @file.pos
282
+ end
283
+
284
+ protected
285
+
286
+ def reset
287
+ @file = nil
288
+ @filename = nil
289
+ @max_read_size = MAX_READ_SIZE
290
+ @target_names = []
291
+ @target_ids = []
292
+ @packets = []
293
+ @packet_ids = []
294
+ @redis_offset = nil
295
+ end
296
+
297
+ # This is best effort. May return unidentified/undefined packets
298
+ def identify_and_define_packet_data(cmd_or_tlm, target_name, packet_name, received_time, packet_data)
299
+ packet = nil
300
+ unless target_name and packet_name
301
+ if cmd_or_tlm == :CMD
302
+ packet = System.commands.identify(packet_data)
303
+ else
304
+ packet = System.telemetry.identify!(packet_data)
305
+ end
306
+ else
307
+ begin
308
+ if cmd_or_tlm == :CMD
309
+ packet = System.commands.packet(target_name, packet_name)
310
+ else
311
+ packet = System.telemetry.packet(target_name, packet_name)
312
+ end
313
+ packet.buffer = packet_data
314
+ rescue
315
+ # Could not find a definition for this packet
316
+ Logger.instance.error "Unknown packet #{target_name} #{packet_name}"
317
+ packet = Packet.new(target_name, packet_name, :BIG_ENDIAN, nil, packet_data)
318
+ end
319
+ end
320
+ packet
321
+ end
322
+
323
+ # Should return if successfully switched to requested configuration
324
+ def read_file_header
325
+ header = @file.read(OPENC3_HEADER_LENGTH)
326
+ if header and header.length == OPENC3_HEADER_LENGTH
327
+ if header == OPENC3_FILE_HEADER
328
+ # Found OpenC3 5 File Header - That's all we need to do
329
+ elsif header == COSMOS4_FILE_HEADER
330
+ raise "COSMOS 4 log file must be converted to OpenC3 5"
331
+ elsif header == COSMOS2_FILE_HEADER
332
+ raise "COSMOS 2 log file must be converted to OpenC3 5"
333
+ else
334
+ raise "OpenC3 file header not found"
335
+ end
336
+ else
337
+ raise "Failed to read at least #{OPENC3_HEADER_LENGTH} bytes from packet log"
338
+ end
339
+ end
340
+
341
+ def seek_to_time(time)
342
+ raise "TODO: Implement me - Use index file or offsets"
343
+ end
344
+ end
345
+ end
@@ -0,0 +1,299 @@
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
+ require 'openc3/logs/log_writer'
21
+ require 'openc3/logs/packet_log_constants'
22
+
23
+ module OpenC3
24
+ # Creates a packet log. Can automatically cycle the log based on an elasped
25
+ # time period or when the log file reaches a predefined size.
26
+ class PacketLogWriter < LogWriter
27
+ include PacketLogConstants
28
+
29
+ # @param remote_log_directory [String] The s3 path to store the log files
30
+ # @param label [String] Label to apply to the log filename
31
+ # @param logging_enabled [Boolean] Whether to start with logging enabled
32
+ # @param cycle_time [Integer] The amount of time in seconds before creating
33
+ # a new log file. This can be combined with cycle_size but is better used
34
+ # independently.
35
+ # @param cycle_size [Integer] The size in bytes before creating a new log
36
+ # file. This can be combined with cycle_time but is better used
37
+ # independently.
38
+ # @param cycle_hour [Integer] The time at which to cycle the log. Combined with
39
+ # cycle_minute to cycle the log daily at the specified time. If nil, the log
40
+ # will be cycled hourly at the specified cycle_minute.
41
+ # @param cycle_minute [Integer] The time at which to cycle the log. See cycle_hour
42
+ # for more information.
43
+ # @param redis_topic [String] The key of the Redis stream to trim when files are
44
+ # moved to S3
45
+ def initialize(
46
+ remote_log_directory,
47
+ label,
48
+ logging_enabled = true,
49
+ cycle_time = nil,
50
+ cycle_size = 1_000_000_000,
51
+ cycle_hour = nil,
52
+ cycle_minute = nil,
53
+ redis_topic: nil
54
+ )
55
+ super(
56
+ remote_log_directory,
57
+ logging_enabled,
58
+ cycle_time,
59
+ cycle_size,
60
+ cycle_hour,
61
+ cycle_minute,
62
+ redis_topic: redis_topic
63
+ )
64
+ @label = label
65
+ @index_file = nil
66
+ @index_filename = nil
67
+ @cmd_packet_table = {}
68
+ @tlm_packet_table = {}
69
+ @target_dec_entries = []
70
+ @packet_dec_entries = []
71
+ @next_packet_index = 0
72
+ @target_indexes = {}
73
+ @next_target_index = 0
74
+
75
+ # This is an optimization to avoid creating a new entry object
76
+ # each time we create an entry which we do a LOT!
77
+ @index_entry = String.new
78
+ end
79
+
80
+ # Write a packet to the log file.
81
+ #
82
+ # If no log file currently exists in the filesystem, a new file will be
83
+ # created.
84
+ #
85
+ # @param entry_type [Symbol] Type of entry to write. Must be one of
86
+ # :TARGET_DECLARATION, :PACKET_DECLARATION, :RAW_PACKET, :JSON_PACKET
87
+ # @param cmd_or_tlm [Symbol] One of :CMD or :TLM
88
+ # @param target_name [String] Name of the target
89
+ # @param packet_name [String] Name of the packet
90
+ # @param time_nsec_since_epoch [Integer] 64 bit integer nsecs since EPOCH
91
+ # @param stored [Boolean] Whether this data is stored telemetry
92
+ # @param data [String] Binary string of data
93
+ # @param id [Integer] Target ID
94
+ # @param redis_offset [Integer] The offset of this packet in its Redis stream
95
+ def write(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id = nil, redis_offset = '0-0')
96
+ return if !@logging_enabled
97
+
98
+ @mutex.synchronize do
99
+ prepare_write(time_nsec_since_epoch, data.length, redis_offset)
100
+ write_entry(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id) if @file
101
+ end
102
+ rescue => err
103
+ Logger.instance.error "Error writing #{@filename} : #{err.formatted}"
104
+ OpenC3.handle_critical_exception(err)
105
+ end
106
+
107
+ # Starting a new index file is a critical operation so the entire method is
108
+ # wrapped with a rescue and handled with handle_critical_exception
109
+ # Assumes mutex has already been taken
110
+ def start_new_file
111
+ super
112
+ @file.write(OPENC3_FILE_HEADER)
113
+ @file_size += OPENC3_FILE_HEADER.length
114
+
115
+ # Start index log file
116
+ @index_filename = create_unique_filename('.idx'.freeze)
117
+ @index_file = File.new(@index_filename, 'wb')
118
+ @index_file.write(OPENC3_INDEX_HEADER)
119
+
120
+ @cmd_packet_table = {}
121
+ @tlm_packet_table = {}
122
+ @next_packet_index = 0
123
+ @target_indexes = {}
124
+ @target_dec_entries = []
125
+ @packet_dec_entries = []
126
+ Logger.debug "Index Log File Opened : #{@index_filename}"
127
+ rescue => err
128
+ Logger.error "Error starting new log file: #{err.formatted}"
129
+ @logging_enabled = false
130
+ OpenC3.handle_critical_exception(err)
131
+ end
132
+
133
+ # Closing a log file isn't critical so we just log an error
134
+ def close_file(take_mutex = true)
135
+ write_entry(:OFFSET_MARKER, nil, nil, nil, nil, nil, nil, nil) if @file
136
+ super
137
+
138
+ @mutex.lock if take_mutex
139
+ begin
140
+ if @index_file
141
+ begin
142
+ write_index_file_footer()
143
+ @index_file.close unless @index_file.closed?
144
+ Logger.debug "Index Log File Closed : #{@index_filename}"
145
+ date = first_timestamp[0..7] # YYYYMMDD
146
+ s3_key = File.join(@remote_log_directory, date, "#{first_timestamp}__#{last_timestamp}__#{@label}.idx")
147
+ S3Utilities.move_log_file_to_s3(@index_filename, s3_key)
148
+ rescue Exception => err
149
+ Logger.instance.error "Error closing #{@index_filename} : #{err.formatted}"
150
+ end
151
+
152
+ @index_file = nil
153
+ @index_filename = nil
154
+ end
155
+ ensure
156
+ @mutex.unlock if take_mutex
157
+ end
158
+ end
159
+
160
+ def get_packet_index(cmd_or_tlm, target_name, packet_name)
161
+ if cmd_or_tlm == :CMD
162
+ target_table = @cmd_packet_table[target_name]
163
+ else
164
+ target_table = @tlm_packet_table[target_name]
165
+ end
166
+ if target_table
167
+ packet_index = target_table[packet_name]
168
+ return packet_index if packet_index
169
+ else
170
+ # New packet_table entry needed
171
+ target_table = {}
172
+ if cmd_or_tlm == :CMD
173
+ @cmd_packet_table[target_name] = target_table
174
+ else
175
+ @tlm_packet_table[target_name] = target_table
176
+ end
177
+ id = nil
178
+ target = System.targets[target_name]
179
+ id = target.id if target
180
+ write_entry(:TARGET_DECLARATION, cmd_or_tlm, target_name, packet_name, nil, nil, nil, id)
181
+ end
182
+
183
+ # New target_table entry needed
184
+ packet_index = @next_packet_index
185
+ if packet_index > OPENC3_MAX_PACKET_INDEX
186
+ raise "Packet Index Overflow"
187
+ end
188
+
189
+ target_table[packet_name] = packet_index
190
+ @next_packet_index += 1
191
+
192
+ id = nil
193
+ begin
194
+ if cmd_or_tlm == :CMD
195
+ id = System.commands.packet(target_nam, packet_name).config_name
196
+ else
197
+ id = System.telemetry.packet(target_name, packet_name).config_name
198
+ end
199
+ rescue
200
+ # No packet def
201
+ end
202
+ write_entry(:PACKET_DECLARATION, cmd_or_tlm, target_name, packet_name, nil, nil, nil, id)
203
+ return packet_index
204
+ end
205
+
206
+ def write_entry(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id)
207
+ raise ArgumentError.new("Length of id must be 64, got #{id.length}") if id and id.length != 64 # 64 hex digits, gets packed to 32 bytes with .pack('H*')
208
+
209
+ length = OPENC3_PRIMARY_FIXED_SIZE
210
+ flags = 0
211
+ flags |= OPENC3_STORED_FLAG_MASK if stored
212
+ flags |= OPENC3_ID_FLAG_MASK if id
213
+ case entry_type
214
+ when :TARGET_DECLARATION
215
+ target_index = @next_target_index
216
+ @target_indexes[target_name] = target_index
217
+ @next_target_index += 1
218
+ if target_index > OPENC3_MAX_TARGET_INDEX
219
+ raise "Target Index Overflow"
220
+ end
221
+
222
+ flags |= OPENC3_TARGET_DECLARATION_ENTRY_TYPE_MASK
223
+ length += OPENC3_TARGET_DECLARATION_SECONDARY_FIXED_SIZE + target_name.length
224
+ length += OPENC3_ID_FIXED_SIZE if id
225
+ @entry.clear
226
+ @entry << [length, flags].pack(OPENC3_TARGET_DECLARATION_PACK_DIRECTIVE) << target_name
227
+ @entry << [id].pack('H*') if id
228
+ @target_dec_entries << @entry.dup
229
+ when :PACKET_DECLARATION
230
+ target_index = @target_indexes[target_name]
231
+ flags |= OPENC3_PACKET_DECLARATION_ENTRY_TYPE_MASK
232
+ if cmd_or_tlm == :CMD
233
+ flags |= OPENC3_CMD_FLAG_MASK
234
+ end
235
+ length += OPENC3_PACKET_DECLARATION_SECONDARY_FIXED_SIZE + packet_name.length
236
+ length += OPENC3_ID_FIXED_SIZE if id
237
+ @entry.clear
238
+ @entry << [length, flags, target_index].pack(OPENC3_PACKET_DECLARATION_PACK_DIRECTIVE) << packet_name
239
+ @entry << [id].pack('H*') if id
240
+ @packet_dec_entries << @entry.dup
241
+ when :OFFSET_MARKER
242
+ flags |= OPENC3_OFFSET_MARKER_ENTRY_TYPE_MASK
243
+ length += OPENC3_OFFSET_MARKER_SECONDARY_FIXED_SIZE + @last_offset.length
244
+ @entry.clear
245
+ @entry << [length, flags].pack(OPENC3_OFFSET_MARKER_PACK_DIRECTIVE) << @last_offset
246
+ when :RAW_PACKET, :JSON_PACKET
247
+ target_name = 'UNKNOWN'.freeze unless target_name
248
+ packet_name = 'UNKNOWN'.freeze unless packet_name
249
+ packet_index = get_packet_index(cmd_or_tlm, target_name, packet_name)
250
+ if entry_type == :RAW_PACKET
251
+ flags |= OPENC3_RAW_PACKET_ENTRY_TYPE_MASK
252
+ else
253
+ flags |= OPENC3_JSON_PACKET_ENTRY_TYPE_MASK
254
+ end
255
+ if cmd_or_tlm == :CMD
256
+ flags |= OPENC3_CMD_FLAG_MASK
257
+ end
258
+ length += OPENC3_PACKET_SECONDARY_FIXED_SIZE + data.length
259
+ @entry.clear
260
+ @index_entry.clear
261
+ @index_entry << [length, flags, packet_index, time_nsec_since_epoch].pack(OPENC3_PACKET_PACK_DIRECTIVE)
262
+ @entry << @index_entry << data
263
+ @index_entry << [@file_size].pack('Q>')
264
+ @index_file.write(@index_entry)
265
+ @first_time = time_nsec_since_epoch if !@first_time or time_nsec_since_epoch < @first_time
266
+ @last_time = time_nsec_since_epoch if !@last_time or time_nsec_since_epoch > @last_time
267
+ else
268
+ raise "Unknown entry_type: #{entry_type}"
269
+ end
270
+ @file.write(@entry)
271
+ @file_size += @entry.length
272
+ end
273
+
274
+ def write_index_file_footer
275
+ footer_length = 4 # Includes length of length field at end
276
+ @index_file.write([@target_dec_entries.length].pack('n'))
277
+ footer_length += 2
278
+ @target_dec_entries.each do |target_dec_entry|
279
+ @index_file.write(target_dec_entry)
280
+ footer_length += target_dec_entry.length
281
+ end
282
+ @index_file.write([@packet_dec_entries.length].pack('n'))
283
+ footer_length += 2
284
+ @packet_dec_entries.each do |packet_dec_entry|
285
+ @index_file.write(packet_dec_entry)
286
+ footer_length += packet_dec_entry.length
287
+ end
288
+ @index_file.write([footer_length].pack('N'))
289
+ end
290
+
291
+ def s3_filename
292
+ "#{first_timestamp}__#{last_timestamp}__#{@label}" + extension
293
+ end
294
+
295
+ def extension
296
+ '.bin'.freeze
297
+ end
298
+ end
299
+ end