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.
- checksums.yaml +7 -0
- data/Gemfile +18 -0
- data/Guardfile +35 -0
- data/LICENSE.txt +727 -0
- data/README.md +37 -0
- data/Rakefile +131 -0
- data/bin/cstol_converter +1178 -0
- data/bin/openc3cli +531 -0
- data/bin/rubysloc +139 -0
- data/data/config/_array_params.yaml +23 -0
- data/data/config/_id_items.yaml +24 -0
- data/data/config/_id_params.yaml +58 -0
- data/data/config/_interfaces.yaml +214 -0
- data/data/config/_interfaces.yaml.err +1017 -0
- data/data/config/_items.yaml +20 -0
- data/data/config/_params.yaml +60 -0
- data/data/config/cmd_tlm_server.yaml +136 -0
- data/data/config/command.yaml +44 -0
- data/data/config/command_modifiers.yaml +160 -0
- data/data/config/command_telemetry.yaml +3 -0
- data/data/config/interface_modifiers.yaml +104 -0
- data/data/config/item_modifiers.yaml +221 -0
- data/data/config/microservice.yaml +78 -0
- data/data/config/param_item_modifiers.yaml +52 -0
- data/data/config/parameter_modifiers.yaml +200 -0
- data/data/config/plugins.yaml +80 -0
- data/data/config/protocols.yaml +290 -0
- data/data/config/screen.yaml +147 -0
- data/data/config/table_manager.yaml +89 -0
- data/data/config/table_parameter_modifiers.yaml +9 -0
- data/data/config/target.yaml +142 -0
- data/data/config/target_config.yaml +94 -0
- data/data/config/telemetry.yaml +87 -0
- data/data/config/telemetry_modifiers.yaml +159 -0
- data/data/config/tool.yaml +63 -0
- data/data/config/unknown.yaml +3 -0
- data/data/config/widgets.yaml +1505 -0
- data/ext/mkrf_conf.rb +49 -0
- data/ext/openc3/ext/array/array.c +122 -0
- data/ext/openc3/ext/array/extconf.rb +13 -0
- data/ext/openc3/ext/buffered_file/buffered_file.c +198 -0
- data/ext/openc3/ext/buffered_file/extconf.rb +13 -0
- data/ext/openc3/ext/config_parser/config_parser.c +280 -0
- data/ext/openc3/ext/config_parser/extconf.rb +13 -0
- data/ext/openc3/ext/crc/crc.c +351 -0
- data/ext/openc3/ext/crc/extconf.rb +13 -0
- data/ext/openc3/ext/openc3_io/extconf.rb +13 -0
- data/ext/openc3/ext/openc3_io/openc3_io.c +158 -0
- data/ext/openc3/ext/packet/extconf.rb +13 -0
- data/ext/openc3/ext/packet/packet.c +318 -0
- data/ext/openc3/ext/platform/extconf.rb +13 -0
- data/ext/openc3/ext/platform/platform.c +134 -0
- data/ext/openc3/ext/polynomial_conversion/extconf.rb +13 -0
- data/ext/openc3/ext/polynomial_conversion/polynomial_conversion.c +79 -0
- data/ext/openc3/ext/string/extconf.rb +13 -0
- data/ext/openc3/ext/string/string.c +63 -0
- data/ext/openc3/ext/structure/structure.c +1719 -0
- data/ext/openc3/ext/tabbed_plots_config/extconf.rb +13 -0
- data/ext/openc3/ext/tabbed_plots_config/tabbed_plots_config.c +62 -0
- data/ext/openc3/ext/telemetry/extconf.rb +13 -0
- data/ext/openc3/ext/telemetry/telemetry.c +336 -0
- data/lib/cosmos.rb +20 -0
- data/lib/cosmosc2.rb +20 -0
- data/lib/openc3/api/api.rb +39 -0
- data/lib/openc3/api/authorized_api.rb +30 -0
- data/lib/openc3/api/cmd_api.rb +451 -0
- data/lib/openc3/api/config_api.rb +58 -0
- data/lib/openc3/api/interface_api.rb +117 -0
- data/lib/openc3/api/limits_api.rb +375 -0
- data/lib/openc3/api/router_api.rb +117 -0
- data/lib/openc3/api/settings_api.rb +70 -0
- data/lib/openc3/api/target_api.rb +78 -0
- data/lib/openc3/api/tlm_api.rb +455 -0
- data/lib/openc3/bridge/bridge.rb +54 -0
- data/lib/openc3/bridge/bridge_config.rb +167 -0
- data/lib/openc3/bridge/bridge_interface_thread.rb +42 -0
- data/lib/openc3/bridge/bridge_router_thread.rb +42 -0
- data/lib/openc3/ccsds/ccsds_packet.rb +68 -0
- data/lib/openc3/ccsds/ccsds_parser.rb +148 -0
- data/lib/openc3/config/config_parser.rb +549 -0
- data/lib/openc3/config/meta_config_parser.rb +74 -0
- data/lib/openc3/conversions/conversion.rb +70 -0
- data/lib/openc3/conversions/generic_conversion.rb +83 -0
- data/lib/openc3/conversions/packet_time_formatted_conversion.rb +43 -0
- data/lib/openc3/conversions/packet_time_seconds_conversion.rb +43 -0
- data/lib/openc3/conversions/polynomial_conversion.rb +87 -0
- data/lib/openc3/conversions/processor_conversion.rb +70 -0
- data/lib/openc3/conversions/received_count_conversion.rb +38 -0
- data/lib/openc3/conversions/received_time_formatted_conversion.rb +42 -0
- data/lib/openc3/conversions/received_time_seconds_conversion.rb +42 -0
- data/lib/openc3/conversions/segmented_polynomial_conversion.rb +171 -0
- data/lib/openc3/conversions/unix_time_conversion.rb +68 -0
- data/lib/openc3/conversions/unix_time_formatted_conversion.rb +49 -0
- data/lib/openc3/conversions/unix_time_seconds_conversion.rb +49 -0
- data/lib/openc3/conversions.rb +34 -0
- data/lib/openc3/core_ext/array.rb +416 -0
- data/lib/openc3/core_ext/binding.rb +29 -0
- data/lib/openc3/core_ext/class.rb +72 -0
- data/lib/openc3/core_ext/exception.rb +61 -0
- data/lib/openc3/core_ext/file.rb +83 -0
- data/lib/openc3/core_ext/hash.rb +37 -0
- data/lib/openc3/core_ext/io.rb +134 -0
- data/lib/openc3/core_ext/kernel.rb +42 -0
- data/lib/openc3/core_ext/math.rb +128 -0
- data/lib/openc3/core_ext/matrix.rb +156 -0
- data/lib/openc3/core_ext/objectspace.rb +36 -0
- data/lib/openc3/core_ext/openc3_io.rb +57 -0
- data/lib/openc3/core_ext/range.rb +27 -0
- data/lib/openc3/core_ext/socket.rb +38 -0
- data/lib/openc3/core_ext/string.rb +389 -0
- data/lib/openc3/core_ext/stringio.rb +33 -0
- data/lib/openc3/core_ext/time.rb +508 -0
- data/lib/openc3/core_ext.rb +36 -0
- data/lib/openc3/interfaces/interface.rb +498 -0
- data/lib/openc3/interfaces/linc_interface.rb +475 -0
- data/lib/openc3/interfaces/protocols/burst_protocol.rb +192 -0
- data/lib/openc3/interfaces/protocols/crc_protocol.rb +193 -0
- data/lib/openc3/interfaces/protocols/fixed_protocol.rb +155 -0
- data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +56 -0
- data/lib/openc3/interfaces/protocols/length_protocol.rb +165 -0
- data/lib/openc3/interfaces/protocols/override_protocol.rb +60 -0
- data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +206 -0
- data/lib/openc3/interfaces/protocols/protocol.rb +82 -0
- data/lib/openc3/interfaces/protocols/template_protocol.rb +261 -0
- data/lib/openc3/interfaces/protocols/terminated_protocol.rb +93 -0
- data/lib/openc3/interfaces/serial_interface.rb +94 -0
- data/lib/openc3/interfaces/simulated_target_interface.rb +168 -0
- data/lib/openc3/interfaces/stream_interface.rb +81 -0
- data/lib/openc3/interfaces/tcpip_client_interface.rb +69 -0
- data/lib/openc3/interfaces/tcpip_server_interface.rb +629 -0
- data/lib/openc3/interfaces/udp_interface.rb +169 -0
- data/lib/openc3/interfaces.rb +44 -0
- data/lib/openc3/io/buffered_file.rb +109 -0
- data/lib/openc3/io/io_multiplexer.rb +80 -0
- data/lib/openc3/io/json_api_object.rb +208 -0
- data/lib/openc3/io/json_drb.rb +335 -0
- data/lib/openc3/io/json_drb_object.rb +114 -0
- data/lib/openc3/io/json_drb_rack.rb +84 -0
- data/lib/openc3/io/json_rpc.rb +420 -0
- data/lib/openc3/io/openc3_snmp.rb +58 -0
- data/lib/openc3/io/posix_serial_driver.rb +156 -0
- data/lib/openc3/io/raw_logger.rb +167 -0
- data/lib/openc3/io/raw_logger_pair.rb +77 -0
- data/lib/openc3/io/serial_driver.rb +105 -0
- data/lib/openc3/io/stderr.rb +43 -0
- data/lib/openc3/io/stdout.rb +43 -0
- data/lib/openc3/io/udp_sockets.rb +194 -0
- data/lib/openc3/io/win32_serial_driver.rb +196 -0
- data/lib/openc3/logs/log_writer.rb +302 -0
- data/lib/openc3/logs/packet_log_constants.rb +62 -0
- data/lib/openc3/logs/packet_log_reader.rb +345 -0
- data/lib/openc3/logs/packet_log_writer.rb +299 -0
- data/lib/openc3/logs/text_log_writer.rb +68 -0
- data/lib/openc3/logs.rb +25 -0
- data/lib/openc3/microservices/cleanup_microservice.rb +68 -0
- data/lib/openc3/microservices/decom_microservice.rb +136 -0
- data/lib/openc3/microservices/interface_microservice.rb +532 -0
- data/lib/openc3/microservices/log_microservice.rb +108 -0
- data/lib/openc3/microservices/microservice.rb +204 -0
- data/lib/openc3/microservices/plugin_microservice.rb +43 -0
- data/lib/openc3/microservices/reaction_microservice.rb +541 -0
- data/lib/openc3/microservices/reducer_microservice.rb +313 -0
- data/lib/openc3/microservices/router_microservice.rb +44 -0
- data/lib/openc3/microservices/text_log_microservice.rb +84 -0
- data/lib/openc3/microservices/timeline_microservice.rb +363 -0
- data/lib/openc3/microservices/trigger_group_microservice.rb +638 -0
- data/lib/openc3/models/activity_model.rb +319 -0
- data/lib/openc3/models/auth_model.rb +65 -0
- data/lib/openc3/models/cvt_model.rb +185 -0
- data/lib/openc3/models/environment_model.rb +58 -0
- data/lib/openc3/models/gem_model.rb +137 -0
- data/lib/openc3/models/info_model.rb +31 -0
- data/lib/openc3/models/interface_model.rb +281 -0
- data/lib/openc3/models/interface_status_model.rb +117 -0
- data/lib/openc3/models/metadata_model.rb +139 -0
- data/lib/openc3/models/metric_model.rb +59 -0
- data/lib/openc3/models/microservice_model.rb +206 -0
- data/lib/openc3/models/microservice_status_model.rb +74 -0
- data/lib/openc3/models/model.rb +204 -0
- data/lib/openc3/models/note_model.rb +122 -0
- data/lib/openc3/models/notification_model.rb +40 -0
- data/lib/openc3/models/ping_model.rb +35 -0
- data/lib/openc3/models/plugin_model.rb +292 -0
- data/lib/openc3/models/process_status_model.rb +76 -0
- data/lib/openc3/models/reaction_model.rb +322 -0
- data/lib/openc3/models/reducer_model.rb +65 -0
- data/lib/openc3/models/router_model.rb +35 -0
- data/lib/openc3/models/router_status_model.rb +27 -0
- data/lib/openc3/models/scope_model.rb +153 -0
- data/lib/openc3/models/settings_model.rb +55 -0
- data/lib/openc3/models/sorted_model.rb +167 -0
- data/lib/openc3/models/target_model.rb +759 -0
- data/lib/openc3/models/timeline_model.rb +154 -0
- data/lib/openc3/models/tool_config_model.rb +38 -0
- data/lib/openc3/models/tool_model.rb +262 -0
- data/lib/openc3/models/trigger_group_model.rb +186 -0
- data/lib/openc3/models/trigger_model.rb +330 -0
- data/lib/openc3/models/widget_model.rb +138 -0
- data/lib/openc3/operators/microservice_operator.rb +128 -0
- data/lib/openc3/operators/operator.rb +277 -0
- data/lib/openc3/packets/binary_accessor.rb +1207 -0
- data/lib/openc3/packets/commands.rb +373 -0
- data/lib/openc3/packets/json_packet.rb +134 -0
- data/lib/openc3/packets/limits.rb +271 -0
- data/lib/openc3/packets/limits_response.rb +53 -0
- data/lib/openc3/packets/packet.rb +1168 -0
- data/lib/openc3/packets/packet_config.rb +625 -0
- data/lib/openc3/packets/packet_item.rb +586 -0
- data/lib/openc3/packets/packet_item_limits.rb +162 -0
- data/lib/openc3/packets/parsers/format_string_parser.rb +65 -0
- data/lib/openc3/packets/parsers/limits_parser.rb +159 -0
- data/lib/openc3/packets/parsers/limits_response_parser.rb +61 -0
- data/lib/openc3/packets/parsers/packet_item_parser.rb +272 -0
- data/lib/openc3/packets/parsers/packet_parser.rb +134 -0
- data/lib/openc3/packets/parsers/processor_parser.rb +73 -0
- data/lib/openc3/packets/parsers/state_parser.rb +127 -0
- data/lib/openc3/packets/parsers/xtce_converter.rb +442 -0
- data/lib/openc3/packets/parsers/xtce_parser.rb +722 -0
- data/lib/openc3/packets/structure.rb +553 -0
- data/lib/openc3/packets/structure_item.rb +365 -0
- data/lib/openc3/packets/telemetry.rb +487 -0
- data/lib/openc3/processors/processor.rb +86 -0
- data/lib/openc3/processors/statistics_processor.rb +82 -0
- data/lib/openc3/processors/watermark_processor.rb +58 -0
- data/lib/openc3/processors.rb +24 -0
- data/lib/openc3/script/api_shared.rb +828 -0
- data/lib/openc3/script/calendar.rb +89 -0
- data/lib/openc3/script/commands.rb +227 -0
- data/lib/openc3/script/exceptions.rb +29 -0
- data/lib/openc3/script/extract.rb +161 -0
- data/lib/openc3/script/limits.rb +60 -0
- data/lib/openc3/script/script.rb +299 -0
- data/lib/openc3/script/script_runner.rb +238 -0
- data/lib/openc3/script/storage.rb +146 -0
- data/lib/openc3/script/suite.rb +542 -0
- data/lib/openc3/script/suite_results.rb +196 -0
- data/lib/openc3/script/suite_runner.rb +217 -0
- data/lib/openc3/script.rb +21 -0
- data/lib/openc3/streams/serial_stream.rb +167 -0
- data/lib/openc3/streams/stream.rb +63 -0
- data/lib/openc3/streams/tcpip_client_stream.rb +116 -0
- data/lib/openc3/streams/tcpip_socket_stream.rb +195 -0
- data/lib/openc3/system/system.rb +127 -0
- data/lib/openc3/system/system_config.rb +411 -0
- data/lib/openc3/system/target.rb +269 -0
- data/lib/openc3/system.rb +24 -0
- data/lib/openc3/tools/cmd_tlm_server/api.rb +20 -0
- data/lib/openc3/tools/cmd_tlm_server/cmd_tlm_server_config.rb +320 -0
- data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +294 -0
- data/lib/openc3/tools/table_manager/table.rb +77 -0
- data/lib/openc3/tools/table_manager/table_config.rb +273 -0
- data/lib/openc3/tools/table_manager/table_item.rb +90 -0
- data/lib/openc3/tools/table_manager/table_item_parser.rb +66 -0
- data/lib/openc3/tools/table_manager/table_manager_core.rb +333 -0
- data/lib/openc3/tools/table_manager/table_parser.rb +93 -0
- data/lib/openc3/tools/test_runner/test.rb +67 -0
- data/lib/openc3/top_level.rb +595 -0
- data/lib/openc3/topics/autonomic_topic.rb +52 -0
- data/lib/openc3/topics/calendar_topic.rb +44 -0
- data/lib/openc3/topics/command_decom_topic.rb +76 -0
- data/lib/openc3/topics/command_topic.rb +83 -0
- data/lib/openc3/topics/config_topic.rb +68 -0
- data/lib/openc3/topics/interface_topic.rb +73 -0
- data/lib/openc3/topics/limits_event_topic.rb +109 -0
- data/lib/openc3/topics/notifications_topic.rb +28 -0
- data/lib/openc3/topics/router_topic.rb +85 -0
- data/lib/openc3/topics/telemetry_decom_topic.rb +54 -0
- data/lib/openc3/topics/telemetry_topic.rb +36 -0
- data/lib/openc3/topics/timeline_topic.rb +45 -0
- data/lib/openc3/topics/topic.rb +53 -0
- data/lib/openc3/utilities/authentication.rb +141 -0
- data/lib/openc3/utilities/authorization.rb +51 -0
- data/lib/openc3/utilities/crc.rb +278 -0
- data/lib/openc3/utilities/csv.rb +153 -0
- data/lib/openc3/utilities/logger.rb +187 -0
- data/lib/openc3/utilities/message_log.rb +91 -0
- data/lib/openc3/utilities/metric.rb +141 -0
- data/lib/openc3/utilities/process_manager.rb +139 -0
- data/lib/openc3/utilities/quaternion.rb +257 -0
- data/lib/openc3/utilities/ruby_lex_utils.rb +568 -0
- data/lib/openc3/utilities/s3.rb +202 -0
- data/lib/openc3/utilities/s3_autoload.rb +9 -0
- data/lib/openc3/utilities/s3_file_cache.rb +274 -0
- data/lib/openc3/utilities/simulated_target.rb +117 -0
- data/lib/openc3/utilities/sleeper.rb +51 -0
- data/lib/openc3/utilities/store.rb +23 -0
- data/lib/openc3/utilities/store_autoload.rb +237 -0
- data/lib/openc3/utilities/zip.rb +21 -0
- data/lib/openc3/utilities.rb +35 -0
- data/lib/openc3/version.rb +14 -0
- data/lib/openc3/win32/excel.rb +132 -0
- data/lib/openc3/win32/win32.rb +402 -0
- data/lib/openc3/win32/win32_main.rb +333 -0
- data/lib/openc3.rb +49 -0
- data/tasks/gemfile_stats.rake +113 -0
- data/tasks/spec.rake +30 -0
- data/templates/plugin-template/README.md +15 -0
- data/templates/plugin-template/Rakefile +12 -0
- data/templates/plugin-template/plugin.gemspec +23 -0
- data/templates/plugin-template/plugin.txt +9 -0
- data/templates/plugin-template/targets/TARGET/cmd_tlm/cmd.txt +8 -0
- data/templates/plugin-template/targets/TARGET/cmd_tlm/tlm.txt +8 -0
- data/templates/plugin-template/targets/TARGET/lib/target.rb +10 -0
- data/templates/plugin-template/targets/TARGET/procedures/procedure.rb +3 -0
- data/templates/plugin-template/targets/TARGET/screens/status.txt +9 -0
- data/templates/plugin-template/targets/TARGET/target.txt +5 -0
- metadata +849 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
3
|
+
# Copyright 2022 Ball Aerospace & Technologies Corp.
|
|
4
|
+
# All Rights Reserved.
|
|
5
|
+
#
|
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
|
7
|
+
# under the terms of the GNU Affero General Public License
|
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
|
10
|
+
#
|
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
# GNU Affero General Public License for more details.
|
|
15
|
+
|
|
16
|
+
# Modified by OpenC3, Inc.
|
|
17
|
+
# All changes Copyright 2022, OpenC3, Inc.
|
|
18
|
+
# All Rights Reserved
|
|
19
|
+
|
|
20
|
+
require 'openc3/packets/binary_accessor'
|
|
21
|
+
require 'openc3/ext/openc3_io' if RUBY_ENGINE == 'ruby' and !ENV['OPENC3_NO_EXT']
|
|
22
|
+
|
|
23
|
+
# OpenC3 specific additions to the Ruby IO and StringIO classes
|
|
24
|
+
module OpenC3IO
|
|
25
|
+
if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
|
|
26
|
+
# Reads a length field and then return the String resulting from reading the
|
|
27
|
+
# number of bytes the length field indicates
|
|
28
|
+
#
|
|
29
|
+
# For example:
|
|
30
|
+
# io = StringIO.new
|
|
31
|
+
# # where io is "\x02\x01\x02\x03\x04...."
|
|
32
|
+
# result = io.read_length_bytes(1)
|
|
33
|
+
# # result will be "\x01x02" because the length field was given
|
|
34
|
+
# # to be 1 byte. We read 1 byte which is a 2. So we then read two
|
|
35
|
+
# # bytes and return.
|
|
36
|
+
#
|
|
37
|
+
# @param length_num_bytes [Integer] Number of bytes in the length field
|
|
38
|
+
# @return [String] A String of "length field" number of bytes
|
|
39
|
+
def read_length_bytes(length_num_bytes, max_read_size = nil)
|
|
40
|
+
return nil unless (length_num_bytes == 1) || (length_num_bytes == 2) or (length_num_bytes == 4)
|
|
41
|
+
|
|
42
|
+
# Read bytes for string length
|
|
43
|
+
temp_string_length = self.read(length_num_bytes)
|
|
44
|
+
return nil if (temp_string_length.nil?) || (temp_string_length.length != length_num_bytes)
|
|
45
|
+
|
|
46
|
+
string_length = OpenC3::BinaryAccessor.read(0, length_num_bytes * 8, :UINT, temp_string_length, :BIG_ENDIAN)
|
|
47
|
+
|
|
48
|
+
# Read String
|
|
49
|
+
return nil if max_read_size and string_length > max_read_size
|
|
50
|
+
|
|
51
|
+
string = self.read(string_length)
|
|
52
|
+
return nil if (string.nil?) || (string.length != string_length)
|
|
53
|
+
|
|
54
|
+
return string
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
# OpenC3 specific additions to the Ruby Range class
|
|
21
|
+
class Range
|
|
22
|
+
# @return [Array<Float>] Array of each value within the Range converted to
|
|
23
|
+
# Float
|
|
24
|
+
def to_a_to_f
|
|
25
|
+
collect { |value| value.to_f }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
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 'socket'
|
|
21
|
+
require 'resolv'
|
|
22
|
+
|
|
23
|
+
# OpenC3 specific additions to the Ruby Socket class
|
|
24
|
+
class Socket
|
|
25
|
+
# @return [String] The IP address of the current machine
|
|
26
|
+
def self.get_own_ip_address
|
|
27
|
+
Resolv.getaddress Socket.gethostname
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @param ip_address [String] IP address in the form xxx.xxx.xxx.xxx
|
|
31
|
+
# @return [String] The hostname of the given IP address or 'UNKNOWN' if the
|
|
32
|
+
# lookup fails
|
|
33
|
+
def self.lookup_hostname_from_ip(ip_address)
|
|
34
|
+
return Resolv.getname(ip_address)
|
|
35
|
+
rescue
|
|
36
|
+
return 'UNKNOWN'
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
3
|
+
# Copyright 2022 Ball Aerospace & Technologies Corp.
|
|
4
|
+
# All Rights Reserved.
|
|
5
|
+
#
|
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
|
7
|
+
# under the terms of the GNU Affero General Public License
|
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
|
10
|
+
#
|
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
# GNU Affero General Public License for more details.
|
|
15
|
+
|
|
16
|
+
# Modified by OpenC3, Inc.
|
|
17
|
+
# All changes Copyright 2022, OpenC3, Inc.
|
|
18
|
+
# All Rights Reserved
|
|
19
|
+
|
|
20
|
+
require 'openc3/packets/binary_accessor'
|
|
21
|
+
require 'openc3/ext/string' if RUBY_ENGINE == 'ruby' and !ENV['OPENC3_NO_EXT']
|
|
22
|
+
|
|
23
|
+
# OpenC3 specific additions to the Ruby String class
|
|
24
|
+
class String
|
|
25
|
+
# The printable range of ASCII characters
|
|
26
|
+
PRINTABLE_RANGE = 32..126
|
|
27
|
+
# Regular expression to identify a character that is not in the printable range
|
|
28
|
+
NON_PRINTABLE_REGEX = /[^\s -~]/
|
|
29
|
+
# Regular expression to identify a String as a floating point number
|
|
30
|
+
FLOAT_CHECK_REGEX = /\A\s*[-+]?\d*\.\d+\s*\z/
|
|
31
|
+
# Regular expression to identify a String as a floating point number in
|
|
32
|
+
# scientific notation
|
|
33
|
+
SCIENTIFIC_CHECK_REGEX = /\A\s*[-+]?(\d+((\.\d+)?)|(\.\d+))[eE][-+]?\d+\s*\z/
|
|
34
|
+
# Regular expression to identify a String as an integer
|
|
35
|
+
INT_CHECK_REGEX = /\A\s*[-+]?\d+\s*\z/
|
|
36
|
+
# Regular expression to identify a String as an integer in hexadecimal format
|
|
37
|
+
HEX_CHECK_REGEX = /\A\s*0[xX][\dabcdefABCDEF]+\s*\z/
|
|
38
|
+
# Regular expression to identify a String as an Array of numbers
|
|
39
|
+
ARRAY_CHECK_REGEX = /\A\s*\[.*\]\s*\z/
|
|
40
|
+
|
|
41
|
+
# Displays a String containing binary data in a human readable format by
|
|
42
|
+
# converting each byte to the hex representation.
|
|
43
|
+
#
|
|
44
|
+
# @param word_size [Integer] How many bytes compose a word. Words are grouped
|
|
45
|
+
# together without spaces in between
|
|
46
|
+
# @param words_per_line [Integer] The number of words to display on a single
|
|
47
|
+
# formatted line
|
|
48
|
+
# @param word_separator [String] The string to place between words
|
|
49
|
+
# @param indent [Integer] The amount of spaces to put in front of each
|
|
50
|
+
# formatted line
|
|
51
|
+
# @param show_address [Boolean] Whether to show the hex address of the first
|
|
52
|
+
# byte in the formatted output
|
|
53
|
+
# @param address_separator [String] The string to put after the hex address.
|
|
54
|
+
# Only used if show_address is true.
|
|
55
|
+
# @param show_ascii [Boolean] Whether to interpret the binary data as ASCII
|
|
56
|
+
# characters and display the printable characters to the right of the
|
|
57
|
+
# formatted line
|
|
58
|
+
# @param ascii_separator [String] The string to put between the formatted
|
|
59
|
+
# line and the ASCII characters. Only used if show_ascii is true.
|
|
60
|
+
# @param unprintable_character [String] The string to output when data in the
|
|
61
|
+
# binary String does not result in a printable ASCII character. Only used if
|
|
62
|
+
# show_ascii is true.
|
|
63
|
+
# @param line_separator [String] The string used to end a line. Normaly newline.
|
|
64
|
+
def formatted(
|
|
65
|
+
word_size = 1,
|
|
66
|
+
words_per_line = 16,
|
|
67
|
+
word_separator = ' ',
|
|
68
|
+
indent = 0,
|
|
69
|
+
show_address = true,
|
|
70
|
+
address_separator = ': ',
|
|
71
|
+
show_ascii = true,
|
|
72
|
+
ascii_separator = ' ',
|
|
73
|
+
unprintable_character = ' ',
|
|
74
|
+
line_separator = "\n"
|
|
75
|
+
)
|
|
76
|
+
string = ''
|
|
77
|
+
byte_offset = 0
|
|
78
|
+
bytes_per_line = word_size * words_per_line
|
|
79
|
+
indent_string = ' ' * indent
|
|
80
|
+
ascii_line = ''
|
|
81
|
+
|
|
82
|
+
self.each_byte do |byte|
|
|
83
|
+
if byte_offset % bytes_per_line == 0
|
|
84
|
+
# Create the indentation at the beginning of each line
|
|
85
|
+
string << indent_string
|
|
86
|
+
|
|
87
|
+
# Add the address if requested
|
|
88
|
+
string << sprintf("%08X%s", byte_offset, address_separator) if show_address
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Add the byte
|
|
92
|
+
string << sprintf("%02X", byte)
|
|
93
|
+
|
|
94
|
+
# Create the ASCII representation if requested
|
|
95
|
+
if show_ascii
|
|
96
|
+
if PRINTABLE_RANGE.include?(byte)
|
|
97
|
+
ascii_line << [byte].pack('C')
|
|
98
|
+
else
|
|
99
|
+
ascii_line << unprintable_character
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Move to next byte
|
|
104
|
+
byte_offset += 1
|
|
105
|
+
|
|
106
|
+
# If we're at the end of the line we output the ascii if requested
|
|
107
|
+
if byte_offset % bytes_per_line == 0
|
|
108
|
+
if show_ascii
|
|
109
|
+
string << "#{ascii_separator}#{ascii_line}"
|
|
110
|
+
ascii_line = ''
|
|
111
|
+
end
|
|
112
|
+
string << line_separator
|
|
113
|
+
|
|
114
|
+
# If we're at a word junction then output the word_separator
|
|
115
|
+
elsif (byte_offset % word_size == 0) and byte_offset != self.length
|
|
116
|
+
string << word_separator
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# We're done printing all the bytes. Now check to see if we ended in the
|
|
121
|
+
# middle of a line. If so we have to print out the final ASCII if
|
|
122
|
+
# requested.
|
|
123
|
+
if byte_offset % bytes_per_line != 0
|
|
124
|
+
if show_ascii
|
|
125
|
+
num_word_separators = ((byte_offset % bytes_per_line) - 1) / word_size
|
|
126
|
+
existing_length = (num_word_separators * word_separator.length) + ((byte_offset % bytes_per_line) * 2)
|
|
127
|
+
full_line_length = (bytes_per_line * 2) + ((words_per_line - 1) * word_separator.length)
|
|
128
|
+
filler = ' ' * (full_line_length - existing_length)
|
|
129
|
+
ascii_filler = ' ' * (bytes_per_line - ascii_line.length)
|
|
130
|
+
string << "#{filler}#{ascii_separator}#{ascii_line}#{ascii_filler}"
|
|
131
|
+
ascii_line = ''
|
|
132
|
+
end
|
|
133
|
+
string << line_separator
|
|
134
|
+
end
|
|
135
|
+
string
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Displays a String containing binary data in a human readable format by
|
|
139
|
+
# converting each byte to the hex representation.
|
|
140
|
+
# Simply formatted as a single string of bytes
|
|
141
|
+
def simple_formatted
|
|
142
|
+
string = ''
|
|
143
|
+
self.each_byte do |byte|
|
|
144
|
+
string << sprintf("%02X", byte)
|
|
145
|
+
end
|
|
146
|
+
string
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Uses the String each_line method to interate through the lines and removes
|
|
150
|
+
# the line specified.
|
|
151
|
+
#
|
|
152
|
+
# @param line_number [Integer] The line to remove from the string (1 based)
|
|
153
|
+
# @param separator [String] The record separator to pass to #each_line
|
|
154
|
+
# ($/ by default is the newline character)
|
|
155
|
+
# @return [String] A new string with the line removed
|
|
156
|
+
def remove_line(line_number, separator = $/)
|
|
157
|
+
new_string = ''
|
|
158
|
+
index = 1
|
|
159
|
+
self.each_line(separator) do |line|
|
|
160
|
+
new_string << line unless index == line_number
|
|
161
|
+
index += 1
|
|
162
|
+
end
|
|
163
|
+
new_string
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# @return [Integer] The number of lines in the string (as split by the newline
|
|
167
|
+
# character)
|
|
168
|
+
def num_lines
|
|
169
|
+
value = self.count("\n")
|
|
170
|
+
value += 1 if self[-1..-1] and self[-1..-1] != "\n"
|
|
171
|
+
value
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
|
|
175
|
+
# @return [String] The string with leading and trailing quotes removed
|
|
176
|
+
def remove_quotes
|
|
177
|
+
return self if self.length < 2
|
|
178
|
+
|
|
179
|
+
first_char = self[0]
|
|
180
|
+
return self if (first_char != '"') && (first_char != "'")
|
|
181
|
+
|
|
182
|
+
last_char = self[-1]
|
|
183
|
+
return self if first_char != last_char
|
|
184
|
+
|
|
185
|
+
return self[1..-2]
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# @return [Boolean] Whether the String represents a floating point number
|
|
190
|
+
def is_float?
|
|
191
|
+
if self =~ FLOAT_CHECK_REGEX or self =~ SCIENTIFIC_CHECK_REGEX then true else false end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# @return [Boolean] Whether the String represents an integer
|
|
195
|
+
def is_int?
|
|
196
|
+
if INT_CHECK_REGEX.match?(self) then true else false end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# @return [Boolean] Whether the String represents a hexadecimal number
|
|
200
|
+
def is_hex?
|
|
201
|
+
if HEX_CHECK_REGEX.match?(self) then true else false end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# @return [Boolean] Whether the String represents an Array
|
|
205
|
+
def is_array?
|
|
206
|
+
if ARRAY_CHECK_REGEX.match?(self) then true else false end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# @return [Boolean] Whether the string contains only printable characters
|
|
210
|
+
def is_printable?
|
|
211
|
+
if NON_PRINTABLE_REGEX.match?(self) then false else true end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# @return Converts the String into either a Float, Integer, or Array
|
|
215
|
+
# depending on what the String represents. It can successfully convert
|
|
216
|
+
# floating point numbers in both fixed and scientific notation, integers
|
|
217
|
+
# in hexadecimal notation, and Arrays. If it can't be converted into
|
|
218
|
+
# any of the above then the original String is returned.
|
|
219
|
+
def convert_to_value
|
|
220
|
+
return_value = self
|
|
221
|
+
begin
|
|
222
|
+
upcase_self = self.upcase
|
|
223
|
+
if upcase_self == 'INFINITY'.freeze
|
|
224
|
+
return_value = Float::INFINITY
|
|
225
|
+
elsif upcase_self == '-INFINITY'.freeze
|
|
226
|
+
return_value = -Float::INFINITY
|
|
227
|
+
elsif upcase_self == 'NAN'.freeze
|
|
228
|
+
return_value = Float::NAN
|
|
229
|
+
elsif self.is_float?
|
|
230
|
+
# Floating Point in normal or scientific notation
|
|
231
|
+
return_value = self.to_f
|
|
232
|
+
elsif self.is_int?
|
|
233
|
+
# Integer
|
|
234
|
+
return_value = self.to_i
|
|
235
|
+
elsif self.is_hex?
|
|
236
|
+
# Hex
|
|
237
|
+
return_value = Integer(self)
|
|
238
|
+
elsif self.is_array?
|
|
239
|
+
# Array
|
|
240
|
+
return_value = eval(self)
|
|
241
|
+
end
|
|
242
|
+
rescue Exception
|
|
243
|
+
# Something went wrong so just return the string as is
|
|
244
|
+
end
|
|
245
|
+
return return_value
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Converts the String representing a hexadecimal number (i.e. "0xABCD")
|
|
249
|
+
# to a binary String with the same data (i.e "\xAB\xCD")
|
|
250
|
+
#
|
|
251
|
+
# @return [String] Binary byte string
|
|
252
|
+
def hex_to_byte_string
|
|
253
|
+
string = self.dup
|
|
254
|
+
|
|
255
|
+
# Remove leading 0x or 0X
|
|
256
|
+
if string[0..1] == '0x' or string[0..1] == '0X'
|
|
257
|
+
string = string[2..-1]
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
length = string.length
|
|
261
|
+
length += 1 unless (length % 2) == 0
|
|
262
|
+
|
|
263
|
+
array = []
|
|
264
|
+
(length / 2).times do
|
|
265
|
+
# Grab last two characters
|
|
266
|
+
if string.length >= 2
|
|
267
|
+
last_two_characters = string[-2..-1]
|
|
268
|
+
string = string[0..-3]
|
|
269
|
+
else
|
|
270
|
+
last_two_characters = string[0..0]
|
|
271
|
+
string = ''
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
int_value = Integer('0x' + last_two_characters)
|
|
275
|
+
|
|
276
|
+
array.unshift(int_value)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
array.pack("C*")
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Converts a String representing a class (i.e. "MyGreatClass") to a Ruby
|
|
283
|
+
# filename which implements the class (i.e. "my_great_class.rb").
|
|
284
|
+
#
|
|
285
|
+
# @param include_extension [Boolean] Whether to add '.rb' extension
|
|
286
|
+
# @return [String] Filename which implements the class name
|
|
287
|
+
def class_name_to_filename(include_extension = true)
|
|
288
|
+
string = self.split("::")[-1] # Remove any namespacing
|
|
289
|
+
filename = ''
|
|
290
|
+
length = string.length
|
|
291
|
+
length.times do |index|
|
|
292
|
+
filename << '_' if index != 0 and string[index..index] == string[index..index].upcase
|
|
293
|
+
filename << string[index..index].downcase
|
|
294
|
+
end
|
|
295
|
+
filename << '.rb' if include_extension
|
|
296
|
+
filename
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Converts a String representing a filename (i.e. "my_great_class.rb") to a Ruby
|
|
300
|
+
# class name (i.e. "MyGreatClass").
|
|
301
|
+
#
|
|
302
|
+
# @return [String] Class name associated with the filename
|
|
303
|
+
def filename_to_class_name
|
|
304
|
+
filename = File.basename(self)
|
|
305
|
+
class_name = ''
|
|
306
|
+
length = filename.length
|
|
307
|
+
upcase_next = true
|
|
308
|
+
length.times do |index|
|
|
309
|
+
break if filename[index..index] == '.'
|
|
310
|
+
|
|
311
|
+
if filename[index..index] == '_'
|
|
312
|
+
upcase_next = true
|
|
313
|
+
elsif upcase_next
|
|
314
|
+
class_name << filename[index..index].upcase
|
|
315
|
+
upcase_next = false
|
|
316
|
+
else
|
|
317
|
+
class_name << filename[index..index].downcase
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
class_name
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Converts a String representing a class (i.e. "MyGreatClass") to the actual
|
|
324
|
+
# class that has been required and is present in the Ruby runtime.
|
|
325
|
+
#
|
|
326
|
+
# @return [Class]
|
|
327
|
+
def to_class
|
|
328
|
+
klass = nil
|
|
329
|
+
split_self = self.split('::')
|
|
330
|
+
if split_self.length > 1
|
|
331
|
+
split_self.each do |class_name|
|
|
332
|
+
if klass
|
|
333
|
+
klass = klass.const_get(class_name)
|
|
334
|
+
else
|
|
335
|
+
klass = Object.const_get(class_name)
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
else
|
|
339
|
+
begin
|
|
340
|
+
klass = OpenC3.const_get(self)
|
|
341
|
+
rescue
|
|
342
|
+
begin
|
|
343
|
+
klass = Object.const_get(self)
|
|
344
|
+
rescue
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
klass
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# Adds quotes if the string contains whitespace
|
|
352
|
+
#
|
|
353
|
+
# @param quote_char [String] The quote character to add if necessary
|
|
354
|
+
# @return [String] quoted string if necessary
|
|
355
|
+
def quote_if_necessary(quote_char = '"')
|
|
356
|
+
if /\s/.match?(self)
|
|
357
|
+
return quote_char + self + quote_char
|
|
358
|
+
else
|
|
359
|
+
return self
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Converts a string to UTF-8 and returns a new string
|
|
364
|
+
# Assumes the string is Windows-1252 encoded if marked ASCII-8BIT and not UTF-8 compatible
|
|
365
|
+
#
|
|
366
|
+
# @return [String] UTF-8 encoded string
|
|
367
|
+
def to_utf8
|
|
368
|
+
self.dup.to_utf8!
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
# Converts a string to UTF-8 in place
|
|
372
|
+
# Assumes the string is Windows-1252 encoded if marked ASCII-8BIT and not UTF-8 compatible
|
|
373
|
+
#
|
|
374
|
+
# @return [String] UTF-8 encoded string
|
|
375
|
+
def to_utf8!
|
|
376
|
+
if self.encoding == Encoding::ASCII_8BIT
|
|
377
|
+
if self.force_encoding('UTF-8').valid_encoding?
|
|
378
|
+
return self
|
|
379
|
+
else
|
|
380
|
+
# Note: this will replace any characters without a valid conversion with space (shouldn't be possible from Windows-1252)
|
|
381
|
+
return self.force_encoding('Windows-1252').encode!('UTF-8', invalid: :replace, undef: :replace, replace: ' ')
|
|
382
|
+
end
|
|
383
|
+
else
|
|
384
|
+
# Note: this will replace any characters without a valid conversion with space
|
|
385
|
+
self.encode!('UTF-8', invalid: :replace, undef: :replace, replace: ' ')
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
end # class String
|
|
@@ -0,0 +1,33 @@
|
|
|
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 'stringio'
|
|
21
|
+
require 'openc3/core_ext/openc3_io'
|
|
22
|
+
|
|
23
|
+
# OpenC3 specific additions to the Ruby IO class
|
|
24
|
+
class StringIO
|
|
25
|
+
include OpenC3IO
|
|
26
|
+
|
|
27
|
+
if !(StringIO.method_defined?(:path))
|
|
28
|
+
# @return [nil]
|
|
29
|
+
def path
|
|
30
|
+
nil
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|