Module: OpenC3

Defined in:
lib/openc3/logs.rb,
lib/openc3/system.rb,
lib/openc3/api/api.rb,
lib/openc3/version.rb,
lib/openc3/accessors.rb,
lib/openc3/io/stderr.rb,
lib/openc3/io/stdout.rb,
lib/openc3/top_level.rb,
lib/openc3/utilities.rb,
lib/openc3/interfaces.rb,
lib/openc3/processors.rb,
lib/openc3/api/cmd_api.rb,
lib/openc3/api/tlm_api.rb,
lib/openc3/conversions.rb,
lib/openc3/io/json_api.rb,
lib/openc3/io/json_drb.rb,
lib/openc3/io/json_rpc.rb,
lib/openc3/win32/excel.rb,
lib/openc3/win32/win32.rb,
lib/openc3/models/model.rb,
lib/openc3/script/suite.rb,
lib/openc3/topics/topic.rb,
lib/openc3/api/stash_api.rb,
lib/openc3/bridge/bridge.rb,
lib/openc3/script/limits.rb,
lib/openc3/script/screen.rb,
lib/openc3/script/script.rb,
lib/openc3/system/system.rb,
lib/openc3/system/target.rb,
lib/openc3/utilities/crc.rb,
lib/openc3/utilities/csv.rb,
lib/openc3/api/config_api.rb,
lib/openc3/api/limits_api.rb,
lib/openc3/api/router_api.rb,
lib/openc3/api/target_api.rb,
lib/openc3/packets/limits.rb,
lib/openc3/packets/packet.rb,
lib/openc3/script/extract.rb,
lib/openc3/script/plugins.rb,
lib/openc3/script/storage.rb,
lib/openc3/streams/stream.rb,
lib/openc3/api/metrics_api.rb,
lib/openc3/logs/log_writer.rb,
lib/openc3/logs/stream_log.rb,
lib/openc3/script/calendar.rb,
lib/openc3/script/commands.rb,
lib/openc3/script/metadata.rb,
lib/openc3/script/packages.rb,
lib/openc3/utilities/store.rb,
lib/openc3/api/settings_api.rb,
lib/openc3/io/buffered_file.rb,
lib/openc3/io/json_drb_rack.rb,
lib/openc3/io/serial_driver.rb,
lib/openc3/models/cvt_model.rb,
lib/openc3/models/gem_model.rb,
lib/openc3/packets/commands.rb,
lib/openc3/script/telemetry.rb,
lib/openc3/utilities/bucket.rb,
lib/openc3/utilities/logger.rb,
lib/openc3/utilities/metric.rb,
lib/openc3/win32/win32_main.rb,
lib/openc3/api/interface_api.rb,
lib/openc3/io/io_multiplexer.rb,
lib/openc3/models/auth_model.rb,
lib/openc3/models/info_model.rb,
lib/openc3/models/note_model.rb,
lib/openc3/models/ping_model.rb,
lib/openc3/models/tool_model.rb,
lib/openc3/packets/structure.rb,
lib/openc3/packets/telemetry.rb,
lib/openc3/script/api_shared.rb,
lib/openc3/script/exceptions.rb,
lib/openc3/utilities/secrets.rb,
lib/openc3/utilities/sleeper.rb,
lib/openc3/accessors/accessor.rb,
lib/openc3/api/authorized_api.rb,
lib/openc3/ccsds/ccsds_packet.rb,
lib/openc3/ccsds/ccsds_parser.rb,
lib/openc3/io/json_api_object.rb,
lib/openc3/io/json_drb_object.rb,
lib/openc3/models/scope_model.rb,
lib/openc3/models/stash_model.rb,
lib/openc3/operators/operator.rb,
lib/openc3/utilities/throttle.rb,
lib/openc3/models/metric_model.rb,
lib/openc3/models/plugin_model.rb,
lib/openc3/models/router_model.rb,
lib/openc3/models/secret_model.rb,
lib/openc3/models/sorted_model.rb,
lib/openc3/models/target_model.rb,
lib/openc3/models/widget_model.rb,
lib/openc3/packets/json_packet.rb,
lib/openc3/packets/packet_item.rb,
lib/openc3/script/suite_runner.rb,
lib/openc3/streams/mqtt_stream.rb,
lib/openc3/topics/config_topic.rb,
lib/openc3/topics/router_topic.rb,
lib/openc3/utilities/migration.rb,
lib/openc3/bridge/bridge_config.rb,
lib/openc3/config/config_parser.rb,
lib/openc3/interfaces/interface.rb,
lib/openc3/logs/stream_log_pair.rb,
lib/openc3/logs/text_log_writer.rb,
lib/openc3/models/reducer_model.rb,
lib/openc3/models/setting_model.rb,
lib/openc3/models/trigger_model.rb,
lib/openc3/processors/processor.rb,
lib/openc3/script/script_runner.rb,
lib/openc3/script/suite_results.rb,
lib/openc3/system/system_config.rb,
lib/openc3/topics/command_topic.rb,
lib/openc3/utilities/aws_bucket.rb,
lib/openc3/utilities/local_mode.rb,
lib/openc3/utilities/quaternion.rb,
lib/openc3/models/activity_model.rb,
lib/openc3/models/metadata_model.rb,
lib/openc3/models/reaction_model.rb,
lib/openc3/models/timeline_model.rb,
lib/openc3/packets/packet_config.rb,
lib/openc3/script/web_socket_api.rb,
lib/openc3/streams/serial_stream.rb,
lib/openc3/topics/calendar_topic.rb,
lib/openc3/topics/timeline_topic.rb,
lib/openc3/utilities/message_log.rb,
lib/openc3/utilities/target_file.rb,
lib/openc3/accessors/xml_accessor.rb,
lib/openc3/api/offline_access_api.rb,
lib/openc3/conversions/conversion.rb,
lib/openc3/io/posix_serial_driver.rb,
lib/openc3/io/win32_serial_driver.rb,
lib/openc3/logs/packet_log_reader.rb,
lib/openc3/logs/packet_log_writer.rb,
lib/openc3/models/interface_model.rb,
lib/openc3/models/migration_model.rb,
lib/openc3/packets/structure_item.rb,
lib/openc3/tools/test_runner/test.rb,
lib/openc3/topics/autonomic_topic.rb,
lib/openc3/topics/interface_topic.rb,
lib/openc3/topics/telemetry_topic.rb,
lib/openc3/utilities/local_bucket.rb,
lib/openc3/utilities/python_proxy.rb,
lib/openc3/utilities/store_queued.rb,
lib/openc3/accessors/cbor_accessor.rb,
lib/openc3/accessors/form_accessor.rb,
lib/openc3/accessors/html_accessor.rb,
lib/openc3/accessors/http_accessor.rb,
lib/openc3/accessors/json_accessor.rb,
lib/openc3/packets/limits_response.rb,
lib/openc3/utilities/cli_generator.rb,
lib/openc3/utilities/redis_secrets.rb,
lib/openc3/interfaces/udp_interface.rb,
lib/openc3/models/environment_model.rb,
lib/openc3/models/tool_config_model.rb,
lib/openc3/utilities/authentication.rb,
lib/openc3/utilities/open_telemetry.rb,
lib/openc3/utilities/store_autoload.rb,
lib/openc3/accessors/binary_accessor.rb,
lib/openc3/config/meta_config_parser.rb,
lib/openc3/interfaces/linc_interface.rb,
lib/openc3/interfaces/mqtt_interface.rb,
lib/openc3/logs/packet_log_constants.rb,
lib/openc3/models/microservice_model.rb,
lib/openc3/packets/command_validator.rb,
lib/openc3/tools/table_manager/table.rb,
lib/openc3/topics/limits_event_topic.rb,
lib/openc3/utilities/process_manager.rb,
lib/openc3/microservices/microservice.rb,
lib/openc3/models/router_status_model.rb,
lib/openc3/models/trigger_group_model.rb,
lib/openc3/packets/packet_item_limits.rb,
lib/openc3/topics/command_decom_topic.rb,
lib/openc3/topics/system_events_topic.rb,
lib/openc3/utilities/bucket_utilities.rb,
lib/openc3/utilities/simulated_target.rb,
lib/openc3/accessors/template_accessor.rb,
lib/openc3/bridge/bridge_router_thread.rb,
lib/openc3/interfaces/serial_interface.rb,
lib/openc3/interfaces/stream_interface.rb,
lib/openc3/models/offline_access_model.rb,
lib/openc3/models/process_status_model.rb,
lib/openc3/models/python_package_model.rb,
lib/openc3/packets/parsers/xtce_parser.rb,
lib/openc3/streams/tcpip_client_stream.rb,
lib/openc3/streams/tcpip_socket_stream.rb,
lib/openc3/packets/parsers/state_parser.rb,
lib/openc3/topics/decom_interface_topic.rb,
lib/openc3/topics/telemetry_decom_topic.rb,
lib/openc3/interfaces/protocols/protocol.rb,
lib/openc3/models/interface_status_model.rb,
lib/openc3/packets/parsers/limits_parser.rb,
lib/openc3/packets/parsers/packet_parser.rb,
lib/openc3/bridge/bridge_interface_thread.rb,
lib/openc3/conversions/generic_conversion.rb,
lib/openc3/conversions/ip_read_conversion.rb,
lib/openc3/microservices/log_microservice.rb,
lib/openc3/packets/parsers/xtce_converter.rb,
lib/openc3/processors/watermark_processor.rb,
lib/openc3/tools/table_manager/table_item.rb,
lib/openc3/conversions/ip_write_conversion.rb,
lib/openc3/logs/buffered_packet_log_reader.rb,
lib/openc3/logs/buffered_packet_log_writer.rb,
lib/openc3/operators/microservice_operator.rb,
lib/openc3/processors/statistics_processor.rb,
lib/openc3/topics/telemetry_reduced_topics.rb,
lib/openc3/conversions/processor_conversion.rb,
lib/openc3/conversions/unix_time_conversion.rb,
lib/openc3/interfaces/http_client_interface.rb,
lib/openc3/interfaces/http_server_interface.rb,
lib/openc3/interfaces/mqtt_stream_interface.rb,
lib/openc3/microservices/decom_microservice.rb,
lib/openc3/microservices/multi_microservice.rb,
lib/openc3/models/microservice_status_model.rb,
lib/openc3/packets/parsers/processor_parser.rb,
lib/openc3/streams/web_socket_client_stream.rb,
lib/openc3/tools/table_manager/table_config.rb,
lib/openc3/tools/table_manager/table_parser.rb,
lib/openc3/utilities/cosmos_rails_formatter.rb,
lib/openc3/conversions/polynomial_conversion.rb,
lib/openc3/interfaces/protocols/crc_protocol.rb,
lib/openc3/interfaces/tcpip_client_interface.rb,
lib/openc3/interfaces/tcpip_server_interface.rb,
lib/openc3/microservices/plugin_microservice.rb,
lib/openc3/microservices/router_microservice.rb,
lib/openc3/conversions/bit_reverse_conversion.rb,
lib/openc3/conversions/object_read_conversion.rb,
lib/openc3/interfaces/protocols/cobs_protocol.rb,
lib/openc3/interfaces/protocols/slip_protocol.rb,
lib/openc3/microservices/cleanup_microservice.rb,
lib/openc3/microservices/reducer_microservice.rb,
lib/openc3/packets/parsers/packet_item_parser.rb,
lib/openc3/conversions/object_write_conversion.rb,
lib/openc3/interfaces/protocols/burst_protocol.rb,
lib/openc3/interfaces/protocols/fixed_protocol.rb,
lib/openc3/microservices/periodic_microservice.rb,
lib/openc3/microservices/reaction_microservice.rb,
lib/openc3/microservices/text_log_microservice.rb,
lib/openc3/microservices/timeline_microservice.rb,
lib/openc3/migrations/20220420190000_log_stuff.rb,
lib/openc3/migrations/20230615000000_autonomic.rb,
lib/openc3/interfaces/protocols/length_protocol.rb,
lib/openc3/microservices/interface_decom_common.rb,
lib/openc3/microservices/interface_microservice.rb,
lib/openc3/packets/parsers/format_string_parser.rb,
lib/openc3/conversions/received_count_conversion.rb,
lib/openc3/interfaces/simulated_target_interface.rb,
lib/openc3/tools/cmd_tlm_server/interface_thread.rb,
lib/openc3/tools/table_manager/table_item_parser.rb,
lib/openc3/interfaces/protocols/template_protocol.rb,
lib/openc3/packets/parsers/limits_response_parser.rb,
lib/openc3/tools/table_manager/table_manager_core.rb,
lib/openc3/migrations/20240915000000_activity_uuid.rb,
lib/openc3/conversions/unix_time_seconds_conversion.rb,
lib/openc3/interfaces/protocols/terminated_protocol.rb,
lib/openc3/microservices/scope_cleanup_microservice.rb,
lib/openc3/microservices/trigger_group_microservice.rb,
lib/openc3/conversions/packet_time_seconds_conversion.rb,
lib/openc3/conversions/unix_time_formatted_conversion.rb,
lib/openc3/interfaces/protocols/cmd_response_protocol.rb,
lib/openc3/migrations/20221202214600_add_target_names.rb,
lib/openc3/migrations/20221210174900_convert_to_multi.rb,
lib/openc3/conversions/segmented_polynomial_conversion.rb,
lib/openc3/interfaces/protocols/ignore_packet_protocol.rb,
lib/openc3/interfaces/protocols/preidentified_protocol.rb,
lib/openc3/migrations/20231022000000_tlm_viewer_config.rb,
lib/openc3/conversions/packet_time_formatted_conversion.rb,
lib/openc3/conversions/received_time_seconds_conversion.rb,
lib/openc3/conversions/received_time_formatted_conversion.rb,
lib/openc3/migrations/20230915000002_no_scope_log_messages.rb,
ext/openc3/ext/crc/crc.c,
ext/openc3/ext/structure/structure.c,
ext/openc3/ext/telemetry/telemetry.c,
ext/openc3/ext/buffered_file/buffered_file.c,
ext/openc3/ext/config_parser/config_parser.c,
ext/openc3/ext/burst_protocol/burst_protocol.c,
ext/openc3/ext/tabbed_plots_config/tabbed_plots_config.c,
ext/openc3/ext/reducer_microservice/reducer_microservice.c,
ext/openc3/ext/polynomial_conversion/polynomial_conversion.c,
lib/openc3/io/udp_sockets.rb

