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
@@ -0,0 +1,199 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 OpenC3, Inc.
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
+ # This file may also be used under the terms of a commercial license
17
+ # if purchased from OpenC3, Inc.
18
+
19
+ require 'openc3/utilities/bucket'
20
+ module Aws
21
+ autoload(:S3, 'openc3/utilities/s3_autoload.rb')
22
+ end
23
+
24
+ module OpenC3
25
+ class AwsBucket < Bucket
26
+ CREATE_CHECK_COUNT = 100 # 10 seconds
27
+
28
+ def initialize
29
+ @client = Aws::S3::Client.new
30
+ end
31
+
32
+ def create(bucket)
33
+ unless exist?(bucket)
34
+ @client.create_bucket({ bucket: bucket })
35
+ count = 0
36
+ until exist?(bucket) or count > CREATE_CHECK_COUNT
37
+ sleep(0.1)
38
+ count += 1
39
+ end
40
+ end
41
+ bucket
42
+ end
43
+
44
+ def ensure_public(bucket)
45
+ policy = <<~EOL
46
+ {
47
+ "Version": "2012-10-17",
48
+ "Statement": [
49
+ {
50
+ "Action": [
51
+ "s3:GetBucketLocation",
52
+ "s3:ListBucket"
53
+ ],
54
+ "Effect": "Allow",
55
+ "Principal": {
56
+ "AWS": [
57
+ "*"
58
+ ]
59
+ },
60
+ "Resource": [
61
+ "arn:aws:s3:::#{bucket}"
62
+ ],
63
+ "Sid": ""
64
+ },
65
+ {
66
+ "Action": [
67
+ "s3:GetObject"
68
+ ],
69
+ "Effect": "Allow",
70
+ "Principal": {
71
+ "AWS": [
72
+ "*"
73
+ ]
74
+ },
75
+ "Resource": [
76
+ "arn:aws:s3:::#{bucket}/*"
77
+ ],
78
+ "Sid": ""
79
+ }
80
+ ]
81
+ }
82
+ EOL
83
+ @client.put_bucket_policy({ bucket: bucket, policy: policy })
84
+ end
85
+
86
+ def exist?(bucket)
87
+ @client.head_bucket({ bucket: bucket })
88
+ true
89
+ rescue Aws::S3::Errors::NotFound
90
+ false
91
+ end
92
+
93
+ def delete(bucket)
94
+ if exist?(bucket)
95
+ @client.delete_bucket({ bucket: bucket })
96
+ end
97
+ end
98
+
99
+ def get_object(bucket:, key:, path: nil)
100
+ if path
101
+ @client.get_object(bucket: bucket, key: key, response_target: path)
102
+ else
103
+ @client.get_object(bucket: bucket, key: key)
104
+ end
105
+ # If the key is not found return nil
106
+ rescue Aws::S3::Errors::NoSuchKey
107
+ nil
108
+ end
109
+
110
+ def list_objects(bucket:, prefix: nil)
111
+ token = nil
112
+ result = []
113
+ while true
114
+ resp = @client.list_objects_v2(bucket: bucket, prefix: prefix, max_keys: 1000)
115
+ result.concat(resp.contents)
116
+ break unless resp.is_truncated
117
+ token = resp.next_continuation_token
118
+ end
119
+ # Array of objects with key and size methods
120
+ result
121
+ end
122
+
123
+ # Lists the directories under a specified path
124
+ def list_directories(bucket:, path:)
125
+ # Trailing slash is important in AWS S3 when listing files
126
+ # See https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Types/ListObjectsV2Output.html#common_prefixes-instance_method
127
+ if path[-1] != '/'
128
+ path += '/'
129
+ end
130
+ token = nil
131
+ result = []
132
+ while true
133
+ resp = @client.list_objects_v2({
134
+ bucket: bucket,
135
+ max_keys: 1000,
136
+ prefix: path,
137
+ delimiter: '/',
138
+ continuation_token: token
139
+ })
140
+ resp.common_prefixes.each do |item|
141
+ # If path was DEFAULT/targets_modified/ then the
142
+ # results look like DEFAULT/targets_modified/INST/
143
+ result << item.prefix.split('/')[-1]
144
+ end
145
+ break unless resp.is_truncated
146
+ token = resp.next_continuation_token
147
+ end
148
+ result
149
+ end
150
+
151
+ # put_object fires off the request to store but does not confirm
152
+ def put_object(bucket:, key:, body:, content_type: nil, cache_control: nil, metadata: nil)
153
+ @client.put_object(bucket: bucket, key: key, body: body,
154
+ content_type: content_type, cache_control: cache_control, metadata: metadata)
155
+ end
156
+
157
+ # @returns [Boolean] Whether the file exists
158
+ def check_object(bucket:, key:)
159
+ @client.wait_until(:object_exists,
160
+ {
161
+ bucket: bucket,
162
+ key: key
163
+ },
164
+ {
165
+ max_attempts: 30,
166
+ delay: 0.1, # seconds
167
+ }
168
+ )
169
+ true
170
+ rescue Aws::Waiters::Errors::TooManyAttemptsError
171
+ false
172
+ end
173
+
174
+ def delete_object(bucket:, key:)
175
+ @client.delete_object(bucket: bucket, key: key)
176
+ end
177
+
178
+ def delete_objects(bucket:, keys:)
179
+ @client.delete_objects(bucket: bucket, delete: { objects: keys.map {|key| { key: key } } })
180
+ end
181
+
182
+ def presigned_request(bucket:, key:, method:, internal: true)
183
+ s3_presigner = Aws::S3::Presigner.new
184
+
185
+ if internal
186
+ prefix = '/'
187
+ else
188
+ prefix = '/files/'
189
+ end
190
+
191
+ url, headers = s3_presigner.presigned_request(method, bucket: bucket, key: key)
192
+ return {
193
+ :url => prefix + url.split('/')[3..-1].join('/'),
194
+ :headers => headers,
195
+ :method => method.to_s.split('_')[0],
196
+ }
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,82 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 OpenC3, Inc.
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
+ # This file may also be used under the terms of a commercial license
17
+ # if purchased from OpenC3, Inc.
18
+
19
+ # Interface class implemented by each cloud provider: AWS, GCS, Azure
20
+ module OpenC3
21
+ class Bucket
22
+ def self.getClient
23
+ raise 'OPENC3_CLOUD environment variable is required' unless ENV['OPENC3_CLOUD']
24
+ # Base is AwsBucket which works with MINIO, Enterprise implements additional
25
+ bucket_class = ENV['OPENC3_CLOUD'].capitalize + 'Bucket'
26
+ klass = OpenC3.require_class('openc3/utilities/'+bucket_class.class_name_to_filename)
27
+ klass.new
28
+ end
29
+
30
+ def initialize
31
+ # Setup the client instance
32
+ end
33
+
34
+ def create(bucket)
35
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
36
+ end
37
+
38
+ def ensure_public(bucket)
39
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
40
+ end
41
+
42
+ def exist?(bucket)
43
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
44
+ end
45
+
46
+ def delete(bucket)
47
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
48
+ end
49
+
50
+ def get_object(bucket:, key:, path: nil)
51
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
52
+ end
53
+
54
+ def list_objects(bucket:, prefix: nil)
55
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
56
+ end
57
+
58
+ def list_directories(bucket:, path:)
59
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
60
+ end
61
+
62
+ def put_object(bucket:, key:, body:, content_type: nil, cache_control: nil, metadata: nil)
63
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
64
+ end
65
+
66
+ def check_object(bucket:, key:)
67
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
68
+ end
69
+
70
+ def delete_object(bucket:, key:)
71
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
72
+ end
73
+
74
+ def delete_objects(bucket:, keys:)
75
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
76
+ end
77
+
78
+ def presigned_request(bucket:, key:, method:, internal: true)
79
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,264 @@
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 'fileutils'
24
+ require 'tmpdir'
25
+ require 'openc3'
26
+ require 'openc3/utilities/bucket_utilities'
27
+ require 'openc3/utilities/bucket'
28
+
29
+ class BucketFile
30
+ MAX_AGE_SECONDS = 3600 * 4 # 4 hours
31
+
32
+ attr_reader :bucket_path
33
+ attr_reader :local_path
34
+ attr_reader :reservation_count
35
+ attr_reader :size
36
+ attr_reader :error
37
+ attr_reader :topic_prefix
38
+ attr_reader :init_time
39
+ attr_accessor :priority
40
+
41
+ def initialize(bucket_path, client = nil)
42
+ @bucket = client
43
+ unless @bucket
44
+ @bucket = OpenC3::Bucket.getClient()
45
+ end
46
+ @bucket_path = bucket_path
47
+ @local_path = nil
48
+ @reservation_count = 0
49
+ @size = 0
50
+ @priority = 0
51
+ @error = nil
52
+ @init_time = Time.now
53
+ @mutex = Mutex.new
54
+ path_split = @bucket_path.split("/")
55
+ scope = path_split[0].to_s.upcase
56
+ stream_mode = path_split[1].to_s.split("_")[0].to_s.upcase
57
+ if stream_mode == 'REDUCED'
58
+ stream_mode << '_' << path_split[1].to_s.split("_")[1].to_s.upcase
59
+ end
60
+ cmd_or_tlm = path_split[2].to_s.upcase
61
+ target_name = path_split[3].to_s.upcase
62
+ if stream_mode == 'RAW'
63
+ type = (@cmd_or_tlm == 'CMD') ? 'COMMAND' : 'TELEMETRY'
64
+ else
65
+ if stream_mode == 'DECOM'
66
+ type = (@cmd_or_tlm == 'CMD') ? 'DECOMCMD' : 'DECOM'
67
+ else
68
+ type = stream_mode # REDUCED_MINUTE, REDUCED_HOUR, or REDUCED_DAY
69
+ end
70
+ end
71
+ @topic_prefix = "#{scope}__#{type}__{#{target_name}}"
72
+ end
73
+
74
+ def retrieve(client = @bucket, uncompress = true)
75
+ @mutex.synchronize do
76
+ local_path = "#{BucketFileCache.instance.cache_dir}/#{File.basename(@bucket_path)}"
77
+ unless File.exist?(local_path)
78
+ OpenC3::Logger.debug "Retrieving #{@bucket_path} from logs bucket"
79
+ client.get_object(bucket: "logs", key: @bucket_path, path: local_path)
80
+ if File.exist?(local_path)
81
+ basename = File.basename(local_path)
82
+ if uncompress and File.extname(basename) == ".gz"
83
+ uncompressed = OpenC3::BucketUtilities.uncompress_file(local_path)
84
+ File.delete(local_path)
85
+ local_path = uncompressed
86
+ end
87
+ @size = File.size(local_path)
88
+ @local_path = local_path
89
+ return true
90
+ end
91
+ end
92
+ return false
93
+ end
94
+ rescue => err
95
+ @error = err
96
+ OpenC3::Logger.error "Failed to retrieve #{@bucket_path}\n#{err.formatted}"
97
+ raise err
98
+ end
99
+
100
+ def reserve
101
+ @mutex.synchronize { @reservation_count += 1 }
102
+ return retrieve()
103
+ end
104
+
105
+ def unreserve
106
+ @mutex.synchronize do
107
+ @reservation_count -= 1
108
+ delete() if @reservation_count <= 0
109
+ return @reservation_count
110
+ end
111
+ end
112
+
113
+ def age_check
114
+ @mutex.synchronize do
115
+ if (Time.now - @init_time) > MAX_AGE_SECONDS and @reservation_count <= 0
116
+ delete()
117
+ return true
118
+ else
119
+ return false
120
+ end
121
+ end
122
+ end
123
+
124
+ # private
125
+
126
+ def delete
127
+ if @local_path and File.exist?(@local_path)
128
+ File.delete(@local_path)
129
+ @local_path = nil
130
+ end
131
+ end
132
+ end
133
+
134
+ class BucketFileCache
135
+ MAX_DISK_USAGE = (ENV['OPENC3_BUCKET_FILE_CACHE_SIZE'] || 20_000_000_000).to_i # Default 20 GB
136
+ CHECK_TIME_SECONDS = 3600
137
+
138
+ attr_reader :cache_dir
139
+
140
+ @@instance = nil
141
+ @@mutex = Mutex.new
142
+
143
+ def self.instance
144
+ return @@instance if @@instance
145
+ @@mutex.synchronize do
146
+ @@instance ||= BucketFileCache.new
147
+ end
148
+ @@instance
149
+ end
150
+
151
+ def initialize
152
+ @bucket = OpenC3::Bucket.getClient()
153
+
154
+ # Create local file cache location
155
+ @cache_dir = Dir.mktmpdir
156
+ FileUtils.mkdir_p(@cache_dir)
157
+ at_exit do
158
+ FileUtils.remove_dir(@cache_dir, true)
159
+ end
160
+
161
+ @current_disk_usage = 0
162
+ @queued_bucket_files = []
163
+ @bucket_file_hash = {}
164
+
165
+ @thread = Thread.new do
166
+ client = OpenC3::Bucket.getClient()
167
+ while true
168
+ check_time = Time.now + CHECK_TIME_SECONDS # Check for aged out files periodically
169
+ if @queued_bucket_files.length > 0 and @current_disk_usage < MAX_DISK_USAGE
170
+ @@mutex.synchronize do
171
+ bucket_file = @queued_bucket_files.shift
172
+ end
173
+ begin
174
+ retrieved = bucket_file.retrieve(client)
175
+ @@mutex.synchronize do
176
+ @current_disk_usage += bucket_file.size if retrieved
177
+ end
178
+ rescue
179
+ # Might have been deleted
180
+ end
181
+ sleep(0.01) # Small throttle
182
+ else
183
+ # Nothing to do or disk full
184
+ if Time.now > check_time
185
+ check_time = Time.now + CHECK_TIME_SECONDS
186
+ # Delete any files that aren't reserved and are old
187
+ removed_files = []
188
+ @bucket_file_hash.each do |bucket_path, bucket_file|
189
+ deleted = bucket_file.age_check
190
+ if deleted
191
+ removed_files << bucket_path
192
+ @current_disk_usage -= bucket_file.size
193
+ end
194
+ end
195
+ removed_files.each do |bucket_path|
196
+ @bucket_file_hash.delete(bucket_path)
197
+ end
198
+ end
199
+ sleep(1)
200
+ end
201
+ end
202
+ rescue => err
203
+ OpenC3::Logger.error "BucketFileCache thread unexpectedly died\n#{err.formatted}"
204
+ end
205
+ end
206
+
207
+ def self.hint(bucket_paths)
208
+ return instance().hint(bucket_paths)
209
+ end
210
+
211
+ def self.reserve(bucket_path)
212
+ return instance().reserve(bucket_path)
213
+ end
214
+
215
+ def self.unreserve(bucket_path)
216
+ return instance().unreserve(bucket_path)
217
+ end
218
+
219
+ def hint(bucket_paths)
220
+ @@mutex.synchronize do
221
+ bucket_paths.each_with_index do |bucket_path, index|
222
+ bucket_file = create_bucket_file(bucket_path)
223
+ bucket_file.priority = index
224
+ end
225
+ @queued_bucket_files.sort! {|file1, file2| file1.priority <=> file2.priority}
226
+ end
227
+ end
228
+
229
+ def reserve(bucket_path)
230
+ @@mutex.synchronize do
231
+ bucket_file = create_bucket_file(bucket_path)
232
+ retrieved = bucket_file.reserve
233
+ @current_disk_usage += bucket_file.size if retrieved
234
+ @queued_bucket_files.delete(bucket_file)
235
+ return bucket_file
236
+ end
237
+ end
238
+
239
+ def unreserve(bucket_path)
240
+ @@mutex.synchronize do
241
+ bucket_file = @bucket_file_hash[bucket_path]
242
+ if bucket_file
243
+ bucket_file.unreserve
244
+ if bucket_file.reservation_count <= 0 and !@queued_bucket_files.include?(bucket_file)
245
+ @current_disk_usage -= bucket_file.size
246
+ @bucket_file_hash.delete(bucket_file)
247
+ end
248
+ end
249
+ end
250
+ end
251
+
252
+ # Private
253
+
254
+ def create_bucket_file(bucket_path)
255
+ bucket_file = @bucket_file_hash[bucket_path]
256
+ unless bucket_file
257
+ bucket_file = BucketFile.new(bucket_path)
258
+ @queued_bucket_files << bucket_file
259
+ @bucket_file_hash[bucket_path] = bucket_file
260
+ end
261
+ return bucket_file
262
+ end
263
+
264
+ end
@@ -0,0 +1,109 @@
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/utilities/bucket'
24
+ require 'openc3/models/reducer_model'
25
+ require 'zlib'
26
+
27
+ module OpenC3
28
+ class BucketUtilities
29
+ def self.list_files_before_time(bucket, prefix, time)
30
+ client = Bucket.getClient
31
+ oldest_list = []
32
+
33
+ # Return nothing if bucket doesn't exist (it won't at the very beginning)
34
+ unless client.exist?(bucket)
35
+ return oldest_list
36
+ end
37
+
38
+ next_folder = false
39
+ resp = client.list_objects(bucket: bucket, prefix: prefix)
40
+ resp.each do |item|
41
+ t = File.basename(item.key).split('__')[1]
42
+ file_end_time = Time.utc(t[0..3], t[4..5], t[6..7], t[8..9], t[10..11], t[12..13])
43
+ if file_end_time < time
44
+ oldest_list << item.key
45
+ else
46
+ break
47
+ end
48
+ end
49
+ return oldest_list
50
+ end
51
+
52
+ def self.move_log_file_to_bucket(filename, bucket_key, metadata: {})
53
+ Thread.new do
54
+ client = Bucket.getClient
55
+
56
+ zipped = compress_file(filename)
57
+ bucket_key = bucket_key + '.gz'
58
+ File.open(zipped, 'rb') do |read_file|
59
+ client.put_object(bucket: ENV['OPENC3_LOGS_BUCKET'], key: bucket_key, body: read_file, metadata: metadata)
60
+ end
61
+ Logger.debug "wrote #{ENV['OPENC3_LOGS_BUCKET']}/#{bucket_key}"
62
+ ReducerModel.add_file(bucket_key) # Record the new file for data reduction
63
+
64
+ File.delete(zipped)
65
+ File.delete(filename)
66
+ rescue => err
67
+ Logger.error("Error saving log file to bucket: #{filename}\n#{err.formatted}")
68
+ end
69
+ end
70
+
71
+ def self.get_cache_control(filename)
72
+ # Allow caching for files that have a filename versioning strategy
73
+ has_version_number = /(-|_|\.)\d+(-|_|\.)\d+(-|_|\.)\d+\./.match(filename)
74
+ has_content_hash = /\.[a-f0-9]{20}\./.match(filename)
75
+ return nil if has_version_number or has_content_hash
76
+ return 'no-cache'
77
+ end
78
+
79
+ def self.compress_file(filename, chunk_size = 50_000_000)
80
+ zipped = "#{filename}.gz"
81
+
82
+ Zlib::GzipWriter.open(zipped) do |gz|
83
+ gz.mtime = File.mtime(filename)
84
+ gz.orig_name = filename
85
+ File.open(filename, 'rb') do |file|
86
+ while chunk = file.read(chunk_size) do
87
+ gz.write(chunk)
88
+ end
89
+ end
90
+ end
91
+
92
+ return zipped
93
+ end
94
+
95
+ def self.uncompress_file(filename, chunk_size = 50_000_000)
96
+ unzipped = filename[0..-4] # Drop .gz
97
+
98
+ Zlib::GzipReader.open(filename) do |gz|
99
+ File.open(unzipped, 'wb') do |file|
100
+ while chunk = gz.read(chunk_size)
101
+ file.write(chunk)
102
+ end
103
+ end
104
+ end
105
+
106
+ return unzipped
107
+ end
108
+ end
109
+ end
@@ -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/ext/crc' if RUBY_ENGINE == 'ruby' and !ENV['OPENC3_NO_EXT']
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 'csv'
21
24
 
@@ -0,0 +1,28 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 OpenC3, Inc.
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
+ # This file may also be used under the terms of a commercial license
17
+ # if purchased from OpenC3, Inc.
18
+
19
+ require 'openc3/utilities/aws_bucket'
20
+
21
+ module OpenC3
22
+ # This class exists simply to enable the following code in bucket.rb
23
+ # bucket_class = ENV['OPENC3_CLOUD'].capitalize + 'Bucket'
24
+ # So when the OPENC3_CLOUD var is set to 'local' this file is used
25
+ # The local code uses Minio which is identical to the Aws APIs
26
+ class LocalBucket < AwsBucket
27
+ end
28
+ end