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,1168 @@
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 'digest'
21
+ require 'openc3/packets/structure'
22
+ require 'openc3/packets/packet_item'
23
+ require 'openc3/ext/packet' if RUBY_ENGINE == 'ruby' and !ENV['OPENC3_NO_EXT']
24
+
25
+ module OpenC3
26
+ # Adds features common to all OpenC3 packets of data to the Structure class.
27
+ # This includes the additional attributes listed below. The primary behavior
28
+ # Packet adds is the ability to apply formatting to PacketItem values as well
29
+ # as managing PacketItem's limit states.
30
+ class Packet < Structure
31
+ RESERVED_ITEM_NAMES = ['PACKET_TIMESECONDS'.freeze, 'PACKET_TIMEFORMATTED'.freeze, 'RECEIVED_TIMESECONDS'.freeze, 'RECEIVED_TIMEFORMATTED'.freeze, 'RECEIVED_COUNT'.freeze]
32
+ CATCH_ALL_STATE = 'ANY'
33
+
34
+ # @return [String] Name of the target this packet is associated with
35
+ attr_reader :target_name
36
+
37
+ # @return [String] Name of the packet
38
+ attr_reader :packet_name
39
+
40
+ # @return [String] Description of the packet
41
+ attr_reader :description
42
+
43
+ # @return [Time] Time at which the packet was received
44
+ attr_reader :received_time
45
+
46
+ # @return [Integer] Number of times the packet has been received
47
+ attr_reader :received_count
48
+
49
+ # @return [Boolean] Flag indicating if the packet is hazardous (typically for commands)
50
+ attr_accessor :hazardous
51
+
52
+ # @return [String] Description of why the packet is hazardous
53
+ attr_reader :hazardous_description
54
+
55
+ # Contains the values given by the user for a command (distinguished from defaults)
56
+ # These values should be used within command conversions if present because the order
57
+ # that values are written into the actual packet can vary
58
+ # @return [Hash<Item Name, Value>] Given values when constructing the packet
59
+ attr_reader :given_values
60
+
61
+ # @return [Boolean] Flag indicating if the packet is stale (hasn't been received recently)
62
+ attr_reader :stale
63
+
64
+ # @return [Boolean] Whether or not this is a 'raw' packet
65
+ attr_accessor :raw
66
+
67
+ # @return [Boolean] Whether or not this is a 'hidden' packet
68
+ attr_accessor :hidden
69
+
70
+ # @return [Boolean] Whether or not this is a 'disabled' packet
71
+ attr_accessor :disabled
72
+
73
+ # @return [Boolean] Whether or not messages should be printed for this packet
74
+ attr_accessor :messages_disabled
75
+
76
+ # @return [Boolean] Whether or not this is a 'abstract' packet
77
+ attr_accessor :abstract
78
+
79
+ # @return [Boolean] Whether or not this was a stored packet
80
+ attr_accessor :stored
81
+
82
+ # @return [Hash] Extra data to be logged/transferred with packet
83
+ attr_accessor :extra
84
+
85
+ # @return [Symbol] :CMD or :TLM
86
+ attr_accessor :cmd_or_tlm
87
+
88
+ # Valid format types
89
+ VALUE_TYPES = [:RAW, :CONVERTED, :FORMATTED, :WITH_UNITS]
90
+
91
+ if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
92
+ # Creates a new packet by initalizing the attributes.
93
+ #
94
+ # @param target_name [String] Name of the target this packet is associated with
95
+ # @param packet_name [String] Name of the packet
96
+ # @param default_endianness [Symbol] One of {BinaryAccessor::ENDIANNESS}
97
+ # @param description [String] Description of the packet
98
+ # @param buffer [String] String buffer to hold the packet data
99
+ # @param item_class [Class] Class used to instantiate items (Must be a
100
+ # subclass of PacketItem)
101
+ def initialize(target_name, packet_name, default_endianness = :BIG_ENDIAN, description = nil, buffer = nil, item_class = PacketItem)
102
+ super(default_endianness, buffer, item_class)
103
+ # Explictly call the defined setter methods
104
+ self.target_name = target_name
105
+ self.packet_name = packet_name
106
+ self.description = description
107
+ self.received_time = nil
108
+ self.received_count = 0
109
+ @id_items = nil
110
+ @hazardous = false
111
+ @hazardous_description = nil
112
+ @given_values = nil
113
+ @limits_items = nil
114
+ @processors = nil
115
+ @stale = true
116
+ @limits_change_callback = nil
117
+ @read_conversion_cache = nil
118
+ @raw = nil
119
+ @messages_disabled = false
120
+ @meta = nil
121
+ @hidden = false
122
+ @disabled = false
123
+ @stored = false
124
+ @extra = nil
125
+ @cmd_or_tlm = nil
126
+ end
127
+
128
+ # Sets the target name this packet is associated with. Unidentified packets
129
+ # will have target name set to nil.
130
+ #
131
+ # @param target_name [String] Name of the target this packet is associated with
132
+ def target_name=(target_name)
133
+ if target_name
134
+ if !(String === target_name)
135
+ raise(ArgumentError, "target_name must be a String but is a #{target_name.class}")
136
+ end
137
+
138
+ @target_name = target_name.upcase.freeze
139
+ else
140
+ @target_name = nil
141
+ end
142
+ @target_name
143
+ end
144
+
145
+ # Sets the packet name. Unidentified packets will have packet name set to
146
+ # nil.
147
+ #
148
+ # @param packet_name [String] Name of the packet
149
+ def packet_name=(packet_name)
150
+ if packet_name
151
+ if !(String === packet_name)
152
+ raise(ArgumentError, "packet_name must be a String but is a #{packet_name.class}")
153
+ end
154
+
155
+ @packet_name = packet_name.upcase.freeze
156
+ else
157
+ @packet_name = nil
158
+ end
159
+ @packet_name
160
+ end
161
+
162
+ # Sets the description of the packet
163
+ #
164
+ # @param description [String] Description of the packet
165
+ def description=(description)
166
+ if description
167
+ if !(String === description)
168
+ raise(ArgumentError, "description must be a String but is a #{description.class}")
169
+ end
170
+
171
+ @description = description.to_utf8.freeze
172
+ else
173
+ @description = nil
174
+ end
175
+ @description
176
+ end
177
+
178
+ # Sets the received time of the packet
179
+ #
180
+ # @param received_time [Time] Time this packet was received
181
+ def received_time=(received_time)
182
+ if received_time
183
+ if !(Time === received_time)
184
+ raise(ArgumentError, "received_time must be a Time but is a #{received_time.class}")
185
+ end
186
+
187
+ @received_time = received_time.clone.freeze
188
+ else
189
+ @received_time = nil
190
+ end
191
+ @read_conversion_cache.clear if @read_conversion_cache
192
+ @received_time
193
+ end
194
+
195
+ # Sets the received count of the packet
196
+ #
197
+ # @param received_count [Integer] Number of times this packet has been
198
+ # received
199
+ def received_count=(received_count)
200
+ if !(Integer === received_count)
201
+ raise(ArgumentError, "received_count must be an Integer but is a #{received_count.class}")
202
+ end
203
+
204
+ @received_count = received_count
205
+ @read_conversion_cache.clear if @read_conversion_cache
206
+ @received_count
207
+ end
208
+
209
+ end # if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
210
+
211
+ # Tries to identify if a buffer represents the currently defined packet. It
212
+ # does this by iterating over all the packet items that were created with
213
+ # an ID value and checking whether that ID value is present at the correct
214
+ # location in the buffer.
215
+ #
216
+ # Incorrectly sized buffers will still positively identify if there is
217
+ # enough data to match the ID values. This is to allow incorrectly sized
218
+ # packets to still be processed as well as possible given the incorrectly
219
+ # sized data.
220
+ #
221
+ # @param buffer [String] Raw buffer of binary data
222
+ # @return [Boolean] Whether or not the buffer of data is this packet
223
+ def identify?(buffer)
224
+ return false unless buffer
225
+ return true unless @id_items
226
+
227
+ @id_items.each do |item|
228
+ begin
229
+ value = read_item(item, :RAW, buffer)
230
+ rescue Exception
231
+ value = nil
232
+ end
233
+ return false if item.id_value != value
234
+ end
235
+
236
+ true
237
+ end
238
+
239
+ # Reads the values from a buffer at the position of each id_item defined
240
+ # in the packet.
241
+ #
242
+ # @param buffer [String] Raw buffer of binary data
243
+ # @return [Array] Array of read id values in order
244
+ def read_id_values(buffer)
245
+ return [] unless buffer
246
+ return [] unless @id_items
247
+
248
+ values = []
249
+
250
+ @id_items.each do |item|
251
+ values << read_item(item, :RAW, buffer)
252
+ rescue Exception
253
+ values << nil
254
+ end
255
+
256
+ values
257
+ end
258
+
259
+ # Returns @received_time unless a packet item called PACKET_TIME exists that returns
260
+ # a Ruby Time object that represents a different timestamp for the packet
261
+ def packet_time
262
+ item = @items['PACKET_TIME'.freeze]
263
+ if item
264
+ return read_item(item, :CONVERTED, @buffer)
265
+ else
266
+ return @received_time
267
+ end
268
+ end
269
+
270
+ # Calculates a unique hashing sum that changes if the parts of the packet configuration change that could affect
271
+ # the "shape" of the packet. This value is cached and that packet should not be changed if this method is being used
272
+ def config_name
273
+ return @config_name if @config_name
274
+
275
+ string = "#{@target_name} #{@packet_name}"
276
+ @sorted_items.each do |item|
277
+ string << " ITEM #{item.name} #{item.bit_offset} #{item.bit_size} #{item.data_type} #{item.array_size} #{item.endianness} #{item.overflow} #{item.states} #{item.read_conversion ? item.read_conversion.class : 'NO_CONVERSION'}"
278
+ end
279
+
280
+ # Use the hashing algorithm established by OpenC3::System
281
+ digest = Digest::SHA256.new
282
+ digest << string
283
+ @config_name = digest.hexdigest
284
+ @config_name
285
+ end
286
+
287
+ # (see Structure#buffer=)
288
+ def buffer=(buffer)
289
+ synchronize() do
290
+ begin
291
+ internal_buffer_equals(buffer)
292
+ rescue RuntimeError
293
+ Logger.instance.error "#{@target_name} #{@packet_name} received with actual packet length of #{buffer.length} but defined length of #{@defined_length}"
294
+ end
295
+ @read_conversion_cache.clear if @read_conversion_cache
296
+ process()
297
+ end
298
+ end
299
+
300
+ # Sets the received time of the packet (without cloning)
301
+ #
302
+ # @param received_time [Time] Time this packet was received
303
+ def set_received_time_fast(received_time)
304
+ @received_time = received_time
305
+ @received_time.freeze if @received_time
306
+ if @read_conversion_cache
307
+ synchronize() do
308
+ @read_conversion_cache.clear
309
+ end
310
+ end
311
+ end
312
+
313
+ # Sets the hazardous description of the packet
314
+ #
315
+ # @param hazardous_description [String] Hazardous description of the packet
316
+ def hazardous_description=(hazardous_description)
317
+ if hazardous_description
318
+ raise ArgumentError, "hazardous_description must be a String but is a #{hazardous_description.class}" unless String === hazardous_description
319
+
320
+ @hazardous_description = hazardous_description.to_utf8.freeze
321
+ else
322
+ @hazardous_description = nil
323
+ end
324
+ end
325
+
326
+ # Saves a hash of the values given by a user when constructing a command
327
+ #
328
+ # @param given_values [Hash<Item Name, Value>] Hash of given command parameters
329
+ def given_values=(given_values)
330
+ if given_values
331
+ raise ArgumentError, "given_values must be a Hash but is a #{given_values.class}" unless Hash === given_values
332
+
333
+ @given_values = given_values.clone
334
+ else
335
+ @given_values = nil
336
+ end
337
+ end
338
+
339
+ # Sets the callback object called when a limits state changes
340
+ #
341
+ # @param limits_change_callback [#call] Object must respond to the call
342
+ # method and take the following arguments: packet (Packet), item (PacketItem),
343
+ # old_limits_state (Symbol), item_value (Object), log_change (Boolean). The
344
+ # current item state can be found by querying the item object:
345
+ # item.limits.state.
346
+ def limits_change_callback=(limits_change_callback)
347
+ if limits_change_callback
348
+ raise ArgumentError, "limits_change_callback must respond to call" unless limits_change_callback.respond_to?(:call)
349
+
350
+ @limits_change_callback = limits_change_callback
351
+ else
352
+ @limits_change_callback = nil
353
+ end
354
+ end
355
+
356
+ # Review bit offset to look for overlapping definitions. This will allow
357
+ # gaps in the packet, but not allow the same bits to be used for multiple
358
+ # variables.
359
+ #
360
+ # @return [Array<String>] Warning messages for big definition overlaps
361
+ def check_bit_offsets
362
+ expected_next_offset = nil
363
+ previous_item = nil
364
+ warnings = []
365
+ @sorted_items.each do |item|
366
+ if expected_next_offset and (item.bit_offset < expected_next_offset) and !item.overlap
367
+ msg = "Bit definition overlap at bit offset #{item.bit_offset} for packet #{@target_name} #{@packet_name} items #{item.name} and #{previous_item.name}"
368
+ Logger.instance.warn(msg)
369
+ warnings << msg
370
+ end
371
+ expected_next_offset = Packet.next_bit_offset(item)
372
+ previous_item = item
373
+ end
374
+ warnings
375
+ end
376
+
377
+ # Checks if the packet has any gaps or overlapped items
378
+ #
379
+ # @return [Boolean] true if the packet has no gaps or overlapped items
380
+ def packed?
381
+ expected_next_offset = nil
382
+ @sorted_items.each do |item|
383
+ if expected_next_offset and item.bit_offset != expected_next_offset
384
+ return false
385
+ end
386
+
387
+ expected_next_offset = Packet.next_bit_offset(item)
388
+ end
389
+ true
390
+ end
391
+
392
+ # Returns the bit offset of the next item after the current item if items are packed
393
+ #
394
+ # @param item [PacketItem] The item to calculate the next offset for
395
+ # @return [Integer] Bit Offset of Next Item if Packed
396
+ def self.next_bit_offset(item)
397
+ if item.array_size
398
+ if item.array_size > 0
399
+ next_offset = item.bit_offset + item.array_size
400
+ else
401
+ next_offset = item.array_size
402
+ end
403
+ else
404
+ next_offset = nil
405
+ if item.bit_offset > 0
406
+ if item.little_endian_bit_field?
407
+ # Bit offset always refers to the most significant bit of a bitfield
408
+ bits_remaining_in_last_byte = 8 - (item.bit_offset % 8)
409
+ if item.bit_size > bits_remaining_in_last_byte
410
+ next_offset = item.bit_offset + bits_remaining_in_last_byte
411
+ end
412
+ end
413
+ end
414
+ unless next_offset
415
+ if item.bit_size > 0
416
+ next_offset = item.bit_offset + item.bit_size
417
+ else
418
+ next_offset = item.bit_size
419
+ end
420
+ end
421
+ end
422
+ next_offset
423
+ end
424
+
425
+ # Id items are used by the identify? method to determine if a raw buffer of
426
+ # data represents this packet.
427
+ # @return [Array<PacketItem>] Packet item identifiers
428
+ def id_items
429
+ @id_items ||= []
430
+ end
431
+
432
+ # @return [Array<PacketItem>] All items with defined limits
433
+ def limits_items
434
+ @limits_items ||= []
435
+ end
436
+
437
+ # @return [Hash] Hash of processors associated with this packet
438
+ def processors
439
+ @processors ||= {}
440
+ end
441
+
442
+ # Returns packet specific metadata
443
+ # @return [Hash<Meta Name, Meta Values>]
444
+ def meta
445
+ @meta ||= {}
446
+ end
447
+
448
+ # Sets packet specific metadata
449
+ def meta=(meta)
450
+ @meta = meta
451
+ end
452
+
453
+ # Indicates if the packet has been identified
454
+ # @return [TrueClass or FalseClass]
455
+ def identified?
456
+ !@target_name.nil? && !@packet_name.nil?
457
+ end
458
+
459
+ # Define an item in the packet. This creates a new instance of the
460
+ # item_class as given in the constructor and adds it to the items hash. It
461
+ # also resizes the buffer to accomodate the new item.
462
+ #
463
+ # @param name [String] Name of the item. Used by the items hash to retrieve
464
+ # the item.
465
+ # @param bit_offset [Integer] Bit offset of the item in the raw buffer
466
+ # @param bit_size [Integer] Bit size of the item in the raw buffer
467
+ # @param data_type [Symbol] Type of data contained by the item. This is
468
+ # dependant on the item_class but by default see StructureItem.
469
+ # @param array_size [Integer] Set to a non nil value if the item is to
470
+ # represented as an array.
471
+ # @param endianness [Symbol] Endianness of this item. By default the
472
+ # endianness as set in the constructure is used.
473
+ # @param overflow [Symbol] How to handle value overflows. This is
474
+ # dependant on the item_class but by default see StructureItem.
475
+ # @param format_string [String] String to pass to Kernel#sprintf
476
+ # @param read_conversion [Conversion] Conversion to apply when reading the
477
+ # item from the packet buffer
478
+ # @param write_conversion [Conversion] Conversion to apply before writing
479
+ # the item to the packet buffer
480
+ # @param id_value [Object] Set to something other than nil to indicate that
481
+ # this item should be used to identify a buffer as this packet. The
482
+ # id_value should make sense according to the data_type.
483
+ # @return [PacketItem] The new packet item
484
+ def define_item(name, bit_offset, bit_size, data_type, array_size = nil, endianness = @default_endianness, overflow = :ERROR, format_string = nil, read_conversion = nil, write_conversion = nil, id_value = nil)
485
+ item = super(name, bit_offset, bit_size, data_type, array_size, endianness, overflow)
486
+ packet_define_item(item, format_string, read_conversion, write_conversion, id_value)
487
+ end
488
+
489
+ # Add an item to the packet by adding it to the items hash. It also
490
+ # resizes the buffer to accomodate the new item.
491
+ #
492
+ # @param item [PacketItem] Item to add to the packet
493
+ # @return [PacketItem] The same packet item
494
+ def define(item)
495
+ item = super(item)
496
+ update_id_items(item)
497
+ update_limits_items_cache(item)
498
+ item
499
+ end
500
+
501
+ # Define an item at the end of the packet. This creates a new instance of the
502
+ # item_class as given in the constructor and adds it to the items hash. It
503
+ # also resizes the buffer to accomodate the new item.
504
+ #
505
+ # @param name (see #define_item)
506
+ # @param bit_size (see #define_item)
507
+ # @param data_type (see #define_item)
508
+ # @param array_size (see #define_item)
509
+ # @param endianness (see #define_item)
510
+ # @param overflow (see #define_item)
511
+ # @param format_string (see #define_item)
512
+ # @param read_conversion (see #define_item)
513
+ # @param write_conversion (see #define_item)
514
+ # @param id_value (see #define_item)
515
+ # @return (see #define_item)
516
+ def append_item(name, bit_size, data_type, array_size = nil, endianness = @default_endianness, overflow = :ERROR, format_string = nil, read_conversion = nil, write_conversion = nil, id_value = nil)
517
+ item = super(name, bit_size, data_type, array_size, endianness, overflow)
518
+ packet_define_item(item, format_string, read_conversion, write_conversion, id_value)
519
+ end
520
+
521
+ # (see Structure#get_item)
522
+ def get_item(name)
523
+ super(name)
524
+ rescue ArgumentError
525
+ raise "Packet item '#{@target_name} #{@packet_name} #{name.upcase}' does not exist"
526
+ end
527
+
528
+ # Read an item in the packet
529
+ #
530
+ # @param item [PacketItem] Instance of PacketItem or one of its subclasses
531
+ # @param value_type [Symbol] How to convert the item before returning it.
532
+ # Must be one of {VALUE_TYPES}
533
+ # @param buffer (see Structure#read_item)
534
+ # @return The value. :FORMATTED and :WITH_UNITS values are always returned
535
+ # as Strings. :RAW values will match their data_type. :CONVERTED values
536
+ # can be any type.
537
+ def read_item(item, value_type = :CONVERTED, buffer = @buffer)
538
+ value = super(item, :RAW, buffer)
539
+ derived_raw = false
540
+ if item.data_type == :DERIVED && value_type == :RAW
541
+ value_type = :CONVERTED
542
+ derived_raw = true
543
+ end
544
+ case value_type
545
+ when :RAW
546
+ # Done above
547
+ when :CONVERTED, :FORMATTED, :WITH_UNITS
548
+ if item.read_conversion
549
+ using_cached_value = false
550
+
551
+ check_cache = buffer.equal?(@buffer)
552
+ if check_cache and @read_conversion_cache
553
+ synchronize_allow_reads() do
554
+ if @read_conversion_cache[item]
555
+ value = @read_conversion_cache[item]
556
+
557
+ # Make sure cached value is not modified by anyone by creating
558
+ # a deep copy
559
+ if String === value
560
+ value = value.clone
561
+ elsif Array === value
562
+ value = Marshal.load(Marshal.dump(value))
563
+ end
564
+
565
+ using_cached_value = true
566
+ end
567
+ end
568
+ end
569
+
570
+ unless using_cached_value
571
+ if item.array_size
572
+ value.map! do |val, index|
573
+ item.read_conversion.call(val, self, buffer)
574
+ end
575
+ else
576
+ value = item.read_conversion.call(value, self, buffer)
577
+ end
578
+ if check_cache
579
+ synchronize_allow_reads() do
580
+ @read_conversion_cache ||= {}
581
+ @read_conversion_cache[item] = value
582
+
583
+ # Make sure cached value is not modified by anyone by creating
584
+ # a deep copy
585
+ if String === value
586
+ value = value.clone
587
+ elsif Array === value
588
+ value = Marshal.load(Marshal.dump(value))
589
+ end
590
+ end
591
+ end
592
+ end
593
+ end
594
+
595
+ # Derived raw values perform read_conversions but nothing else
596
+ return value if derived_raw
597
+
598
+ # Convert from value to state if possible
599
+ if item.states
600
+ if Array === value
601
+ value = value.map do |val, index|
602
+ if item.states.key(val)
603
+ item.states.key(val)
604
+ elsif item.states.values.include?(CATCH_ALL_STATE)
605
+ item.states.key(CATCH_ALL_STATE)
606
+ else
607
+ apply_format_string_and_units(item, val, value_type)
608
+ end
609
+ end
610
+ else
611
+ state_value = item.states.key(value)
612
+ if state_value
613
+ value = state_value
614
+ elsif item.states.values.include?(CATCH_ALL_STATE)
615
+ value = item.states.key(CATCH_ALL_STATE)
616
+ else
617
+ value = apply_format_string_and_units(item, value, value_type)
618
+ end
619
+ end
620
+ else
621
+ if Array === value
622
+ value = value.map do |val, index|
623
+ apply_format_string_and_units(item, val, value_type)
624
+ end
625
+ else
626
+ value = apply_format_string_and_units(item, value, value_type)
627
+ end
628
+ end
629
+ else
630
+ raise ArgumentError, "Unknown value type on read: #{value_type}"
631
+ end
632
+ return value
633
+ end
634
+
635
+ # Write an item in the packet
636
+ #
637
+ # @param item [PacketItem] Instance of PacketItem or one of its subclasses
638
+ # @param value (see Structure#write_item)
639
+ # @param value_type (see #read_item)
640
+ # @param buffer (see Structure#write_item)
641
+ def write_item(item, value, value_type = :CONVERTED, buffer = @buffer)
642
+ case value_type
643
+ when :RAW
644
+ super(item, value, value_type, buffer)
645
+ when :CONVERTED
646
+ if item.states
647
+ # Convert from state to value if possible
648
+ state_value = item.states[value.to_s.upcase]
649
+ value = state_value if state_value
650
+ end
651
+ if item.write_conversion
652
+ value = item.write_conversion.call(value, self, buffer)
653
+ else
654
+ raise "Cannot write DERIVED item #{item.name} without a write conversion" if item.data_type == :DERIVED
655
+ end
656
+ begin
657
+ super(item, value, :RAW, buffer) unless item.data_type == :DERIVED
658
+ rescue ArgumentError => err
659
+ if item.states and String === value and err.message =~ /invalid value for/
660
+ raise "Unknown state #{value} for #{item.name}"
661
+ else
662
+ raise err
663
+ end
664
+ end
665
+ when :FORMATTED, :WITH_UNITS
666
+ raise ArgumentError, "Invalid value type on write: #{value_type}"
667
+ else
668
+ raise ArgumentError, "Unknown value type on write: #{value_type}"
669
+ end
670
+ if @read_conversion_cache
671
+ synchronize() do
672
+ @read_conversion_cache.clear
673
+ end
674
+ end
675
+ end
676
+
677
+ # Read an item in the packet by name
678
+ #
679
+ # @param name [String] Name of the item to read
680
+ # @param value_type (see #read_item)
681
+ # @param buffer (see #read_item)
682
+ # @return (see #read_item)
683
+ def read(name, value_type = :CONVERTED, buffer = @buffer)
684
+ return super(name, value_type, buffer)
685
+ end
686
+
687
+ # Write an item in the packet by name
688
+ #
689
+ # @param name [String] Name of the item to write
690
+ # @param value (see #write_item)
691
+ # @param value_type (see #write_item)
692
+ # @param buffer (see #write_item)
693
+ def write(name, value, value_type = :CONVERTED, buffer = @buffer)
694
+ super(name, value, value_type, buffer)
695
+ end
696
+
697
+ # Read all items in the packet into an array of arrays
698
+ # [[item name, item value], ...]
699
+ #
700
+ # @param value_type (see #read_item)
701
+ # @param buffer (see Structure#read_all)
702
+ # @param top (See Structure#read_all)
703
+ # @return (see Structure#read_all)
704
+ def read_all(value_type = :CONVERTED, buffer = @buffer, top = true)
705
+ return super(value_type, buffer, top)
706
+ end
707
+
708
+ # Read all items in the packet into an array of arrays
709
+ # [[item name, item value], [item limits state], ...]
710
+ #
711
+ # @param value_type (see #read_all)
712
+ # @param buffer (see #read_all)
713
+ # @return [Array<String, Object, Symbol|nil>] Returns an Array consisting
714
+ # of [item name, item value, item limits state] where the item limits
715
+ # state can be one of {OpenC3::Limits::LIMITS_STATES}
716
+ def read_all_with_limits_states(value_type = :CONVERTED, buffer = @buffer)
717
+ result = nil
718
+ synchronize_allow_reads(true) do
719
+ result = read_all(value_type, buffer, false).map! do |array|
720
+ array << @items[array[0]].limits.state
721
+ end
722
+ end
723
+ return result
724
+ end
725
+
726
+ # Create a string that shows the name and value of each item in the packet
727
+ #
728
+ # @param value_type (see #read_item)
729
+ # @param indent (see Structure#formatted)
730
+ # @param buffer (see Structure#formatted)
731
+ # @param ignored (see Structure#ignored)
732
+ # @return (see Structure#formatted)
733
+ def formatted(value_type = :CONVERTED, indent = 0, buffer = @buffer, ignored = nil)
734
+ return super(value_type, indent, buffer, ignored)
735
+ end
736
+
737
+ # Restore all items in the packet to their default value
738
+ #
739
+ # @param buffer [String] Raw buffer of binary data
740
+ # @param skip_item_names [Array] Array of item names to skip
741
+ def restore_defaults(buffer = @buffer, skip_item_names = nil)
742
+ upcase_skip_item_names = skip_item_names.map(&:upcase) if skip_item_names
743
+ @sorted_items.each do |item|
744
+ next if RESERVED_ITEM_NAMES.include?(item.name)
745
+
746
+ write_item(item, item.default, :CONVERTED, buffer) unless skip_item_names and upcase_skip_item_names.include?(item.name)
747
+ end
748
+ end
749
+
750
+ # Define the reserved items on the current telemetry packet
751
+ def define_reserved_items
752
+ item = define_item('PACKET_TIMESECONDS', 0, 0, :DERIVED, nil, @default_endianness,
753
+ :ERROR, '%0.6f', PacketTimeSecondsConversion.new)
754
+ item.description = 'OpenC3 Packet Time (UTC, Floating point, Unix epoch)'
755
+ item = define_item('PACKET_TIMEFORMATTED', 0, 0, :DERIVED, nil, @default_endianness,
756
+ :ERROR, nil, PacketTimeFormattedConversion.new)
757
+ item.description = 'OpenC3 Packet Time (Local time zone, Formatted string)'
758
+ item = define_item('RECEIVED_TIMESECONDS', 0, 0, :DERIVED, nil, @default_endianness,
759
+ :ERROR, '%0.6f', ReceivedTimeSecondsConversion.new)
760
+ item.description = 'OpenC3 Received Time (UTC, Floating point, Unix epoch)'
761
+ item = define_item('RECEIVED_TIMEFORMATTED', 0, 0, :DERIVED, nil, @default_endianness,
762
+ :ERROR, nil, ReceivedTimeFormattedConversion.new)
763
+ item.description = 'OpenC3 Received Time (Local time zone, Formatted string)'
764
+ item = define_item('RECEIVED_COUNT', 0, 0, :DERIVED, nil, @default_endianness,
765
+ :ERROR, nil, ReceivedCountConversion.new)
766
+ item.description = 'OpenC3 packet received count'
767
+ end
768
+
769
+ # Enable limits on an item by name
770
+ #
771
+ # @param name [String] Name of the item to enable limits
772
+ def enable_limits(name)
773
+ get_item(name).limits.enabled = true
774
+ end
775
+
776
+ # Disable limits on an item by name
777
+ #
778
+ # @param name [String] Name of the item to disable limits
779
+ def disable_limits(name)
780
+ item = get_item(name)
781
+ item.limits.enabled = false
782
+ unless item.limits.state == :STALE
783
+ old_limits_state = item.limits.state
784
+ item.limits.state = nil
785
+ @limits_change_callback.call(self, item, old_limits_state, nil, false) if @limits_change_callback
786
+ end
787
+ end
788
+
789
+ # Add an item to the limits items cache if necessary.
790
+ # You MUST call this after adding limits to an item
791
+ # This is an optimization so we don't have to iterate through all the items when
792
+ # checking for limits.
793
+ def update_limits_items_cache(item)
794
+ if item.limits.values || item.state_colors
795
+ @limits_items ||= []
796
+ @limits_items_hash ||= {}
797
+ unless @limits_items_hash[item]
798
+ @limits_items << item
799
+ @limits_items_hash[item] = true
800
+ end
801
+ end
802
+ end
803
+
804
+ # Return an array of arrays indicating all items in the packet that are out of limits
805
+ # [[target name, packet name, item name, item limits state], ...]
806
+ #
807
+ # @return [Array<Array<String, String, String, Symbol>>]
808
+ def out_of_limits
809
+ items = []
810
+ return items unless @limits_items
811
+
812
+ @limits_items.each do |item|
813
+ if item.limits.enabled && item.limits.state &&
814
+ PacketItemLimits::OUT_OF_LIMITS_STATES.include?(item.limits.state)
815
+ items << [@target_name, @packet_name, item.name, item.limits.state]
816
+ end
817
+ end
818
+ return items
819
+ end
820
+
821
+ # Set the limits state for all items to the given state
822
+ #
823
+ # @param state [Symbol] Must be one of PacketItemLimits::LIMITS_STATES
824
+ def set_all_limits_states(state)
825
+ @sorted_items.each { |item| item.limits.state = state }
826
+ end
827
+
828
+ # Check all the items in the packet against their defined limits. Update
829
+ # their internal limits state and persistence and call the
830
+ # limits_change_callback as necessary.
831
+ #
832
+ # @param limits_set [Symbol] Which limits set to check the item values
833
+ # against.
834
+ # @param ignore_persistence [Boolean] Whether to ignore persistence when
835
+ # checking for out of limits
836
+ def check_limits(limits_set = :DEFAULT, ignore_persistence = false)
837
+ # If check_limits is being called, then a new packet has arrived and
838
+ # this packet is no longer stale
839
+ # Stored telemetry doesn't affect the current value table and such doesn't affect stale
840
+ if @stale and !@stored
841
+ @stale = false
842
+ set_all_limits_states(nil)
843
+ end
844
+
845
+ return unless @limits_items
846
+
847
+ @limits_items.each do |item|
848
+ # Verify limits monitoring is enabled for this item
849
+ if item.limits.enabled
850
+ value = read_item(item)
851
+
852
+ # Handle state monitoring and value monitoring differently
853
+ if item.states
854
+ handle_limits_states(item, value)
855
+ elsif item.limits.values
856
+ handle_limits_values(item, value, limits_set, ignore_persistence)
857
+ end
858
+ end
859
+ end
860
+ end
861
+
862
+ # Sets the overall packet stale state to true and sets each packet item
863
+ # limits state to :STALE.
864
+ def set_stale
865
+ @stale = true
866
+ set_all_limits_states(:STALE)
867
+ end
868
+
869
+ # Reset temporary packet data
870
+ # This includes packet received time, received count, and processor state
871
+ def reset
872
+ # The SYSTEM META packet is a special case that does not get reset
873
+ return if @target_name == 'SYSTEM' && @packet_name == 'META'
874
+
875
+ @received_time = nil
876
+ @received_count = 0
877
+ @stored = false
878
+ @extra = nil
879
+ if @read_conversion_cache
880
+ synchronize() do
881
+ @read_conversion_cache.clear
882
+ end
883
+ end
884
+ return unless @processors
885
+
886
+ @processors.each do |processor_name, processor|
887
+ processor.reset
888
+ end
889
+ end
890
+
891
+ # Make a light weight clone of this packet. This only creates a new buffer
892
+ # of data and clones the processors. The defined packet items are the same.
893
+ #
894
+ # @return [Packet] A copy of the current packet with a new underlying
895
+ # buffer of data and processors
896
+ def clone
897
+ packet = super()
898
+ if packet.instance_variable_get("@processors".freeze)
899
+ packet.instance_variable_set("@processors".freeze, packet.processors.clone)
900
+ packet.processors.each do |processor_name, processor|
901
+ packet.processors[processor_name] = processor.clone
902
+ end
903
+ end
904
+ packet.instance_variable_set("@read_conversion_cache".freeze, nil)
905
+ packet.extra = JSON.parse(packet.extra.as_json(:allow_nan => true).to_json(:allow_nan => true), :allow_nan => true, :create_additions => true) if packet.extra # Deep copy using JSON
906
+ packet
907
+ end
908
+ alias dup clone
909
+
910
+ def update_id_items(item)
911
+ if item.id_value
912
+ @id_items ||= []
913
+ # Add to Id Items
914
+ unless @id_items.empty?
915
+ last_item = @id_items[-1]
916
+ @id_items << item
917
+ # If the current item or last item have a negative offset then we have
918
+ # to re-sort. We also re-sort if the current item is less than the last
919
+ # item because we are inserting.
920
+ if last_item.bit_offset <= 0 or item.bit_offset <= 0 or item.bit_offset < last_item.bit_offset
921
+ @id_items = @id_items.sort
922
+ end
923
+ else
924
+ @id_items << item
925
+ end
926
+ end
927
+ item
928
+ end
929
+
930
+ def to_config(cmd_or_tlm)
931
+ config = ''
932
+
933
+ if cmd_or_tlm == :TELEMETRY
934
+ config << "TELEMETRY #{@target_name.to_s.quote_if_necessary} #{@packet_name.to_s.quote_if_necessary} #{@default_endianness} \"#{@description}\"\n"
935
+ else
936
+ config << "COMMAND #{@target_name.to_s.quote_if_necessary} #{@packet_name.to_s.quote_if_necessary} #{@default_endianness} \"#{@description}\"\n"
937
+ end
938
+ config << " ALLOW_SHORT\n" if @short_buffer_allowed
939
+ config << " HAZARDOUS #{@hazardous_description.to_s.quote_if_necessary}\n" if @hazardous
940
+ config << " DISABLE_MESSAGES\n" if @messages_disabled
941
+ if @disabled
942
+ config << " DISABLED\n"
943
+ elsif @hidden
944
+ config << " HIDDEN\n"
945
+ end
946
+
947
+ if @processors
948
+ @processors.each do |processor_name, processor|
949
+ config << processor.to_config
950
+ end
951
+ end
952
+
953
+ if @meta
954
+ @meta.each do |key, values|
955
+ config << " META #{key.to_s.quote_if_necessary} #{values.map { |a| a..to_s.quote_if_necessary }.join(" ")}\n"
956
+ end
957
+ end
958
+
959
+ # Items with derived items last
960
+ @sorted_items.each do |item|
961
+ if item.data_type != :DERIVED
962
+ config << item.to_config(cmd_or_tlm, @default_endianness)
963
+ end
964
+ end
965
+ @sorted_items.each do |item|
966
+ if item.data_type == :DERIVED
967
+ unless RESERVED_ITEM_NAMES.include?(item.name)
968
+ config << item.to_config(cmd_or_tlm, @default_endianness)
969
+ end
970
+ end
971
+ end
972
+
973
+ config
974
+ end
975
+
976
+ def as_json(*a)
977
+ config = {}
978
+ config['target_name'] = @target_name.to_s
979
+ config['packet_name'] = @packet_name.to_s
980
+ config['endianness'] = @default_endianness.to_s
981
+ config['description'] = @description
982
+ config['short_buffer_allowed'] = true if @short_buffer_allowed
983
+ config['hazardous'] = true if @hazardous
984
+ config['hazardous_description'] = @hazardous_description.to_s if @hazardous_description
985
+ config['messages_disabled'] = true if @messages_disabled
986
+ config['disabled'] = true if @disabled
987
+ config['hidden'] = true if @hidden
988
+ config['stale'] = true if @stale
989
+
990
+ if @processors
991
+ processors = []
992
+ config['processors'] = processors
993
+ @processors.each do |processor_name, processor|
994
+ processors << processor.as_json(*a)
995
+ end
996
+ end
997
+
998
+ config['meta'] = @meta if @meta
999
+
1000
+ items = []
1001
+ config['items'] = items
1002
+ # Items with derived items last
1003
+ @sorted_items.each do |item|
1004
+ if item.data_type != :DERIVED
1005
+ items << item.as_json(*a)
1006
+ end
1007
+ end
1008
+ @sorted_items.each do |item|
1009
+ if item.data_type == :DERIVED
1010
+ items << item.as_json(*a)
1011
+ end
1012
+ end
1013
+
1014
+ config
1015
+ end
1016
+
1017
+ def self.from_json(hash)
1018
+ endianness = hash['endianness'] ? hash['endianness'].intern : nil # Convert to symbol
1019
+ packet = Packet.new(hash['target_name'], hash['packet_name'], endianness, hash['description'])
1020
+ packet.short_buffer_allowed = hash['short_buffer_allowed']
1021
+ packet.hazardous = hash['hazardous']
1022
+ packet.hazardous_description = hash['hazardous_description']
1023
+ packet.messages_disabled = hash['messages_disabled']
1024
+ packet.disabled = hash['disabled']
1025
+ packet.hidden = hash['hidden']
1026
+ # packet.stale is read only
1027
+ packet.meta = hash['meta']
1028
+ # Can't convert processors
1029
+ hash['items'].each do |item|
1030
+ packet.define(PacketItem.from_json(item))
1031
+ end
1032
+ packet
1033
+ end
1034
+
1035
+ protected
1036
+
1037
+ # Performs packet specific processing on the packet.
1038
+ # Intended to only be run once for each packet received
1039
+ def process(buffer = @buffer)
1040
+ return unless @processors
1041
+
1042
+ @processors.each do |processor_name, processor|
1043
+ processor.call(self, buffer)
1044
+ end
1045
+ end
1046
+
1047
+ def handle_limits_states(item, value)
1048
+ # Retrieve limits state for the given value
1049
+ limits_state = item.state_colors[value]
1050
+
1051
+ if item.limits.state != limits_state # PacketItemLimits state has changed
1052
+ # Save old limits state
1053
+ old_limits_state = item.limits.state
1054
+ # Update to new limits state
1055
+ item.limits.state = limits_state
1056
+
1057
+ if old_limits_state == nil # Changing from nil
1058
+ if limits_state != :GREEN && limits_state != :BLUE # Warnings are needed
1059
+ @limits_change_callback.call(self, item, old_limits_state, value, true) if @limits_change_callback
1060
+ end
1061
+ else # Changing from a state other than nil so always call the callback
1062
+ if @limits_change_callback
1063
+ if item.limits.state.nil?
1064
+ @limits_change_callback.call(self, item, old_limits_state, value, false)
1065
+ else
1066
+ @limits_change_callback.call(self, item, old_limits_state, value, true)
1067
+ end
1068
+ end
1069
+ end
1070
+ end
1071
+ end
1072
+
1073
+ def handle_limits_values(item, value, limits_set, ignore_persistence)
1074
+ # Retrieve limits settings for the specified limits_set
1075
+ limits = item.limits.values[limits_set]
1076
+
1077
+ # Use the default limits set if limits aren't specified for the
1078
+ # particular limits set
1079
+ limits = item.limits.values[:DEFAULT] unless limits
1080
+
1081
+ # Extract limits from array
1082
+ red_low = limits[0]
1083
+ yellow_low = limits[1]
1084
+ yellow_high = limits[2]
1085
+ red_high = limits[3]
1086
+ green_low = limits[4]
1087
+ green_high = limits[5]
1088
+ limits_state = nil
1089
+
1090
+ # Determine the limits_state based on the limits values and the current
1091
+ # value of the item
1092
+ if value > yellow_low
1093
+ if value < yellow_high
1094
+ if green_low
1095
+ if value < green_high
1096
+ if value > green_low
1097
+ limits_state = :BLUE
1098
+ else
1099
+ limits_state = :GREEN_LOW
1100
+ end
1101
+ else
1102
+ limits_state = :GREEN_HIGH
1103
+ end
1104
+ else
1105
+ limits_state = :GREEN
1106
+ end
1107
+ elsif value < red_high
1108
+ limits_state = :YELLOW_HIGH
1109
+ else
1110
+ limits_state = :RED_HIGH
1111
+ end
1112
+ else # value <= yellow_low
1113
+ if value > red_low
1114
+ limits_state = :YELLOW_LOW
1115
+ else
1116
+ limits_state = :RED_LOW
1117
+ end
1118
+ end
1119
+
1120
+ if item.limits.state != limits_state # limits state has changed
1121
+ # Save old limits state for use in the callback
1122
+ old_limits_state = item.limits.state
1123
+
1124
+ item.limits.persistence_count += 1
1125
+
1126
+ # Check for item to achieve its persistence which means we
1127
+ # have to update the state and call the callback
1128
+ # Note when going back to green (or blue) persistence is ignored
1129
+ if (item.limits.persistence_count >= item.limits.persistence_setting) || ignore_persistence
1130
+ item.limits.state = limits_state
1131
+
1132
+ # Additional actions for limits change
1133
+ @limits_change_callback.call(self, item, old_limits_state, value, true) if @limits_change_callback
1134
+
1135
+ # Clear persistence since we've entered a new state
1136
+ item.limits.persistence_count = 0
1137
+ end
1138
+ else # limits state has not changed so clear persistence
1139
+ item.limits.persistence_count = 0
1140
+ end
1141
+ end
1142
+
1143
+ def apply_format_string_and_units(item, value, value_type)
1144
+ if value_type == :FORMATTED or value_type == :WITH_UNITS
1145
+ if item.format_string && value
1146
+ value = sprintf(item.format_string, value)
1147
+ else
1148
+ value = value.to_s
1149
+ end
1150
+ end
1151
+ value << ' ' << item.units if value_type == :WITH_UNITS and item.units
1152
+ value
1153
+ end
1154
+
1155
+ def packet_define_item(item, format_string, read_conversion, write_conversion, id_value)
1156
+ item.format_string = format_string
1157
+ item.read_conversion = read_conversion
1158
+ item.write_conversion = write_conversion
1159
+
1160
+ # Change id_value to the correct type
1161
+ if id_value
1162
+ item.id_value = id_value
1163
+ update_id_items(item)
1164
+ end
1165
+ item
1166
+ end
1167
+ end
1168
+ end