Overview

Modified by OpenC3, Inc. All changes Copyright 2022, OpenC3, Inc. All Rights Reserved

This file may also be used under the terms of a commercial license if purchased from OpenC3, Inc.

Defined Under Namespace

Modules: Api, ApiShared, AuthorizedApi, ExcelColumnConstants, Extract, InterfaceDecomCommon, LocalMode, PacketLogConstants, Script, Version Classes: Accessor, ActivityError, ActivityInputError, ActivityModel, ActivityOverlapError, ActivityUuid, AddTargetNames, AllScriptsWebSocketApi, AuthModel, Autonomic, AutonomicEventsWebSocketApi, AutonomicTopic, AwsBucket, BinaryAccessor, BitReverseConversion, Bridge, BridgeConfig, BridgeInterfaceThread, BridgeRouterThread, Bucket, BucketUtilities, BufferedFile, BufferedPacketLogReader, BufferedPacketLogWriter, BurstProtocol, CSV, CalendarEventsWebSocketApi, CalendarTopic, CborAccessor, CcsdsPacket, CcsdsParser, CheckError, CleanupMicroservice, CliGenerator, CmdResponseProtocol, CmdTlmWebSocketApi, CobsProtocol, CommandDecomTopic, CommandTopic, CommandValidator, Commands, ConfigEventsWebSocketApi, ConfigParser, ConfigTopic, Conversion, ConvertToMulti, CosmosRailsFormatter, Crc, Crc16, Crc32, Crc64, Crc8, CrcProtocol, CvtModel, DecomInterfaceTopic, DecomMicroservice, EnvironmentError, EnvironmentModel, EphemeralModel, EphemeralStore, EphemeralStoreQueued, ExcelSpreadsheet, FatalError, FixedProtocol, FormAccessor, FormatStringParser, GemModel, GenericConversion, Group, HtmlAccessor, HttpAccessor, HttpClientInterface, HttpServerInterface, IgnorePacketProtocol, InfoModel, Interface, InterfaceCmdHandlerThread, InterfaceMicroservice, InterfaceModel, InterfaceStatusModel, InterfaceThread, InterfaceTopic, IoMultiplexer, IpReadConversion, IpWriteConversion, JsonAccessor, JsonApi, JsonApiError, JsonApiObject, JsonDRb, JsonDRbError, JsonDRbObject, JsonDRbUnknownError, JsonDrbRack, JsonPacket, JsonRpc, JsonRpcError, JsonRpcErrorResponse, JsonRpcRequest, JsonRpcResponse, JsonRpcSuccessResponse, LengthProtocol, Limits, LimitsEventTopic, LimitsEventsWebSocketApi, LimitsParser, LimitsResponse, LimitsResponseParser, LincHandshake, LincHandshakeCommand, LincInterface, LocalBucket, LogMicroservice, LogStuff, LogWriter, Logger, MessageLog, MessagesWebSocketApi, MetaConfigParser, MetadataModel, Metric, MetricModel, Microservice, MicroserviceModel, MicroserviceOperator, MicroserviceStatusModel, Migration, MigrationModel, Model, MqttInterface, MqttStream, MqttStreamInterface, MultiMicroservice, NoScopeLogMessages, NoteModel, ObjectReadConversion, ObjectWriteConversion, OfflineAccessModel, OpenC3Authentication, OpenC3AuthenticationError, OpenC3AuthenticationRetryableError, OpenC3KeycloakAuthentication, Operator, OperatorProcess, OperatorProcessIO, Packet, PacketBase, PacketConfig, PacketItem, PacketItemLimits, PacketItemParser, PacketLogReader, PacketLogWriter, PacketParser, PacketTimeFormattedConversion, PacketTimeSecondsConversion, PeriodicMicroservice, PingModel, PluginMicroservice, PluginModel, PolynomialConversion, PosixSerialDriver, PreidentifiedProtocol, ProcessManager, ProcessManagerProcess, ProcessStatusModel, Processor, ProcessorConversion, ProcessorParser, Protocol, PythonPackageModel, PythonProxy, Quaternion, QueueBase, ReactionBase, ReactionError, ReactionInputError, ReactionMicroservice, ReactionModel, ReactionShare, ReactionSnoozeManager, ReactionWorker, ReceivedCountConversion, ReceivedTimeFormattedConversion, ReceivedTimeSecondsConversion, RedisSecrets, ReducerMicroservice, ReducerModel, ReducerState, RouterMicroservice, RouterModel, RouterStatusModel, RouterTlmHandlerThread, RouterTopic, RunningScriptWebSocketApi, Schedule, ScopeCleanupMicroservice, ScopeModel, ScriptResult, ScriptServerProxy, ScriptStatus, ScriptWebSocketApi, SecretModel, Secrets, SegmentedPolynomialConversion, SerialDriver, SerialInterface, SerialStream, ServerProxy, SettingModel, SimulatedTarget, SimulatedTargetInterface, SkipScript, SkipTestCase, Sleeper, SlipProtocol, SnoozeBase, SortedError, SortedInputError, SortedModel, SortedOverlapError, StashModel, StateParser, StatisticsProcessor, Stderr, Stdout, StopScript, Store, StoreConnectionPool, StoreQueued, Stream, StreamInterface, StreamLog, StreamLogPair, StreamingWebSocketApi, Structure, StructureItem, Suite, SuiteResults, SuiteRunner, System, SystemConfig, SystemEventsTopic, SystemEventsWebSocketApi, TabbedPlotsConfig, Table, TableConfig, TableItem, TableItemParser, TableManagerCore, TableParser, Target, TargetFile, TargetModel, TcpipClientInterface, TcpipClientStream, TcpipServerInterface, TcpipSocketStream, Telemetry, TelemetryDecomTopic, TelemetryReducedDayTopic, TelemetryReducedHourTopic, TelemetryReducedMinuteTopic, TelemetryTopic, TemplateAccessor, TemplateProtocol, TerminatedProtocol, Test, TestResult, TestStatus, TestSuite, TextLogMicroservice, TextLogWriter, Throttle, TimelineError, TimelineEventsWebSocketApi, TimelineInputError, TimelineManager, TimelineMicroservice, TimelineModel, TimelineTopic, TimelineWorker, TlmViewerConfig, ToolConfigModel, ToolModel, Topic, TriggerBase, TriggerError, TriggerGroupError, TriggerGroupInputError, TriggerGroupManager, TriggerGroupMicroservice, TriggerGroupModel, TriggerGroupShare, TriggerGroupWorker, TriggerInputError, TriggerLoopError, TriggerModel, UdpInterface, UdpReadSocket, UdpReadWriteSocket, UdpWriteSocket, UnassignedSuite, UnixTimeConversion, UnixTimeFormattedConversion, UnixTimeSecondsConversion, WatermarkProcessor, WebSocketApi, WebSocketClientStream, WidgetModel, Win32, Win32API, Win32SerialDriver, WriteRejectError, XmlAccessor, XtceConverter, XtceParser

