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,532 @@
|
|
|
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/microservices/microservice'
|
|
21
|
+
require 'openc3/models/interface_model'
|
|
22
|
+
require 'openc3/models/router_model'
|
|
23
|
+
require 'openc3/models/interface_status_model'
|
|
24
|
+
require 'openc3/models/router_status_model'
|
|
25
|
+
require 'openc3/topics/telemetry_topic'
|
|
26
|
+
require 'openc3/topics/command_topic'
|
|
27
|
+
require 'openc3/topics/command_decom_topic'
|
|
28
|
+
require 'openc3/topics/interface_topic'
|
|
29
|
+
require 'openc3/topics/router_topic'
|
|
30
|
+
|
|
31
|
+
module OpenC3
|
|
32
|
+
class InterfaceCmdHandlerThread
|
|
33
|
+
def initialize(interface, tlm, scope:)
|
|
34
|
+
@interface = interface
|
|
35
|
+
@tlm = tlm
|
|
36
|
+
@scope = scope
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def start
|
|
40
|
+
@thread = Thread.new do
|
|
41
|
+
run()
|
|
42
|
+
rescue Exception => err
|
|
43
|
+
Logger.error "#{@interface.name}: Command handler thread died: #{err.formatted}"
|
|
44
|
+
raise err
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def stop
|
|
49
|
+
OpenC3.kill_thread(self, @thread)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def graceful_kill
|
|
53
|
+
InterfaceTopic.shutdown(@interface, scope: @scope)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def run
|
|
57
|
+
InterfaceTopic.receive_commands(@interface, scope: @scope) do |topic, msg_hash|
|
|
58
|
+
# Check for a raw write to the interface
|
|
59
|
+
if topic =~ /CMD}INTERFACE/
|
|
60
|
+
if msg_hash['shutdown']
|
|
61
|
+
Logger.info "#{@interface.name}: Shutdown requested"
|
|
62
|
+
return
|
|
63
|
+
end
|
|
64
|
+
if msg_hash['connect']
|
|
65
|
+
Logger.info "#{@interface.name}: Connect requested"
|
|
66
|
+
@tlm.attempting()
|
|
67
|
+
next 'SUCCESS'
|
|
68
|
+
end
|
|
69
|
+
if msg_hash['disconnect']
|
|
70
|
+
Logger.info "#{@interface.name}: Disconnect requested"
|
|
71
|
+
@tlm.disconnect(false)
|
|
72
|
+
next 'SUCCESS'
|
|
73
|
+
end
|
|
74
|
+
if msg_hash['raw']
|
|
75
|
+
Logger.info "#{@interface.name}: Write raw"
|
|
76
|
+
# A raw interface write results in an UNKNOWN packet
|
|
77
|
+
command = System.commands.packet('UNKNOWN', 'UNKNOWN')
|
|
78
|
+
command.received_count += 1
|
|
79
|
+
command = command.clone
|
|
80
|
+
command.buffer = msg_hash['raw']
|
|
81
|
+
command.received_time = Time.now
|
|
82
|
+
CommandTopic.write_packet(command, scope: @scope)
|
|
83
|
+
@interface.write_raw(msg_hash['raw'])
|
|
84
|
+
next 'SUCCESS'
|
|
85
|
+
end
|
|
86
|
+
if msg_hash.key?('log_raw')
|
|
87
|
+
if msg_hash['log_raw'] == 'true'
|
|
88
|
+
Logger.info "#{@interface.name}: Enable raw logging"
|
|
89
|
+
@interface.start_raw_logging
|
|
90
|
+
else
|
|
91
|
+
Logger.info "#{@interface.name}: Disable raw logging"
|
|
92
|
+
@interface.stop_raw_logging
|
|
93
|
+
end
|
|
94
|
+
next 'SUCCESS'
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
target_name = msg_hash['target_name']
|
|
99
|
+
cmd_name = msg_hash['cmd_name']
|
|
100
|
+
cmd_params = nil
|
|
101
|
+
cmd_buffer = nil
|
|
102
|
+
hazardous_check = nil
|
|
103
|
+
if msg_hash['cmd_params']
|
|
104
|
+
cmd_params = JSON.parse(msg_hash['cmd_params'], :allow_nan => true, :create_additions => true)
|
|
105
|
+
range_check = ConfigParser.handle_true_false(msg_hash['range_check'])
|
|
106
|
+
raw = ConfigParser.handle_true_false(msg_hash['raw'])
|
|
107
|
+
hazardous_check = ConfigParser.handle_true_false(msg_hash['hazardous_check'])
|
|
108
|
+
elsif msg_hash['cmd_buffer']
|
|
109
|
+
cmd_buffer = msg_hash['cmd_buffer']
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
begin
|
|
113
|
+
begin
|
|
114
|
+
if cmd_params
|
|
115
|
+
command = System.commands.build_cmd(target_name, cmd_name, cmd_params, range_check, raw)
|
|
116
|
+
elsif cmd_buffer
|
|
117
|
+
if target_name
|
|
118
|
+
command = System.commands.identify(cmd_buffer, [target_name])
|
|
119
|
+
else
|
|
120
|
+
command = System.commands.identify(cmd_buffer, @target_names)
|
|
121
|
+
end
|
|
122
|
+
unless command
|
|
123
|
+
command = System.commands.packet('UNKNOWN', 'UNKNOWN')
|
|
124
|
+
command.received_count += 1
|
|
125
|
+
command = command.clone
|
|
126
|
+
command.buffer = cmd_buffer
|
|
127
|
+
end
|
|
128
|
+
else
|
|
129
|
+
raise "Invalid command received:\n #{msg_hash}"
|
|
130
|
+
end
|
|
131
|
+
command.received_time = Time.now
|
|
132
|
+
rescue => e
|
|
133
|
+
Logger.error "#{@interface.name}: #{msg_hash}"
|
|
134
|
+
Logger.error "#{@interface.name}: #{e.formatted}"
|
|
135
|
+
next e.message
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
if hazardous_check
|
|
139
|
+
hazardous, hazardous_description = System.commands.cmd_pkt_hazardous?(command)
|
|
140
|
+
# Return back the error, description, and the formatted command
|
|
141
|
+
# This allows the error handler to simply re-send the command
|
|
142
|
+
next "HazardousError\n#{hazardous_description}\n#{System.commands.format(command)}" if hazardous
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
begin
|
|
146
|
+
@interface.write(command)
|
|
147
|
+
CommandTopic.write_packet(command, scope: @scope)
|
|
148
|
+
CommandDecomTopic.write_packet(command, scope: @scope)
|
|
149
|
+
InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
|
|
150
|
+
next 'SUCCESS'
|
|
151
|
+
rescue => e
|
|
152
|
+
Logger.error "#{@interface.name}: #{e.formatted}"
|
|
153
|
+
next e.message
|
|
154
|
+
end
|
|
155
|
+
rescue => e
|
|
156
|
+
Logger.error "#{@interface.name}: #{e.formatted}"
|
|
157
|
+
next e.message
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
class RouterTlmHandlerThread
|
|
164
|
+
def initialize(router, tlm, scope:)
|
|
165
|
+
@router = router
|
|
166
|
+
@tlm = tlm
|
|
167
|
+
@scope = scope
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def start
|
|
171
|
+
@thread = Thread.new do
|
|
172
|
+
run()
|
|
173
|
+
rescue Exception => err
|
|
174
|
+
Logger.error "#{@router.name}: Telemetry handler thread died: #{err.formatted}"
|
|
175
|
+
raise err
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def stop
|
|
180
|
+
OpenC3.kill_thread(self, @thread)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def graceful_kill
|
|
184
|
+
RouterTopic.shutdown(@router, scope: @scope)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def run
|
|
188
|
+
RouterTopic.receive_telemetry(@router, scope: @scope) do |topic, msg_hash|
|
|
189
|
+
# Check for commands to the router itself
|
|
190
|
+
if /CMD}ROUTER/.match?(topic)
|
|
191
|
+
if msg_hash['shutdown']
|
|
192
|
+
Logger.info "#{@router.name}: Shutdown requested"
|
|
193
|
+
return
|
|
194
|
+
end
|
|
195
|
+
if msg_hash['connect']
|
|
196
|
+
Logger.info "#{@router.name}: Connect requested"
|
|
197
|
+
@tlm.attempting()
|
|
198
|
+
end
|
|
199
|
+
if msg_hash['disconnect']
|
|
200
|
+
Logger.info "#{@router.name}: Disconnect requested"
|
|
201
|
+
@tlm.disconnect(false)
|
|
202
|
+
end
|
|
203
|
+
if msg_hash.key?('log_raw')
|
|
204
|
+
if msg_hash['log_raw'] == 'true'
|
|
205
|
+
Logger.info "#{@router.name}: Enable raw logging"
|
|
206
|
+
@router.start_raw_logging
|
|
207
|
+
else
|
|
208
|
+
Logger.info "#{@router.name}: Disable raw logging"
|
|
209
|
+
@router.stop_raw_logging
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
next 'SUCCESS'
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
if @router.connected?
|
|
216
|
+
target_name = msg_hash["target_name"]
|
|
217
|
+
packet_name = msg_hash["packet_name"]
|
|
218
|
+
|
|
219
|
+
packet = System.telemetry.packet(target_name, packet_name)
|
|
220
|
+
packet.stored = ConfigParser.handle_true_false(msg_hash["stored"])
|
|
221
|
+
packet.received_time = Time.from_nsec_from_epoch(msg_hash["time"].to_i)
|
|
222
|
+
packet.received_count = msg_hash["received_count"].to_i
|
|
223
|
+
packet.buffer = msg_hash["buffer"]
|
|
224
|
+
|
|
225
|
+
begin
|
|
226
|
+
@router.write(packet)
|
|
227
|
+
RouterStatusModel.set(@router.as_json(:allow_nan => true), scope: @scope)
|
|
228
|
+
next 'SUCCESS'
|
|
229
|
+
rescue => e
|
|
230
|
+
Logger.error "#{@router.name}: #{e.formatted}"
|
|
231
|
+
next e.message
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
class InterfaceMicroservice < Microservice
|
|
239
|
+
UNKNOWN_BYTES_TO_PRINT = 16
|
|
240
|
+
|
|
241
|
+
def initialize(name)
|
|
242
|
+
super(name)
|
|
243
|
+
@interface_or_router = self.class.name.to_s.split("Microservice")[0].upcase.split("::")[-1]
|
|
244
|
+
@scope = name.split("__")[0]
|
|
245
|
+
interface_name = name.split("__")[2]
|
|
246
|
+
if @interface_or_router == 'INTERFACE'
|
|
247
|
+
@interface = InterfaceModel.get_model(name: interface_name, scope: @scope).build
|
|
248
|
+
else
|
|
249
|
+
@interface = RouterModel.get_model(name: interface_name, scope: @scope).build
|
|
250
|
+
end
|
|
251
|
+
@interface.name = interface_name
|
|
252
|
+
# Map the interface to the interface's targets
|
|
253
|
+
@interface.target_names do |target_name|
|
|
254
|
+
target = System.targets[target_name]
|
|
255
|
+
target.interface = @interface
|
|
256
|
+
end
|
|
257
|
+
if @interface.connect_on_startup
|
|
258
|
+
@interface.state = 'ATTEMPTING'
|
|
259
|
+
else
|
|
260
|
+
@interface.state = 'DISCONNECTED'
|
|
261
|
+
end
|
|
262
|
+
if @interface_or_router == 'INTERFACE'
|
|
263
|
+
InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
|
|
264
|
+
else
|
|
265
|
+
RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
@interface_thread_sleeper = Sleeper.new
|
|
269
|
+
@cancel_thread = false
|
|
270
|
+
@connection_failed_messages = []
|
|
271
|
+
@connection_lost_messages = []
|
|
272
|
+
@mutex = Mutex.new
|
|
273
|
+
if @interface_or_router == 'INTERFACE'
|
|
274
|
+
@handler_thread = InterfaceCmdHandlerThread.new(@interface, self, scope: @scope)
|
|
275
|
+
else
|
|
276
|
+
@handler_thread = RouterTlmHandlerThread.new(@interface, self, scope: @scope)
|
|
277
|
+
end
|
|
278
|
+
@handler_thread.start
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# External method to be called by the InterfaceCmdHandlerThread to connect
|
|
282
|
+
# Thus we just set the state and allow the run method to handle the action
|
|
283
|
+
def attempting
|
|
284
|
+
@interface.state = 'ATTEMPTING'
|
|
285
|
+
if @interface_or_router == 'INTERFACE'
|
|
286
|
+
InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
|
|
287
|
+
else
|
|
288
|
+
RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def run
|
|
293
|
+
begin
|
|
294
|
+
if @interface.read_allowed?
|
|
295
|
+
Logger.info "#{@interface.name}: Starting packet reading"
|
|
296
|
+
else
|
|
297
|
+
Logger.info "#{@interface.name}: Starting connection maintenance"
|
|
298
|
+
end
|
|
299
|
+
while true
|
|
300
|
+
break if @cancel_thread
|
|
301
|
+
|
|
302
|
+
case @interface.state
|
|
303
|
+
when 'DISCONNECTED'
|
|
304
|
+
begin
|
|
305
|
+
# Just wait to see if we should connect later
|
|
306
|
+
@interface_thread_sleeper.sleep(1)
|
|
307
|
+
rescue Exception => err
|
|
308
|
+
break if @cancel_thread
|
|
309
|
+
end
|
|
310
|
+
when 'ATTEMPTING'
|
|
311
|
+
begin
|
|
312
|
+
@mutex.synchronize do
|
|
313
|
+
# We need to make sure connect is not called after stop() has been called
|
|
314
|
+
connect() unless @cancel_thread
|
|
315
|
+
end
|
|
316
|
+
rescue Exception => connect_error
|
|
317
|
+
handle_connection_failed(connect_error)
|
|
318
|
+
break if @cancel_thread
|
|
319
|
+
end
|
|
320
|
+
when 'CONNECTED'
|
|
321
|
+
if @interface.read_allowed?
|
|
322
|
+
begin
|
|
323
|
+
packet = @interface.read
|
|
324
|
+
if packet
|
|
325
|
+
handle_packet(packet)
|
|
326
|
+
@count += 1
|
|
327
|
+
else
|
|
328
|
+
Logger.info "#{@interface.name}: Internal disconnect requested (returned nil)"
|
|
329
|
+
handle_connection_lost()
|
|
330
|
+
break if @cancel_thread
|
|
331
|
+
end
|
|
332
|
+
rescue Exception => err
|
|
333
|
+
handle_connection_lost(err)
|
|
334
|
+
break if @cancel_thread
|
|
335
|
+
end
|
|
336
|
+
else
|
|
337
|
+
@interface_thread_sleeper.sleep(1)
|
|
338
|
+
handle_connection_lost() if !@interface.connected?
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
rescue Exception => error
|
|
343
|
+
Logger.error "#{@interface.name}: Packet reading thread died: #{error.formatted}"
|
|
344
|
+
OpenC3.handle_fatal_exception(error)
|
|
345
|
+
# Try to do clean disconnect because we're going down
|
|
346
|
+
disconnect(false)
|
|
347
|
+
end
|
|
348
|
+
if @interface_or_router == 'INTERFACE'
|
|
349
|
+
InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
|
|
350
|
+
else
|
|
351
|
+
RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
|
|
352
|
+
end
|
|
353
|
+
Logger.info "#{@interface.name}: Stopped packet reading"
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def handle_packet(packet)
|
|
357
|
+
InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
|
|
358
|
+
packet.received_time = Time.now.sys unless packet.received_time
|
|
359
|
+
|
|
360
|
+
if packet.stored
|
|
361
|
+
# Stored telemetry does not update the current value table
|
|
362
|
+
identified_packet = System.telemetry.identify_and_define_packet(packet, @target_names)
|
|
363
|
+
else
|
|
364
|
+
# Identify and update packet
|
|
365
|
+
if packet.identified?
|
|
366
|
+
begin
|
|
367
|
+
# Preidentifed packet - place it into the current value table
|
|
368
|
+
identified_packet = System.telemetry.update!(packet.target_name,
|
|
369
|
+
packet.packet_name,
|
|
370
|
+
packet.buffer)
|
|
371
|
+
rescue RuntimeError
|
|
372
|
+
# Packet identified but we don't know about it
|
|
373
|
+
# Clear packet_name and target_name and try to identify
|
|
374
|
+
Logger.warn "#{@interface.name}: Received unknown identified telemetry: #{packet.target_name} #{packet.packet_name}"
|
|
375
|
+
packet.target_name = nil
|
|
376
|
+
packet.packet_name = nil
|
|
377
|
+
identified_packet = System.telemetry.identify!(packet.buffer,
|
|
378
|
+
@target_names)
|
|
379
|
+
end
|
|
380
|
+
else
|
|
381
|
+
# Packet needs to be identified
|
|
382
|
+
identified_packet = System.telemetry.identify!(packet.buffer,
|
|
383
|
+
@target_names)
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
if identified_packet
|
|
388
|
+
identified_packet.received_time = packet.received_time
|
|
389
|
+
identified_packet.stored = packet.stored
|
|
390
|
+
identified_packet.extra = packet.extra
|
|
391
|
+
packet = identified_packet
|
|
392
|
+
else
|
|
393
|
+
unknown_packet = System.telemetry.update!('UNKNOWN', 'UNKNOWN', packet.buffer)
|
|
394
|
+
unknown_packet.received_time = packet.received_time
|
|
395
|
+
unknown_packet.stored = packet.stored
|
|
396
|
+
unknown_packet.extra = packet.extra
|
|
397
|
+
packet = unknown_packet
|
|
398
|
+
json_hash = CvtModel.build_json_from_packet(packet)
|
|
399
|
+
CvtModel.set(json_hash, target_name: packet.target_name, packet_name: packet.packet_name, scope: scope)
|
|
400
|
+
num_bytes_to_print = [UNKNOWN_BYTES_TO_PRINT, packet.length].min
|
|
401
|
+
data = packet.buffer(false)[0..(num_bytes_to_print - 1)]
|
|
402
|
+
prefix = data.each_byte.map { | byte | sprintf("%02X", byte) }.join()
|
|
403
|
+
Logger.warn "#{@interface.name} #{packet.target_name} packet length: #{packet.length} starting with: #{prefix}"
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
# Write to stream
|
|
407
|
+
packet.received_count += 1
|
|
408
|
+
TelemetryTopic.write_packet(packet, scope: @scope)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def handle_connection_failed(connect_error)
|
|
412
|
+
@error = connect_error
|
|
413
|
+
Logger.error "#{@interface.name}: Connection Failed: #{connect_error.formatted(false, false)}"
|
|
414
|
+
case connect_error
|
|
415
|
+
when Interrupt
|
|
416
|
+
Logger.info "#{@interface.name}: Closing from signal"
|
|
417
|
+
@cancel_thread = true
|
|
418
|
+
when Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::ENOTSOCK, Errno::EHOSTUNREACH, IOError
|
|
419
|
+
# Do not write an exception file for these extremely common cases
|
|
420
|
+
else
|
|
421
|
+
if RuntimeError === connect_error and (connect_error.message =~ /canceled/ or connect_error.message =~ /timeout/)
|
|
422
|
+
# Do not write an exception file for these extremely common cases
|
|
423
|
+
else
|
|
424
|
+
Logger.error "#{@interface.name}: #{connect_error.formatted}"
|
|
425
|
+
unless @connection_failed_messages.include?(connect_error.message)
|
|
426
|
+
OpenC3.write_exception_file(connect_error)
|
|
427
|
+
@connection_failed_messages << connect_error.message
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
disconnect() # Ensure we do a clean disconnect
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def handle_connection_lost(err = nil, reconnect: true)
|
|
435
|
+
if err
|
|
436
|
+
@error = err
|
|
437
|
+
Logger.info "#{@interface.name}: Connection Lost: #{err.formatted(false, false)}"
|
|
438
|
+
case err
|
|
439
|
+
when Interrupt
|
|
440
|
+
Logger.info "#{@interface.name}: Closing from signal"
|
|
441
|
+
@cancel_thread = true
|
|
442
|
+
when Errno::ECONNABORTED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EBADF, Errno::ENOTSOCK, IOError
|
|
443
|
+
# Do not write an exception file for these extremely common cases
|
|
444
|
+
else
|
|
445
|
+
Logger.error "#{@interface.name}: #{err.formatted}"
|
|
446
|
+
unless @connection_lost_messages.include?(err.message)
|
|
447
|
+
OpenC3.write_exception_file(err)
|
|
448
|
+
@connection_lost_messages << err.message
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
else
|
|
452
|
+
Logger.info "#{@interface.name}: Connection Lost"
|
|
453
|
+
end
|
|
454
|
+
disconnect(reconnect) # Ensure we do a clean disconnect
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def connect
|
|
458
|
+
Logger.info "#{@interface.name}: Connecting ..."
|
|
459
|
+
@interface.connect
|
|
460
|
+
@interface.state = 'CONNECTED'
|
|
461
|
+
if @interface_or_router == 'INTERFACE'
|
|
462
|
+
InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
|
|
463
|
+
else
|
|
464
|
+
RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
|
|
465
|
+
end
|
|
466
|
+
Logger.info "#{@interface.name}: Connection Success"
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
def disconnect(allow_reconnect = true)
|
|
470
|
+
return if @interface.state == 'DISCONNECTED' && !@interface.connected?
|
|
471
|
+
|
|
472
|
+
# Synchronize the calls to @interface.disconnect since it takes an unknown
|
|
473
|
+
# amount of time. If two calls to disconnect stack up, the if statement
|
|
474
|
+
# should avoid multiple calls to disconnect.
|
|
475
|
+
@mutex.synchronize do
|
|
476
|
+
begin
|
|
477
|
+
@interface.disconnect if @interface.connected?
|
|
478
|
+
rescue => e
|
|
479
|
+
Logger.error "Disconnect: #{@interface.name}: #{e.formatted}"
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
# If the interface is set to auto_reconnect then delay so the thread
|
|
484
|
+
# can come back around and allow the interface a chance to reconnect.
|
|
485
|
+
if allow_reconnect and @interface.auto_reconnect and @interface.state != 'DISCONNECTED'
|
|
486
|
+
attempting()
|
|
487
|
+
if !@cancel_thread
|
|
488
|
+
# Logger.debug "reconnect delay: #{@interface.reconnect_delay}"
|
|
489
|
+
@interface_thread_sleeper.sleep(@interface.reconnect_delay)
|
|
490
|
+
end
|
|
491
|
+
else
|
|
492
|
+
@interface.state = 'DISCONNECTED'
|
|
493
|
+
if @interface_or_router == 'INTERFACE'
|
|
494
|
+
InterfaceStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
|
|
495
|
+
else
|
|
496
|
+
RouterStatusModel.set(@interface.as_json(:allow_nan => true), scope: @scope)
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
# Disconnect from the interface and stop the thread
|
|
502
|
+
def stop
|
|
503
|
+
Logger.info "#{@interface.name}: stop requested"
|
|
504
|
+
@mutex.synchronize do
|
|
505
|
+
# Need to make sure that @cancel_thread is set and the interface disconnected within
|
|
506
|
+
# mutex to ensure that connect() is not called when we want to stop()
|
|
507
|
+
@cancel_thread = true
|
|
508
|
+
@handler_thread.stop
|
|
509
|
+
@interface_thread_sleeper.cancel
|
|
510
|
+
@interface.disconnect
|
|
511
|
+
if @interface_or_router == 'INTERFACE'
|
|
512
|
+
valid_interface = InterfaceStatusModel.get_model(name: @interface.name, scope: @scope)
|
|
513
|
+
else
|
|
514
|
+
valid_interface = RouterStatusModel.get_model(name: @interface.name, scope: @scope)
|
|
515
|
+
end
|
|
516
|
+
valid_interface.destroy if valid_interface
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
def shutdown(sig = nil)
|
|
521
|
+
Logger.info "#{@interface.name}: shutdown requested"
|
|
522
|
+
stop()
|
|
523
|
+
super()
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
def graceful_kill
|
|
527
|
+
# Just to avoid warning
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
OpenC3::InterfaceMicroservice.run if __FILE__ == $0
|
|
@@ -0,0 +1,108 @@
|
|
|
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/microservices/microservice'
|
|
21
|
+
require 'openc3/topics/topic'
|
|
22
|
+
|
|
23
|
+
module OpenC3
|
|
24
|
+
class LogMicroservice < Microservice
|
|
25
|
+
def initialize(name)
|
|
26
|
+
super(name)
|
|
27
|
+
@config['options'].each do |option|
|
|
28
|
+
case option[0].upcase
|
|
29
|
+
when 'RAW_OR_DECOM'
|
|
30
|
+
@raw_or_decom = option[1].intern
|
|
31
|
+
when 'CMD_OR_TLM'
|
|
32
|
+
@cmd_or_tlm = option[1].intern
|
|
33
|
+
when 'CYCLE_TIME' # Maximum time between log files
|
|
34
|
+
@cycle_time = option[1].to_i
|
|
35
|
+
when 'CYCLE_SIZE' # Maximum size of a log file
|
|
36
|
+
@cycle_size = option[1].to_i
|
|
37
|
+
else
|
|
38
|
+
Logger.error("Unknown option passed to microservice #{@name}: #{option}")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
raise "Microservice #{@name} not fully configured" unless @raw_or_decom and @cmd_or_tlm
|
|
43
|
+
|
|
44
|
+
# These settings limit the log file to 10 minutes or 50MB of data, whichever comes first
|
|
45
|
+
@cycle_time = 600 unless @cycle_time # 10 minutes
|
|
46
|
+
@cycle_size = 50_000_000 unless @cycle_size # ~50 MB
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def run
|
|
50
|
+
plws = setup_plws
|
|
51
|
+
while true
|
|
52
|
+
break if @cancel_thread
|
|
53
|
+
|
|
54
|
+
Topic.read_topics(@topics) do |topic, msg_id, msg_hash, redis|
|
|
55
|
+
break if @cancel_thread
|
|
56
|
+
|
|
57
|
+
log_data(plws, topic, msg_id, msg_hash, redis)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def setup_plws
|
|
63
|
+
plws = {}
|
|
64
|
+
@topics.each do |topic|
|
|
65
|
+
topic_split = topic.gsub(/{|}/, '').split("__") # Remove the redis hashtag curly braces
|
|
66
|
+
scope = topic_split[0]
|
|
67
|
+
target_name = topic_split[2]
|
|
68
|
+
packet_name = topic_split[3]
|
|
69
|
+
type = @raw_or_decom.to_s.downcase
|
|
70
|
+
remote_log_directory = "#{scope}/#{type}_logs/#{@cmd_or_tlm.to_s.downcase}/#{target_name}/#{packet_name}"
|
|
71
|
+
rt_label = "#{scope}__#{target_name}__#{packet_name}__rt__#{type}"
|
|
72
|
+
stored_label = "#{scope}__#{target_name}__#{packet_name}__stored__#{type}"
|
|
73
|
+
plws[topic] = {
|
|
74
|
+
:RT => PacketLogWriter.new(remote_log_directory, rt_label, true, @cycle_time, @cycle_size, redis_topic: topic),
|
|
75
|
+
:STORED => PacketLogWriter.new(remote_log_directory, stored_label, true, @cycle_time, @cycle_size, redis_topic: topic)
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
return plws
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def log_data(plws, topic, msg_id, msg_hash, redis)
|
|
82
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
83
|
+
topic_split = topic.gsub(/{|}/, '').split("__") # Remove the redis hashtag curly braces
|
|
84
|
+
target_name = topic_split[2]
|
|
85
|
+
packet_name = topic_split[3]
|
|
86
|
+
rt_or_stored = ConfigParser.handle_true_false(msg_hash["stored"]) ? :STORED : :RT
|
|
87
|
+
packet_type = nil
|
|
88
|
+
data_key = nil
|
|
89
|
+
if @raw_or_decom == :RAW
|
|
90
|
+
packet_type = :RAW_PACKET
|
|
91
|
+
data_key = "buffer"
|
|
92
|
+
else # :DECOM
|
|
93
|
+
packet_type = :JSON_PACKET
|
|
94
|
+
data_key = "json_data"
|
|
95
|
+
end
|
|
96
|
+
plws[topic][rt_or_stored].write(packet_type, @cmd_or_tlm, target_name, packet_name, msg_hash["time"].to_i, rt_or_stored == :STORED, msg_hash[data_key], nil, msg_id)
|
|
97
|
+
@count += 1
|
|
98
|
+
diff = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start # seconds as a float
|
|
99
|
+
metric_labels = { "packet" => packet_name, "target" => target_name, "raw_or_decom" => @raw_or_decom.to_s, "cmd_or_tlm" => @cmd_or_tlm.to_s }
|
|
100
|
+
@metric.add_sample(name: "log_duration_seconds", value: diff, labels: metric_labels)
|
|
101
|
+
rescue => err
|
|
102
|
+
@error = err
|
|
103
|
+
Logger.error("#{@name} error: #{err.formatted}")
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
OpenC3::LogMicroservice.run if __FILE__ == $0
|