Constant Summary collapse

VERSION =
'5.19.0'
GEM_VERSION =
'5.19.0'
BASE_PWD =
Dir.pwd
OPENC3_MUTEX =

Global mutex for the OpenC3 module

Mutex.new
PATH =

Path to OpenC3 Gem based on location of top_level.rb

File.expand_path(File.join(File.dirname(__FILE__), '../..'))
OPENC3_MARSHAL_HEADER =

Header to put on all marshal files created by OpenC3

"ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE} patchlevel #{RUBY_PATCHLEVEL}) [#{RUBY_PLATFORM}] OpenC3 #{OPENC3_VERSION}"
POINTER_TYPE =
Fiddle::SIZEOF_VOIDP == Fiddle::SIZEOF_LONG_LONG ? 'q*' : 'l!*'

Class Method Summary collapse

Class Method Details

.add_to_search_path(path, front = true) ⇒ Object

Adds a path to the global Ruby search path

Parameters:

  • path (String)

    Directory path



95
96
97
98
99
100
101
102
103
# File 'lib/openc3/top_level.rb', line 95

def self.add_to_search_path(path, front = true)
  path = File.expand_path(path)
  $:.delete(path)
  if front
    $:.unshift(path)
  else # Back
    $: << path
  end
end

.close_socket(socket) ⇒ Object

Close a socket in a manner that ensures that any reads blocked in select will unblock across platforms

Parameters:

  • socket

    The socket to close



535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
# File 'lib/openc3/top_level.rb', line 535

def self.close_socket(socket)
  if socket
    # Calling shutdown and then sleep seems to be required
    # to get select to reliably unblock on linux
    begin
      socket.shutdown(:RDWR)
      sleep(0)
    rescue Exception
      # Oh well we tried
    end
    begin
      socket.close unless socket.closed?
    rescue Exception
      # Oh well we tried
    end
  end
end

.create_log_file(filename, log_dir = nil) {|file| ... } ⇒ String|nil

Opens a timestamped log file for writing. The opened file is yielded back to the block.

Parameters:

  • filename (String)

    String to append to the exception log filename. The filename will start with a date/time stamp.

  • log_dir (String) (defaults to: nil)

    By default this method will write to the OpenC3 default log directory. By setting this parameter you can override the directory the log will be written to.

Yield Parameters:

  • file (File)

    The log file

Returns:

  • (String|nil)

    The fully pathed log filename or nil if there was an error creating the log file.



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/openc3/top_level.rb', line 241

def self.create_log_file(filename, log_dir = nil)
  log_file = nil
  begin
    # If this has an error we won't be able
    # to determine the log path but we still want to write the log.
    log_dir = System.instance.paths['LOGS'] unless log_dir
    # Make sure the log directory exists
    raise unless File.exist?(log_dir)
  rescue Exception
    log_dir = nil # Reset log dir since it failed above
    # First check for ./logs
    log_dir = './logs' if File.exist?('./logs')
    # Prefer ./outputs/logs if it exists
    log_dir = './outputs/logs' if File.exist?('./outputs/logs')
    # If all else fails just use the local directory
    log_dir = '.' unless log_dir
  end
  log_file = File.join(log_dir,
                        File.build_timestamped_filename([filename]))
  # Check for the log file existing. This could happen if this method gets
  # called more than once in the same second.
  if File.exist?(log_file)
    sleep 1.01 # Sleep before rebuilding the timestamp to get something unique
    log_file = File.join(log_dir,
                          File.build_timestamped_filename([filename]))
  end
  begin
    OPENC3_MUTEX.synchronize do
      file = File.open(log_file, 'w')
      yield file
    ensure
      file.close unless file.closed?
      File.chmod(0444, log_file) # Make file read only
    end
  rescue Exception
    # Ensure we always return
  end
  log_file = File.expand_path(log_file)
  return log_file
end

.disable_warningsObject

Disables the Ruby interpreter warnings such as when redefining a constant



84
85
86
87
88
89
90
# File 'lib/openc3/top_level.rb', line 84

def self.disable_warnings
  saved_verbose = $VERBOSE
  $VERBOSE = nil
  yield
ensure
  $VERBOSE = saved_verbose
end

.handle_critical_exception(error, _try_gui = true) ⇒ Object

CriticalErrors are errors that need to be brought to a user’s attention but do not cause an exit. A good example is if the packet log writer fails and can no longer write the log file. Write a message to the Logger, write an exception file, and popup a GUI window if try_gui. Ensure the GUI only comes up once so this method can be called over and over by failing code.

Parameters:

  • error (Exception)

    The exception to handle

  • try_gui (Boolean)

    Whether to try and create a GUI exception popup



392
393
394
395
# File 'lib/openc3/top_level.rb', line 392

def self.handle_critical_exception(error, _try_gui = true)
  Logger.error "Critical Exception! #{error.formatted}"
  self.write_exception_file(error)
end

.handle_fatal_exception(error, _try_gui = true) ⇒ Object

Write a message to the Logger, write an exception file, and popup a GUI window if try_gui. Finally ‘exit 1’ is called to end the calling program.

Parameters:

  • error (Exception)

    The exception to handle

  • try_gui (Boolean)

    Whether to try and create a GUI exception popup



366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# File 'lib/openc3/top_level.rb', line 366

def self.handle_fatal_exception(error, _try_gui = true)
  unless SystemExit === error or SignalException === error
    $openc3_fatal_exception = error
    self.write_exception_file(error)
    Logger.fatal "Fatal Exception! Exiting..."
    Logger.fatal error.formatted
    if $stdout != STDOUT
      $stdout = STDOUT
      Logger.fatal "Fatal Exception! Exiting..."
      Logger.fatal error.formatted
    end
    sleep 1 # Allow the messages to be printed and then crash
    exit 1
  else
    exit 0
  end
end

.hash_files(filenames, additional_data = nil, hashing_algorithm = 'SHA256') ⇒ Digest::<algorithm>

Runs a hash algorithm over one or more files and returns the Digest object. Handles windows/unix new line differences but changes in whitespace will change the hash sum.

Usage:

digest = OpenC3.hash_files(files, additional_data, hashing_algorithm)
digest.digest # => the 16 bytes of digest
digest.hexdigest # => the formatted string in hex

Parameters:

  • filenames (Array<String>)

    List of files to read and calculate a hashing sum on

  • additional_data (String) (defaults to: nil)

    Additional data to add to the hashing sum

  • hashing_algorithm (String) (defaults to: 'SHA256')

    Hashing algorithm to use

Returns:

  • (Digest::<algorithm>)

    The hashing sum object



217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/openc3/top_level.rb', line 217

def self.hash_files(filenames, additional_data = nil, hashing_algorithm = 'SHA256')
  digest = Digest.const_get(hashing_algorithm).public_send('new')

  filenames.each do |filename|
    next if File.directory?(filename)

    # Read the file's data and add to the running hashing sum
    digest << File.read(filename)
  end
  digest << additional_data if additional_data
  digest
end

.in_span(span_name, tracer_name = 'openc3-tracer') ⇒ Object



45
46
47
48
49
50
51
52
53
54
# File 'lib/openc3/utilities/open_telemetry.rb', line 45

def self.in_span(span_name, tracer_name = 'openc3-tracer')
  if @otel_enabled
    tracer = OpenTelemetry.tracer_provider.tracer(tracer_name)
    tracer.in_span(span_name) do |span|
      yield span
    end
  else
    yield nil
  end
end

.inject_context(hash) ⇒ Object



28
29
30
31
32
# File 'lib/openc3/utilities/open_telemetry.rb', line 28

def self.inject_context(hash)
  if @otel_enabled
    OpenTelemetry.propagation.inject(hash)
  end
end

.kill_thread(owner, thread, graceful_timeout = 1, timeout_interval = 0.01, hard_timeout = 1) ⇒ Object

Attempt to gracefully kill a thread

Parameters:

  • owner

    Object that owns the thread and may have a graceful_kill method

  • thread

    The thread to gracefully kill

  • graceful_timeout (defaults to: 1)

    Timeout in seconds to wait for it to die gracefully

  • timeout_interval (defaults to: 0.01)

    How often to poll for aliveness

  • hard_timeout (defaults to: 1)

    Timeout in seconds to wait for it to die ungracefully



495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
# File 'lib/openc3/top_level.rb', line 495

def self.kill_thread(owner, thread, graceful_timeout = 1, timeout_interval = 0.01, hard_timeout = 1)
  if thread
    if owner and owner.respond_to? :graceful_kill
      if Thread.current != thread
        owner.graceful_kill
        end_time = Time.now.sys + graceful_timeout
        while thread.alive? && ((end_time - Time.now.sys) > 0)
          sleep(timeout_interval)
        end
      else
        Logger.warn "Threads cannot graceful_kill themselves"
      end
    elsif owner
      Logger.info "Thread owner #{owner.class} does not support graceful_kill"
    end
    if thread.alive?
      # If the thread dies after alive? but before backtrace, bt will be nil.
      bt = thread.backtrace

      # Graceful failed
      msg =  "Failed to gracefully kill thread:\n"
      msg << "  Caller Backtrace:\n  #{caller().join("\n  ")}\n"
      msg << "  \n  Thread Backtrace:\n  #{bt.join("\n  ")}\n" if bt
      msg << "\n"
      Logger.warn msg
      thread.kill
      end_time = Time.now.sys + hard_timeout
      while thread.alive? && ((end_time - Time.now.sys) > 0)
        sleep(timeout_interval)
      end
    end
    if thread.alive?
      Logger.error "Failed to kill thread"
    end
  end
end

.marshal_dump(marshal_filename, obj) ⇒ Object

Creates a marshal file by serializing the given obj

Parameters:

  • marshal_filename (String)

    Name of the marshal file to create

  • obj (Object)

    The object to serialize to the file



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/openc3/top_level.rb', line 109

def self.marshal_dump(marshal_filename, obj)
  File.open(marshal_filename, 'wb') do |file|
    file.write(OPENC3_MARSHAL_HEADER)
    file.write(Marshal.dump(obj))
  end
rescue Exception => e
  begin
    File.delete(marshal_filename)
  rescue Exception
    # Oh well - we tried
  end
  if e.class == TypeError and e.message =~ /Thread::Mutex/
    original_backtrace = e.backtrace
    e = e.exception("Mutex exists in a packet.  Note: Packets must not be read during class initializers for Conversions, Limits Responses, etc.: #{e}")
    e.set_backtrace(original_backtrace)
  end
  self.handle_fatal_exception(e)
end

.marshal_load(marshal_filename) ⇒ Object

Loads the marshal file back into a Ruby object

Parameters:

  • marshal_filename (String)

    Name of the marshal file to load



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/openc3/top_level.rb', line 131

def self.marshal_load(marshal_filename)
  openc3_marshal_header = nil
  data = nil
  File.open(marshal_filename, 'rb') do |file|
    openc3_marshal_header = file.read(OPENC3_MARSHAL_HEADER.length)
    data = file.read
  end
  if openc3_marshal_header == OPENC3_MARSHAL_HEADER
    return Marshal.load(data)
  else
    Logger.warn "Marshal load failed with invalid marshal file: #{marshal_filename}"
    return nil
  end
rescue Exception => e
  if File.exist?(marshal_filename)
    Logger.error "Marshal load failed with exception: #{marshal_filename}\n#{e.formatted}"
  else
    Logger.info "Marshal file does not exist: #{marshal_filename}"
  end

  # Try to delete the bad marshal file
  begin
    File.delete(marshal_filename)
  rescue Exception
    # Oh well - we tried
  end
  self.handle_fatal_exception(e) if File.exist?(marshal_filename)
  return nil
end

.open_in_web_browser(_filename) ⇒ Object

Parameters:

  • filename (String)

    Name of the file to open in the web browser



460
461
462
# File 'lib/openc3/top_level.rb', line 460

def self.open_in_web_browser(_filename)
  puts "open_in_web_browser is DEPRECATED"
end

.otel_enabledObject



24
25
26
# File 'lib/openc3/utilities/open_telemetry.rb', line 24

def self.otel_enabled
  @otel_enabled
end

.require_class(class_name_or_class_filename, log_error = true) ⇒ Object

Require the class represented by the filename. This uses the standard Ruby convention of having a single class per file where the class name is camel cased and filename is lowercase with underscores.

Parameters:

  • class_name_or_class_filename (String)

    The name of the class or the file which contains the Ruby class to require

  • log_error (Boolean) (defaults to: true)

    Whether to log an error if we can't require the class



429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
# File 'lib/openc3/top_level.rb', line 429

def self.require_class(class_name_or_class_filename, log_error = true)
  if class_name_or_class_filename.downcase[-3..-1] == '.rb' or (class_name_or_class_filename[0] == class_name_or_class_filename[0].downcase)
    class_filename = class_name_or_class_filename
    class_name = class_filename.filename_to_class_name
  else
    class_name = class_name_or_class_filename
    class_filename = class_name.class_name_to_filename
  end
  return class_name.to_class if class_name.to_class and defined? class_name.to_class

  self.require_file(class_filename, log_error)
  klass = class_name.to_class
  raise "Ruby class #{class_name} not found" unless klass

  klass
end

.require_file(filename, log_error = true) ⇒ Object

Requires a file with a standard error message if it fails

Parameters:

  • filename (String)

    The name of the file to require

  • log_error (Boolean) (defaults to: true)

    Whether to log an error if we can't require the class



450
451
452
453
454
455
456
457
# File 'lib/openc3/top_level.rb', line 450

def self.require_file(filename, log_error = true)
  require filename
rescue Exception => e
  msg = "Unable to require #{filename} due to #{e.message}. "\
        "Ensure #{filename} is in the OpenC3 lib directory."
  Logger.error msg if log_error
  raise $!, msg, $!.backtrace
end

.run_process(command) ⇒ Object

Executes the command in a new Ruby Thread.

Parameters:

  • command (String)

    The command to execute via the 'system' call



164
165
166
167
168
169
170
171
172
173
# File 'lib/openc3/top_level.rb', line 164

def self.run_process(command)
  thread = nil
  thread = Thread.new do
    system(command)
  end
  # Wait for the thread and process to start
  sleep 0.01 until !thread.status.nil?
  sleep 0.1
  thread
end

.run_process_check_output(command) ⇒ Object

Executes the command in a new Ruby Thread. Will print the output if the process produces any output

Parameters:

  • command (String)

    The command to execute via the 'system' call



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/openc3/top_level.rb', line 179

def self.run_process_check_output(command)
  thread = nil
  thread = Thread.new do
    output, _ = Open3.capture2e(command)
    if !output.empty?
      # Ignore modalSession messages on Mac Mavericks
      new_output = ''
      output.each_line do |line|
        new_output << line if !/modalSession/.match?(line)
      end
      output = new_output

      if !output.empty?
        Logger.error output
        self.write_unexpected_file(output)
      end
    end
  end
  # Wait for the thread and process to start
  sleep 0.01 until !thread.status.nil?
  sleep 0.1
  thread
end

.safe_thread(name, retry_attempts = 0) ⇒ Object

Creates a Ruby Thread to run the given block. Rescues any exceptions and retries the threads the given number of times before handling the thread death by calling handle_fatal_exception.

Parameters:

  • name (String)

    Name of the thread

  • retry_attempts (Integer) (defaults to: 0)

    The number of times to allow the thread to restart before exiting



404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/openc3/top_level.rb', line 404

def self.safe_thread(name, retry_attempts = 0)
  Thread.new do
    retry_count = 0
    begin
      yield
    rescue => e
      Logger.error "#{name} thread unexpectedly died. Retries: #{retry_count} of #{retry_attempts}"
      Logger.error e.formatted
      retry_count += 1
      if retry_count <= retry_attempts
        self.write_exception_file(e)
        retry
      end
      handle_fatal_exception(e)
    end
  end
end

.set_working_dir(working_dir) ⇒ Object

Temporarily set the working directory during a block Working directory is global, so this can make other threads wait Ruby Dir.chdir with block always throws an error if multiple threads call Dir.chdir



468
469
470
471
472
473
474
475
476
# File 'lib/openc3/top_level.rb', line 468

def self.set_working_dir(working_dir, &)
  if $openc3_chdir_mutex.owned?
    set_working_dir_internal(working_dir, &)
  else
    $openc3_chdir_mutex.synchronize do
      set_working_dir_internal(working_dir, &)
    end
  end
end

.set_working_dir_internal(working_dir) ⇒ Object

Private helper method



479
480
481
482
483
484
485
486
487
# File 'lib/openc3/top_level.rb', line 479

def self.set_working_dir_internal(working_dir)
  current_dir = Dir.pwd
  Dir.chdir(working_dir)
  begin
    yield
  ensure
    Dir.chdir(current_dir)
  end
end

.setup_open_telemetry(service_name, support_rails = false) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/openc3/utilities/open_telemetry.rb', line 56

def self.setup_open_telemetry(service_name, support_rails = false)
  if ENV['OTEL_EXPORTER_OTLP_ENDPOINT']
    split_services = ENV['OPENC3_OTEL'].to_s.split(',')
    @otel_enabled = true if split_services.include?(service_name) or split_services.include?('ALL')

    if @otel_enabled
      require 'redis'
      require 'faraday'
      require 'openc3/utilities/bucket'
      # Load the bucket client code so the instrumentation works later
      Bucket.getClient()
      require 'opentelemetry/sdk'
      require 'opentelemetry/exporter/otlp'
      require 'opentelemetry/instrumentation/redis'
      require 'opentelemetry/instrumentation/http_client'
      require 'opentelemetry/instrumentation/aws_sdk'
      if support_rails
        require 'opentelemetry/instrumentation/rack'
        require 'opentelemetry/instrumentation/action_pack'
      end
      OpenTelemetry::SDK.configure do |c|
        c.service_name = service_name
        if support_rails
          c.use('OpenTelemetry::Instrumentation::Rack')
          c.use('OpenTelemetry::Instrumentation::ActionPack', { enable_recognize_route: true })
        end
        c.use 'OpenTelemetry::Instrumentation::Redis', {
          # The obfuscation of arguments in the db.statement attribute is enabled by default.
          # To include the full query, set db_statement to :include.
          # To obfuscate, set db_statement to :obfuscate.
          # To omit the attribute, set db_statement to :omit.
          db_statement: :include,
        }
        c.use 'OpenTelemetry::Instrumentation::Faraday'
        c.use 'OpenTelemetry::Instrumentation::AwsSdk'
        # TODO: Add in additional cloud SDKs
      end
    end
  end
end

.with_context(hash) ⇒ Object



34
35
36
37
38
39
40
41
42
43
# File 'lib/openc3/utilities/open_telemetry.rb', line 34

def self.with_context(hash)
  if @otel_enabled
    extracted_context = OpenTelemetry.propagation.extract(hash)
    OpenTelemetry::Context.with_current(extracted_context) do
      yield
    end
  else
    yield
  end
end

.write_exception_file(exception, filename = 'exception', log_dir = nil) ⇒ String|nil

Writes a log file with information about the current configuration including the Ruby version, OpenC3 version, whether you are on Windows, the OpenC3 path, and the Ruby path along with the exception that is passed in.

Parameters:

  • filename (String) (defaults to: 'exception')

    String to append to the exception log filename. The filename will start with a date/time stamp.

  • log_dir (String) (defaults to: nil)

    By default this method will write to the OpenC3 default log directory. By setting this parameter you can override the directory the log will be written to.

Returns:

  • (String|nil)

    The fully pathed log filename or nil if there was an error creating the log file.



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/openc3/top_level.rb', line 294

def self.write_exception_file(exception, filename = 'exception', log_dir = nil)
  log_file = create_log_file(filename, log_dir) do |file|
    file.puts "Exception:"
    if exception
      file.puts exception.formatted
      file.puts
    else
      file.puts "No Exception Given"
      file.puts caller.join("\n")
      file.puts
    end
    file.puts "Caller Backtrace:"
    file.puts caller().join("\n")
    file.puts

    file.puts "Ruby Version: ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE} patchlevel #{RUBY_PATCHLEVEL}) [#{RUBY_PLATFORM}]"
    file.puts "Rubygems Version: #{Gem::VERSION}"
    file.puts "OpenC3 Version: #{OpenC3::VERSION}"
    file.puts "OpenC3::PATH: #{OpenC3::PATH}"
    file.puts ""
    file.puts "Environment:"
    file.puts "RUBYOPT: #{ENV['RUBYOPT']}"
    file.puts "RUBYLIB: #{ENV['RUBYLIB']}"
    file.puts "GEM_PATH: #{ENV['GEM_PATH']}"
    file.puts "GEMRC: #{ENV['GEMRC']}"
    file.puts "RI_DEVKIT: #{ENV['RI_DEVKIT']}"
    file.puts "GEM_HOME: #{ENV['GEM_HOME']}"
    file.puts "PYTHONUSERBASE: #{ENV['PYTHONUSERBASE']}"
    file.puts "PATH: #{ENV['PATH']}"
    file.puts ""
    file.puts "Ruby Path:\n  #{$:.join("\n  ")}\n\n"
    file.puts "Gems:"
    Gem.loaded_specs.values.map { |x| file.puts "#{x.name} #{x.version} #{x.platform}" }
    file.puts ""
    file.puts "All Threads Backtraces:"
    Thread.list.each do |thread|
      file.puts thread.backtrace.join("\n")
      file.puts
    end
    file.puts ""
    file.puts ""
  ensure
    file.close
  end
  return log_file
end

.write_unexpected_file(text, filename = 'unexpected', log_dir = nil) ⇒ String|nil

Writes a log file with information about unexpected output

Parameters:

  • text (String)

    The unexpected output text

  • filename (String) (defaults to: 'unexpected')

    String to append to the exception log filename. The filename will start with a date/time stamp.

  • log_dir (String) (defaults to: nil)

    By default this method will write to the OpenC3 default log directory. By setting this parameter you can override the directory the log will be written to.

Returns:

  • (String|nil)

    The fully pathed log filename or nil if there was an error creating the log file.



351
352
353
354
355
356
357
358
359
# File 'lib/openc3/top_level.rb', line 351

def self.write_unexpected_file(text, filename = 'unexpected', log_dir = nil)
  log_file = create_log_file(filename, log_dir) do |file|
    file.puts "Unexpected Output:\n\n"
    file.puts text
  ensure
    file.close
  end
  return log_file
end