Class: Roby::Application
- Extended by:
- Logger::Forward, Logger::Hierarchy
- Defined in:
- lib/roby/app.rb
Overview
There is one and only one Application object, which holds mainly the system-wide configuration and takes care of file loading and system-wide setup (#setup). A Roby application can be started in multiple modes. The first and most important mode is the runtime mode (scripts/run). Other modes are the testing mode (#testing? returns true, entered through scripts/test) and the shell mode (#shell? returns true, entered through scripts/shell). Usually, user code does not have to take the modes into account, but it is sometime useful.
Finally, in both testing and runtime mode, the code can be started in simulation or live setups (see #simulation?). Specific plugins can for instance start and set up a simulation system in simulation mode, and as well set up some simulation-specific configuration for the functional layer of the architecture.
Configuration files
In all modes, a specific set of configuration files are loaded. The files that are actually loaded are defined by the robot name and type, as specified to #robot. The loaded files are, in order, the following: [config/app.yml] the application configuration as a YAML file. See the comments in that file for more details. [config/init.rb] Ruby code for the common configuration of all robots [config/ROBOT_NAME.rb or config/ROBOT_TYPE.rb] Ruby code for the configuration of either all robots of the same type, or a specific robot. It is one or the other. If a given robot needs to inherit the configuration of its type, explicitely require the ROBOT_TYPE.rb file in config/ROBOT_NAME.rb.
Runtime mode (scripts/run)
Then, in runtime mode the robot controller controller/ROBOT_NAME.rb or controller/ROBOT_TYPE.rb is loaded. The same rules than for the configuration file config/ROBOT_NAME.rb apply.
Testing mode (scripts/test)
This mode is used to run test suites in the test directory. See
Roby::Test::TestCase for a description of Roby-specific tests.
Plugin Integration
Plugins are integrated by providing methods that get called during setup and teardown of the application. It is therefore important to understand the order in which methods get called, and where the plugins can 'plug-in' this process.
On setup, the following methods are called:
- load base configuration files. app.yml and init.rb
- load_base_config hook
- set up directories (log dir, ...) and loggers
- set up singletons
- base_setup hook
- setup hook. The difference is that the setup hook is called only if #setup is called. base_setup is always called.
- load models in models/tasks
- require_models hook
- load models in models/planners and models/actions
- require_planners hook
- load additional model files
- finalize_model_loading hook
- load config file config/ROBOT.rb
- require_config hook
- setup main planner
- setup testing if in testing mode
- setup shell interface
Defined Under Namespace
Classes: ActionResolutionError, InvalidLoggerName, InvalidRobyAppDirEnv, LifecycleHook, LogDirNotInitialized, NoCurrentLog, NoSuchRobot, NotInCurrentApp, PluginsDisabled
Constant Summary collapse
- DEFAULT_OPTIONS =
{ "log" => { "events" => true, "server" => true, "levels" => {}, "filter_backtraces" => true }, "discovery" => {}, "engine" => {} }.freeze
- LOCK_FILE_EXT =
".roby-logdir"
Instance Attribute Summary collapse
-
#action_handlers ⇒ Array<#call>
readonly
List of blocks that should be executed once the application is started.
-
#additional_model_files ⇒ Array<String>
readonly
List of paths to files not in models/ that contain some models.
-
#app_extra_metadata ⇒ Object
readonly
Additional metadata saved in log_dir/info.yml by the app.
-
#app_name ⇒ Object
Returns the name of the application.
-
#argv_set ⇒ Object
readonly
The --set options passed on the command line.
-
#auto_load_models ⇒ Boolean
writeonly
Controls whether Roby should load the available the model files automatically in #require_models.
-
#available_plugins ⇒ Object
readonly
A [name, dir, file, module] array of available plugins, where 'name' is the plugin name, 'dir' the directory in which it is installed, 'file' the file which should be required to load the plugin and 'module' the Application-compatible module for configuration of the plug-in.
-
#cleanup_handlers ⇒ Array<#call>
readonly
List of objects called when the app cleans up (it is the opposite of setup).
-
#clear_models_handlers ⇒ Array<#call>
readonly
List of objects called when the app is doing #clear_models.
-
#controllers ⇒ Array<#call>
readonly
List of blocks that should be executed once the application is started.
-
#created_log_base_dirs ⇒ Array<String>
readonly
The list of directories created by this app in the paths to #created_log_dirs.
-
#created_log_dirs ⇒ Array<String>
readonly
The list of log directories created by this app.
-
#filter_out_patterns ⇒ Object
readonly
Array of regular expressions used to filter out backtraces.
-
#init_handlers ⇒ Array<#call>
readonly
List of objects called when the app gets initialized (i.e. just after init.rb is loaded).
-
#log_base_dir ⇒ Object
The base directory in which logs should be saved.
-
#log_server_pid ⇒ Integer?
readonly
The PID of the server that gives access to the log file.
-
#log_server_port ⇒ Integer?
readonly
The port on which the log server is started.
-
#log_timepoints ⇒ Object
writeonly
Override #log_timepoints? as configured in app.yml.
-
#module_name ⇒ Object
Returns the name of this app's toplevel module.
-
#notification_listeners ⇒ #call
readonly
The blocks that listen to notifications.
-
#options ⇒ Object
readonly
Applicatio configuration information is stored in a YAML file config/app.yml.
-
#plan ⇒ ExecutablePlan
readonly
The main plan on which this application acts.
-
#planners ⇒ Array
readonly
A set of planners declared in this application.
-
#plugins ⇒ Object
readonly
An [name, module] array of the loaded plugins.
-
#registered_exceptions ⇒ Array<(Exception,String)>
readonly
A set of exceptions that have been encountered by the application The associated string, if given, is a hint about in which context this exception got raised.
-
#require_handlers ⇒ Array<#call>
readonly
List of objects called when the app gets to require its models (i.e. after #require_models).
-
#rest_interface_host ⇒ String
The host to which the REST interface server should bind.
-
#rest_interface_port ⇒ Integer
The port on which the REST interface server should be.
-
#robots ⇒ App::RobotNames
readonly
The robot names configuration.
-
#search_path ⇒ Array<String>
The list of paths in which the application should be looking for files.
-
#setup_handlers ⇒ Array<#call>
readonly
List of objects called when the app gets initialized (i.e. in #setup after #base_setup).
-
#shell_interface ⇒ Object
readonly
The TCP server that gives access to the Interface.
-
#shell_interface_fd ⇒ Integer
A file descriptor that should be used as-is for the interface server.
-
#shell_interface_host ⇒ String
The host to which the shell interface server should bind.
-
#shell_interface_port ⇒ Integer
The port on which the shell interface server should be.
-
#shell_interface_v2 ⇒ Object
readonly
The TCP server that gives access to main remote Interface using the v2 protocol.
-
#shell_interface_v2_fd ⇒ Integer
A file descriptor that should be used as-is for the v2 interface server.
-
#shell_interface_v2_host ⇒ String
The host to which the v2 shell interface server should bind.
-
#shell_interface_v2_port ⇒ Integer
The port on which the v2 shell interface server should be.
-
#shutdown_handlers ⇒ Array<#call>
readonly
List of objects called when the app shuts down, that is when the plan is being tore down but before cleanup.
-
#ui_event_listeners ⇒ #call
readonly
The blocks that listen to ui events.
Class Method Summary collapse
-
.apply_conf_from_argv(value, conf: Conf) ⇒ Object
Set a value in a conf object (defaults to Conf) from a parameter given on the command line.
-
.attr_config(config_key) ⇒ Object
Allows to attribute configuration keys to override configuration parameters stored in config/app.yml.
- .common_optparse_add_stackprof(parser) ⇒ Object
- .common_optparse_add_tracing(parser) ⇒ Object
-
.common_optparse_setup(parser) ⇒ Object
Defines common configuration options valid for all Roby-oriented scripts.
- .find_data(*name) ⇒ Object
-
.guess_app_dir ⇒ String?
Guess the app directory based on the current directory.
-
.host_options(parser, options, interface_versions: false) ⇒ Object
Sets up provided option parser to add the --host and --vagrant option.
-
.host_options_set_defaults(options) ⇒ Object
Fill defaults in the option hash setup by Application.host_options.
-
.is_app_dir?(test_dir) ⇒ Boolean
Tests if the given directory looks like the root of a Roby app.
-
.lockfile_present?(path) ⇒ Boolean
Tests whether the given folder has a lockfile created in it.
-
.log_dir_locked?(path, strict: false) ⇒ Boolean
Checks if a path is currently locked by a Roby application.
-
.overridable_configuration(config_set, config_key, options = {}) ⇒ Object
Defines accessors for a configuration parameter stored in #options.
-
.read_current_dir(current_path) ⇒ Object
private
Read and validate the 'current' dir by means of the 'current' symlink that Roby maintains in its log base directory.
- .register_plugin(name, mod, &init) ⇒ Object
-
.resolve_matcher_argument(arg) ⇒ String, Regexp
private
Resolve a command-line argument into a string matcher (regexp or plain string).
- .resolve_timepoint_group_argument(spec) ⇒ Object
-
.unique_dirname(base_dir, path_spec, date_tag = nil) ⇒ Object
Returns a unique directory name as a subdirectory of
base_dir, based onpath_spec.
Instance Method Summary collapse
-
#abort_on_application_exception=(flag) ⇒ Object
Controls whether the Roby app should quit if an application (i.e. non-plan) exception is received.
-
#abort_on_application_exception? ⇒ Object
Controls whether the Roby app should quit if an application (i.e. non-plan) exception is received.
-
#abort_on_exception=(flag) ⇒ Object
Controls whether the application should quit if an unhandled plan exception is received.
-
#abort_on_exception? ⇒ Object
Controls whether the application should quit if an unhandled plan exception is received.
-
#action_from_model(model) ⇒ Actions::Models::Action
Find an action on the planning interface that can generate the given task model.
-
#action_from_name(name) ⇒ Actions::Models::Action
Finds the action matching the given name.
-
#actions(user: false, &block) ⇒ Object
Declares that the following block should be used to setup the main action interface.
-
#add_app_metadata(metadata, append: true) ⇒ Object
Add some metadata to #app_metadata, and save it to the log dir's info.yml if it is already created.
-
#add_lifecyle_hook(hook_set, block, **options) ⇒ Object
private
Registers a lifecycle hook in the provided list of hooks.
- #add_plugin(name, mod) ⇒ Object
-
#app_dir ⇒ String?
Returns the application base directory.
-
#app_dir=(dir) ⇒ Object
Allows to override the application base directory.
-
#app_file?(path) ⇒ Boolean
Returns true if the given path points to a file in the Roby app.
-
#app_metadata ⇒ Object
Metadata used to describe the app.
-
#app_module ⇒ Object
Returns this app's toplevel module.
- #app_path ⇒ Object
-
#apply_argv_set(conf: Conf) ⇒ Object
Apply the --set command line parameters stored in #argv_set to a conf object.
-
#apply_config(config) ⇒ Object
private
Sets relevant configuration values from a configuration hash.
-
#apply_config_interface(host_port) ⇒ Object
private
Parses and applies the 'interface' value from a configuration hash.
-
#auto_load_all=(flag) ⇒ Boolean
Controls whether Roby's auto-load feature should load all models in #search_path or only the ones in #app_dir.
-
#auto_load_all? ⇒ Boolean
Controls whether Roby's auto-load feature should load all models in #search_path or only the ones in #app_dir.
-
#auto_load_models? ⇒ Boolean
Controls whether Roby should load the available the model files automatically in #require_models.
-
#auto_load_search_path ⇒ Array<String>
The search path for the auto-load feature.
- #auto_require_models ⇒ Object
- #auto_require_planners ⇒ Object
-
#autodiscover_tests_in?(path, extra_robot_names: []) ⇒ Boolean
Hook for the plugins to filter out some paths that should not be auto-loaded by #each_test_file_in_app.
-
#automatic_testing=(flag) ⇒ Object
True if user interaction is disabled during tests.
-
#automatic_testing? ⇒ Object
True if user interaction is disabled during tests.
-
#backward_compatible_naming=(flag) ⇒ Object
If set to true, the app will enable backward-compatible behaviour related to naming schemes, file placements and so on.
-
#backward_compatible_naming? ⇒ Object
If set to true, the app will enable backward-compatible behaviour related to naming schemes, file placements and so on.
-
#base_cleanup ⇒ Object
The inverse of #base_setup.
- #base_setup ⇒ Object
- #base_setup_done? ⇒ Boolean
-
#call_plugins(method, *args, deprecated: nil) ⇒ Object
Call
methodon each loaded extension module which define it, with argumentsargs. -
#cleanup ⇒ Object
The inverse of #setup.
-
#cleanup_user_lifecycle_hooks(hook_set) ⇒ Object
private
Removes all lifecycle hooks that are marked as user hooks.
- #clear_config ⇒ Object
- #clear_exceptions ⇒ Object
-
#clear_model?(m) ⇒ Boolean
Whether this model should be cleared in #clear_models.
-
#clear_models ⇒ Object
Clear all models for which #clear_model? returns true.
- #compute_absolute_paths(relative_paths, root_paths, prioritize_root_paths: false) ⇒ Object
-
#controller(reset: false, user: false, &block) ⇒ Object
Declares that the following block should be used as the robot controller.
-
#created_log_dir? ⇒ Boolean
Test whether this app already created its log directory.
- #define_actions_module ⇒ Object
- #define_main_planner_if_needed ⇒ Object
-
#defined_plugin?(name) ⇒ Boolean
True if
nameis a plugin known to us. -
#definition_file_for(model) ⇒ Object
Returns the downmost app file that was involved in the given model's definition.
-
#development_mode? ⇒ Object
Whether the app should run in development mode.
-
#discover_test_files(all: true, only_self: false, base_dir: File.join(app_dir, "test"), extra_robot_names: []) ⇒ Array<String>
Discover which tests should be run, and require them.
-
#each_model(root_model = nil) {|| ... } ⇒ Object
Enumerate all models registered in this app.
-
#each_notification_listener {|the| ... } ⇒ Object
Enumerates the listeners currently registered through #on_notification.
-
#each_plugin(on_available = false) ⇒ Object
Enumerates all available plugins, yielding only the plugin module (i.e. the plugin object itself).
-
#each_responding_plugin(method, _on_available = false) ⇒ Object
Yields each plugin object that respond to
method. -
#each_robot_test_file_in(dir) ⇒ Object
Enumerate all the test files specific for this robot configuration.
-
#each_test_file_for_loaded_models {|path, models| ... } ⇒ Object
Enumerate the test files that should be run to test the current app configuration.
-
#each_test_file_in(dir, extra_robot_names: [], &block) ⇒ Object
Enumerate all the test files in a specific dir for this robot configuration.
-
#each_test_file_in_app(&block) ⇒ Object
Enumerate all the test files in this app and for this robot configuration.
-
#each_ui_event_listener {|the| ... } ⇒ Object
Enumerates the listeners currently registered through #on_ui_event.
-
#enable_remote_interface_version(version) ⇒ Module
Called by tooling to enable the given remote interface version.
-
#execution_engine ⇒ ExecutionEngine?
The engine associated with #plan.
-
#find_action_from_name(name) ⇒ (ActionInterface,Actions::Models::Action)?
Find an action with the given name on the action interfaces registered on #planners.
-
#find_and_create_log_dir(time_tag = self.time_tag) ⇒ String
Create a log directory for the given time tag, and make it this app's log directory.
-
#find_base_path_for(path) ⇒ nil, String
Returns the path in search_path that contains the given file or path.
- #find_data(*name) ⇒ Object
-
#find_dir(*args) ⇒ Object
Returns the first match from #find_dirs, or nil if nothing matches.
-
#find_files_in_dirs(*path, options) ⇒ Array<String>
Enumerates the subdirectories of paths in #search_path matching the given path.
-
#find_file(*args) ⇒ Object
Returns the first match from #find_files, or nil if nothing matches.
-
#find_files(*path, options) ⇒ Array<String>
Enumerates files based on their relative paths in #search_path.
-
#find_files_in_dirs(*path, options) ⇒ Array<String>
Enumerates the files that are present in subdirectories of paths in #search_path.
- #find_registered_app_path(app_name) ⇒ Object
-
#framework_file?(path) ⇒ Boolean
Tests whether a path is within a framework library.
-
#guess_app_dir ⇒ String
Guess the app directory based on the current directory, and sets #app_dir.
-
#has_app? ⇒ Boolean
Whether there is a supporting app directory.
- #has_registered_app?(app_name) ⇒ Boolean
-
#ignore_all_load_errors=(flag) ⇒ Object
If set to true, files that generate errors while loading will be ignored.
-
#ignore_all_load_errors? ⇒ Object
If set to true, files that generate errors while loading will be ignored.
-
#initialize(plan: ExecutablePlan.new) ⇒ Application
constructor
A new instance of Application.
-
#io_event_logger ⇒ EventLogging::IOEventLogger
Return an IOEventLogger that listens to the log events of #plan and #execution_engine.
- #isolate_load_errors(message, logger = Application, level = :warn) ⇒ Object
- #load_all_model_files_in(prefix_name, ignored_exceptions: []) ⇒ Object
-
#load_base_config ⇒ Object
Loads the base configuration.
- #load_config_yaml ⇒ Object
-
#load_default_models ⇒ Object
Helper to the robot config files to load the root files in models/ (e.g. models/tasks.rb).
-
#load_plugin_file(appfile) ⇒ Object
Load the given Roby plugin file.
-
#load_plugins_from_prefix(dir) ⇒ Object
Looks into subdirectories of
dirfor files called app.rb and registers them as Roby plugins. -
#loaded_plugin?(name) ⇒ Boolean
Returns true if
nameis a loaded plugin. - #lock_log_dir ⇒ Object
-
#log ⇒ Object
:method: log_server=.
-
#log_create_current=(flag) ⇒ Object
If set to true, this Roby application will create a 'current' entry in #log_base_dir that points to the latest log directory.
-
#log_create_current? ⇒ Object
If set to true, this Roby application will create a 'current' entry in #log_base_dir that points to the latest log directory.
-
#log_current_dir ⇒ Object
The path to the current log directory.
-
#log_current_file ⇒ Object
The path to the current log file.
-
#log_dir ⇒ Object
The directory in which logs are to be saved Defaults to app_dir/data/$time_tag.
-
#log_dir=(dir) ⇒ Object
Explicitely set the log directory.
-
#log_read_metadata ⇒ Object
Read the time tag from the current log directory.
- #log_read_time_tag ⇒ Object
-
#log_save_metadata(append: true) ⇒ Object
Save #app_metadata in the log directory.
-
#log_setup(mod_path, level, file = nil) ⇒ Object
Configures a text logger in the system.
-
#log_timepoints? ⇒ Boolean
Whether internal timepoints should be logged.
-
#main_action_interface ⇒ Object
Returns this app's main action interface.
-
#make_path_relative(path) ⇒ Object
Transforms
pathinto a path relative to an entry insearch_path(usually the application root directory). -
#model_defined_in_app?(model) ⇒ Boolean
Tests whether a model class has been defined in this app's code.
- #modelling_only ⇒ Object
-
#needs_to_be_in_current_app(allowed_outside: true) ⇒ Object
Call to check whether the current directory is within #app_dir.
-
#notify(source, level, message) ⇒ Object
Sends a message to all notification listeners.
-
#on_cleanup(user: false, &block) ⇒ Object
Declares that the following block should be called when #clear_models is called.
-
#on_clear_models(user: false, &block) ⇒ Object
Declares that the following block should be called when #clear_models is called.
-
#on_config(user: false, &block) ⇒ Object
deprecated
Deprecated.
use #on_setup instead
-
#on_init(user: false, &block) ⇒ Object
Declares a block that should be executed when the Roby app gets initialized (i.e. just after init.rb gets loaded).
-
#on_notification(user: false) {|source, level, message| ... } ⇒ Object
Registers a block to be called when a message needs to be dispatched from #notify.
-
#on_require(user: false, &block) ⇒ Object
Declares a block that should be executed when the Roby app loads models (i.e. in #require_models).
-
#on_setup(user: false, &block) ⇒ Object
Declares a block that should be executed when the Roby app is begin setup.
-
#on_shutdown(user: false, &block) ⇒ Object
Registers a callback to perform cleanup just after an execution.
-
#on_ui_event {|name, args| ... } ⇒ Object
Registers a block to be called when a message needs to be dispatched from #ui_event.
-
#plugin_definition(name) ⇒ Object
Returns the [name, dir, file, module] array definition of the plugin
name, or nil ifnameis not a known plugin. -
#plugins_enabled=(flag) ⇒ Object
True if plugins should be discovered, registered and loaded (true by default).
-
#plugins_enabled? ⇒ Object
True if plugins should be discovered, registered and loaded (true by default).
-
#prepare ⇒ Object
Prepares the environment to actually run.
-
#prepare_action(name, mission: false, **arguments) ⇒ Object
Generate the plan pattern that will call the required action on the planning interface, with the given arguments.
- #prepare_event_log ⇒ Object private
-
#prepend_search_path(path) ⇒ Object
Add a path to the search path, with the highest priority for e.g.
-
#public_log_server=(flag) ⇒ Object
If set to true, this Roby application will start a log server, from which tools like the Syskit IDE can access the Roby logs at runtime.
-
#public_log_server? ⇒ Object
If set to true, this Roby application will start a log server, from which tools like the Syskit IDE can access the Roby logs at runtime.
-
#public_logs=(flag) ⇒ Object
If set to true, this Roby application will make its logs public, i.e.
-
#public_logs? ⇒ Object
If set to true, this Roby application will make its logs public, i.e.
-
#public_rest_interface=(flag) ⇒ Object
If set to true, this Roby application will publish a Interface::REST::API object.
-
#public_rest_interface? ⇒ Object
If set to true, this Roby application will publish a Interface::REST::API object.
-
#public_shell_interface=(flag) ⇒ Object
If set to true, this Roby application will publish a Interface::Interface object as a TCP server.
-
#public_shell_interface? ⇒ Object
If set to true, this Roby application will publish a Interface::Interface object as a TCP server.
-
#public_shell_interface_v2=(flag) ⇒ Object
If set to true, this Roby application will publish a Interface::Interface object as a TCP server.
-
#public_shell_interface_v2? ⇒ Object
If set to true, this Roby application will publish a Interface::Interface object as a TCP server.
-
#push_search_path(path) ⇒ Object
Add a path to the search path, with the lowest search priority.
-
#quit ⇒ Object
Quit the application.
-
#register_app(path, where: :push) ⇒ Object
Register another Roby app.
- #register_exception(e, reason = nil) ⇒ Object
- #register_plugin(name, mod, path: nil, &init) ⇒ Object
- #register_plugins(force: false) ⇒ Object
-
#register_server(name, port) ⇒ Object
Register a server port that can be discovered later.
-
#reload_actions ⇒ Object
Reload action models defined in models/actions/.
-
#reload_config ⇒ Object
Reload files in config/.
-
#reload_models ⇒ Object
Reload model files in models/.
- #reload_planners ⇒ Object
-
#remove_notification_listener(listener) ⇒ Object
Removes a notification listener added with #on_notification.
-
#remove_ui_event_listener(listener) ⇒ Object
Removes a notification listener added with #on_ui_event.
- #require(absolute_path) ⇒ Object
-
#require_app_dir(needs_current: false, allowed_outside: true) ⇒ Object
Call to require this roby application to be in a Roby application.
-
#require_models ⇒ Object
Loads the models, based on the given robot name and robot type.
-
#require_planners ⇒ Object
Loads the planner models.
- #require_robot_file ⇒ Object
-
#require_v2_protocol_extensions ⇒ Object
Require the protocol extension files defined in interface.protocol_extensions.
-
#reset_log_dir ⇒ Object
Reset the current log dir so that #setup picks a new one.
-
#reset_plan(plan = ExecutablePlan.new) ⇒ Object
Reset the plan to a new Plan object.
-
#restart(*cmdline) ⇒ Object
Quits this app and replaces with a new one after a proper cleanup.
-
#restart! ⇒ Object
Restarts the same app.
-
#restarting? ⇒ Boolean
Indicates to whomever is managing this app that #restart should be called.
-
#robot(name, type = nil) ⇒ Object
Sets up the name and type of the robot.
-
#robot_configuration_names ⇒ Array
Returns all unique valid robot configuration names (i.e robot_type and robot_name).
-
#robot_name ⇒ String?
The robot name.
-
#robot_name?(name) ⇒ Boolean
Test if the given name is a valid robot name.
-
#robot_type ⇒ String?
The robot type.
-
#root_models ⇒ Array<#each_submodel>
The list of model classes that allow to discover all models in this app.
- #run(thread_priority: 0, &block) ⇒ Object
-
#run_controller_blocks ⇒ Object
private
Run the blocks registered with #controller.
-
#run_plugins(mods, &block) ⇒ Object
Helper for Application#run to call the plugin's run or start methods while guaranteeing the system's cleanup.
- #run_shutdown_blocks ⇒ Object
-
#running? ⇒ Boolean
Whether we're inside #run.
-
#self_file?(path) ⇒ Boolean
Returns true if the given path points to a file under #app_dir.
-
#setup ⇒ Object
Does basic setup of the Roby environment.
- #setup_for_minimal_tooling ⇒ Object
-
#setup_loggers(ignore_missing: false, redirections: true) ⇒ Object
Sets up all the default loggers.
-
#setup_rest_interface ⇒ Object
Publishes a REST API.
- #setup_robot_names_from_config_dir ⇒ Object
-
#setup_shell_interface_v1 ⇒ Object
Publishes a shell interface.
-
#setup_shell_interface_v2 ⇒ Object
Publishes a shell interface.
- #shell ⇒ Object
- #shell_interface_has_clients? ⇒ Boolean
-
#shift_search_path(path) ⇒ Object
Add a path to the search path, just after the app dir in search priority.
-
#shutdown ⇒ Object
The inverse of #prepare.
- #shutdown_event_logger ⇒ Object
- #simulation ⇒ Object
- #single ⇒ Object
-
#stackprof_event_manager ⇒ EventLogging::StackProfEventManager
Return an object that allows to trigger stackprof profiling based on timepoints.
- #start_log_server(logfile, options = {}) ⇒ Object
- #stop ⇒ Object
- #stop_log_server ⇒ Object
-
#stop_rest_interface(join: false) ⇒ Object
Stops a running REST interface.
-
#stop_shell_interface ⇒ Object
Stops a running shell interface.
-
#test_files_for(model) ⇒ Object
Given a model class, returns the full path of an existing test file that is meant to verify this model.
- #testing ⇒ Object
-
#time_tag ⇒ Object
The time tag.
-
#ui_event(name, *args) ⇒ Object
Sends a message to all UI event listeners.
-
#unload_features(*pattern) ⇒ Object
Ensure tha require'd files that match the given pattern can be re-required.
- #unlock_log_dir ⇒ Object
- #update_load_path ⇒ Object
-
#using(*names, force: false) ⇒ Object
Loads the plugins whose name are listed in
names.
Constructor Details
#initialize(plan: ExecutablePlan.new) ⇒ Application
Returns a new instance of Application.
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 |
# File 'lib/roby/app.rb', line 1035 def initialize(plan: ExecutablePlan.new) @plan = plan @argv_set = [] @registered_apps = {} @auto_load_all = false @default_auto_load = true @auto_load_models = nil @app_name = nil @module_name = nil @app_dir = nil @backward_compatible_naming = true @development_mode = true @search_path = nil @plugins = [] @plugins_enabled = true @available_plugins = [] = DEFAULT_OPTIONS.dup @public_logs = false @public_log_server = false @log_create_current = true @created_log_dirs = [] @created_log_base_dirs = [] @additional_model_files = [] @restarting = false @running = false @shell_interface = nil @shell_interface_host = nil @shell_interface_port = Interface::DEFAULT_PORT @shell_interface_fd = nil @shell_abort_on_exception = false @shell_interface_v2 = nil @shell_interface_v2_host = nil @shell_interface_v2_port = Interface::DEFAULT_PORT_V2 @shell_interface_v2_fd = nil @rest_interface = nil @rest_interface_host = nil @rest_interface_port = Interface::DEFAULT_REST_PORT @display_all_threads_state_on_abort = true @automatic_testing = true @registered_exceptions = [] = {} @filter_out_patterns = [Roby::RX_IN_FRAMEWORK, Roby::RX_IN_METARUBY, Roby::RX_IN_UTILRB, Roby::RX_REQUIRE] self.abort_on_application_exception = true @planners = [] @notification_listeners = [] @ui_event_listeners = [] @init_handlers = [] @setup_handlers = [] @require_handlers = [] @clear_models_handlers = [] @cleanup_handlers = [] @shutdown_handlers = [] @controllers = [] @action_handlers = [] @robots = App::RobotNames.new @base_setup_done = false @lock_file = nil end |
Instance Attribute Details
#action_handlers ⇒ Array<#call> (readonly)
Returns list of blocks that should be executed once the application is started.
575 576 577 |
# File 'lib/roby/app.rb', line 575 def action_handlers @action_handlers end |
#additional_model_files ⇒ Array<String> (readonly)
Returns list of paths to files not in models/ that contain some models. This is mainly used by the command-line tools so that the user can load separate "model-based scripts" files.
542 543 544 |
# File 'lib/roby/app.rb', line 542 def additional_model_files @additional_model_files end |
#app_extra_metadata ⇒ Object (readonly)
Additional metadata saved in log_dir/info.yml by the app
Do not modify directly, use #add_app_metadata instead
597 598 599 |
# File 'lib/roby/app.rb', line 597 def end |
#app_name ⇒ Object
Returns the name of the application
263 264 265 266 267 268 269 270 271 |
# File 'lib/roby/app.rb', line 263 def app_name if @app_name @app_name elsif app_dir @app_name = File.basename(app_dir).gsub(/[^\w]/, "_") else "default" end end |
#argv_set ⇒ Object (readonly)
The --set options passed on the command line
160 161 162 |
# File 'lib/roby/app.rb', line 160 def argv_set @argv_set end |
#auto_load_models=(flag) ⇒ Boolean (writeonly)
Controls whether Roby should load the available the model files automatically in #require_models
3038 3039 3040 |
# File 'lib/roby/app.rb', line 3038 def auto_load_models=(value) @auto_load_models = value end |
#available_plugins ⇒ Object (readonly)
A [name, dir, file, module] array of available plugins, where 'name' is the plugin name, 'dir' the directory in which it is installed, 'file' the file which should be required to load the plugin and 'module' the Application-compatible module for configuration of the plug-in
501 502 503 |
# File 'lib/roby/app.rb', line 501 def available_plugins @available_plugins end |
#cleanup_handlers ⇒ Array<#call> (readonly)
Returns list of objects called when the app cleans up (it is the opposite of setup).
562 563 564 |
# File 'lib/roby/app.rb', line 562 def cleanup_handlers @cleanup_handlers end |
#clear_models_handlers ⇒ Array<#call> (readonly)
Returns list of objects called when the app is doing #clear_models.
558 559 560 |
# File 'lib/roby/app.rb', line 558 def clear_models_handlers @clear_models_handlers end |
#controllers ⇒ Array<#call> (readonly)
Returns list of blocks that should be executed once the application is started.
571 572 573 |
# File 'lib/roby/app.rb', line 571 def controllers @controllers end |
#created_log_base_dirs ⇒ Array<String> (readonly)
The list of directories created by this app in the paths to #created_log_dirs
They are deleted on cleanup if #public_logs? is false. Unlike with #created_log_dirs, they are not deleted if they are not empty.
592 593 594 |
# File 'lib/roby/app.rb', line 592 def created_log_base_dirs @created_log_base_dirs end |
#created_log_dirs ⇒ Array<String> (readonly)
The list of log directories created by this app
They are deleted on cleanup if #public_logs? is false. Unlike with #created_log_base_dirs, they are deleted even if they are not empty.
583 584 585 |
# File 'lib/roby/app.rb', line 583 def created_log_dirs @created_log_dirs end |
#filter_out_patterns ⇒ Object (readonly)
Array of regular expressions used to filter out backtraces
847 848 849 |
# File 'lib/roby/app.rb', line 847 def filter_out_patterns @filter_out_patterns end |
#init_handlers ⇒ Array<#call> (readonly)
Returns list of objects called when the app gets initialized (i.e. just after init.rb is loaded).
546 547 548 |
# File 'lib/roby/app.rb', line 546 def init_handlers @init_handlers end |
#log_base_dir ⇒ Object
The base directory in which logs should be saved
Logs are saved in log_base_dir/$time_tag by default, and a log_base_dir/current symlink gets updated to reflect the most current log directory.
The path is controlled by the log/dir configuration variable. If the provided value, it is interpreted relative to the application directory. It defaults to "data".
1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 |
# File 'lib/roby/app.rb', line 1700 def log_base_dir maybe_relative_dir = if @log_base_dir ||= log["dir"] @log_base_dir elsif (global_base_dir = ENV["ROBY_BASE_LOG_DIR"]) if ENV["ROBY_ADD_APP_NAME_TO_BASE_LOG_DIR"] != "0" File.join(global_base_dir, app_name) else global_base_dir end else "logs" end File.(maybe_relative_dir, app_dir || Dir.pwd) end |
#log_server_pid ⇒ Integer? (readonly)
The PID of the server that gives access to the log file
Its port is allocated automatically, and must be discovered through the Roby interface
321 322 323 |
# File 'lib/roby/app.rb', line 321 def log_server_pid @log_server_pid end |
#log_server_port ⇒ Integer? (readonly)
The port on which the log server is started
It is by default started on an ephemeral port, that needs to be discovered by clients through the Roby interface's Interface#log_server_port
330 331 332 |
# File 'lib/roby/app.rb', line 330 def log_server_port @log_server_port end |
#log_timepoints=(value) ⇒ Object (writeonly)
Override #log_timepoints? as configured in app.yml
149 150 151 |
# File 'lib/roby/app.rb', line 149 def log_timepoints=(value) @log_timepoints = value end |
#module_name ⇒ Object
Returns the name of this app's toplevel module
283 284 285 |
# File 'lib/roby/app.rb', line 283 def module_name @module_name || app_name.camelcase(:upper) end |
#notification_listeners ⇒ #call (readonly)
Returns the blocks that listen to notifications. They are added with #on_notification and removed with #remove_notification_listener.
3409 3410 3411 |
# File 'lib/roby/app.rb', line 3409 def notification_listeners @notification_listeners end |
#options ⇒ Object (readonly)
Applicatio configuration information is stored in a YAML file config/app.yml. The options are saved in a hash.
This attribute contains the raw hash as read from the file. It is overlaid
125 126 127 |
# File 'lib/roby/app.rb', line 125 def end |
#plan ⇒ ExecutablePlan (readonly)
The main plan on which this application acts
106 107 108 |
# File 'lib/roby/app.rb', line 106 def plan @plan end |
#planners ⇒ Array (readonly)
A set of planners declared in this application
118 119 120 |
# File 'lib/roby/app.rb', line 118 def planners @planners end |
#plugins ⇒ Object (readonly)
An [name, module] array of the loaded plugins
503 504 505 |
# File 'lib/roby/app.rb', line 503 def plugins @plugins end |
#registered_exceptions ⇒ Array<(Exception,String)> (readonly)
A set of exceptions that have been encountered by the application The associated string, if given, is a hint about in which context this exception got raised
133 134 135 |
# File 'lib/roby/app.rb', line 133 def registered_exceptions @registered_exceptions end |
#require_handlers ⇒ Array<#call> (readonly)
Returns list of objects called when the app gets to require its models (i.e. after #require_models).
554 555 556 |
# File 'lib/roby/app.rb', line 554 def require_handlers @require_handlers end |
#rest_interface_host ⇒ String
The host to which the REST interface server should bind
947 948 949 |
# File 'lib/roby/app.rb', line 947 def rest_interface_host @rest_interface_host end |
#rest_interface_port ⇒ Integer
The port on which the REST interface server should be
951 952 953 |
# File 'lib/roby/app.rb', line 951 def rest_interface_port @rest_interface_port end |
#robots ⇒ App::RobotNames (readonly)
The robot names configuration
1430 1431 1432 |
# File 'lib/roby/app.rb', line 1430 def robots @robots end |
#search_path ⇒ Array<String>
The list of paths in which the application should be looking for files
422 423 424 425 426 427 428 429 430 |
# File 'lib/roby/app.rb', line 422 def search_path if @search_path @search_path elsif app_dir [app_dir] else [] end end |
#setup_handlers ⇒ Array<#call> (readonly)
Returns list of objects called when the app gets initialized (i.e. in #setup after #base_setup).
550 551 552 |
# File 'lib/roby/app.rb', line 550 def setup_handlers @setup_handlers end |
#shell_interface ⇒ Object (readonly)
The TCP server that gives access to the Interface
954 955 956 |
# File 'lib/roby/app.rb', line 954 def shell_interface @shell_interface end |
#shell_interface_fd ⇒ Integer
A file descriptor that should be used as-is for the interface server
It supersedes the setting for #shell_interface_port and #shell_interface_host
986 987 988 |
# File 'lib/roby/app.rb', line 986 def shell_interface_fd @shell_interface_fd end |
#shell_interface_host ⇒ String
The host to which the shell interface server should bind
This is ignored if #shell_interface_fd is set
969 970 971 |
# File 'lib/roby/app.rb', line 969 def shell_interface_host @shell_interface_host end |
#shell_interface_port ⇒ Integer
The port on which the shell interface server should be
This is ignored if #shell_interface_fd is set
977 978 979 |
# File 'lib/roby/app.rb', line 977 def shell_interface_port @shell_interface_port end |
#shell_interface_v2 ⇒ Object (readonly)
The TCP server that gives access to main remote Interface using the v2 protocol
Unless #shell_interface_v2_fd is set, the interface listens to #shell_interface_host and #shell_interface_port + 2
1000 1001 1002 |
# File 'lib/roby/app.rb', line 1000 def shell_interface_v2 @shell_interface_v2 end |
#shell_interface_v2_fd ⇒ Integer
A file descriptor that should be used as-is for the v2 interface server
Unless #shell_interface_v2_fd is set, the interface listens to #shell_interface_host and #shell_interface_port + 2
1024 1025 1026 |
# File 'lib/roby/app.rb', line 1024 def shell_interface_v2_fd @shell_interface_v2_fd end |
#shell_interface_v2_host ⇒ String
The host to which the v2 shell interface server should bind
This is ignored if #shell_interface_v2_fd is set
1008 1009 1010 |
# File 'lib/roby/app.rb', line 1008 def shell_interface_v2_host @shell_interface_v2_host end |
#shell_interface_v2_port ⇒ Integer
The port on which the v2 shell interface server should be
This is ignored if #shell_interface_v2_fd is set
1016 1017 1018 |
# File 'lib/roby/app.rb', line 1016 def shell_interface_v2_port @shell_interface_v2_port end |
#shutdown_handlers ⇒ Array<#call> (readonly)
Returns list of objects called when the app shuts down, that is when the plan is being tore down but before cleanup. This is called during test teardown as well.
567 568 569 |
# File 'lib/roby/app.rb', line 567 def shutdown_handlers @shutdown_handlers end |
#ui_event_listeners ⇒ #call (readonly)
Returns the blocks that listen to ui events. They are added with #on_ui_event and removed with #remove_ui_event.
3365 3366 3367 |
# File 'lib/roby/app.rb', line 3365 def ui_event_listeners @ui_event_listeners end |
Class Method Details
.apply_conf_from_argv(value, conf: Conf) ⇒ Object
Set a value in a conf object (defaults to Conf) from a parameter given on the command line
755 756 757 758 759 760 |
# File 'lib/roby/app.rb', line 755 def self.apply_conf_from_argv(value, conf: Conf) key, value = value.split("=") path = key.split(".") base_conf = path[0..-2].inject(conf) { |c, name| c.send(name) } base_conf.send("#{path[-1]}=", YAML.load(value)) end |
.attr_config(config_key) ⇒ Object
Allows to attribute configuration keys to override configuration parameters stored in config/app.yml
For instance,
attr_config 'log'
creates a log_overrides attribute, which contains a hash. Any value stored in this hash will override those stored in the config file. The final value can be accessed by accessing the generated #config_key method:
E.g.,
in config/app.yml:
log:
dir: test
in config/init.rb:
Roby.app.log_overrides['dir'] = 'bla'
Then, Roby.app.log will return { 'dir' => 'bla }
This override mechanism is not meant to be used directly by the user, but as a tool for the rest of the framework
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/roby/app.rb', line 189 def self.attr_config(config_key) config_key = config_key.to_s # Ignore if already done return if method_defined?("#{config_key}_overrides") attribute("#{config_key}_overrides") { {} } define_method(config_key) do plain = self.[config_key] || {} overrides = instance_variable_get "@#{config_key}_overrides" if overrides plain.recursive_merge(overrides) else plain end end end |
.common_optparse_add_stackprof(parser) ⇒ Object
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 |
# File 'lib/roby/app.rb', line 676 def self.common_optparse_add_stackprof(parser) parser.on( "--stackprof-start=SPEC", String, "start stackprof on timepoints matching SPEC." \ "Enclose with // to have the string interpreted as regexp" ) do |spec| Roby.app.stackprof_event_manager.start_on(resolve_matcher_argument(spec)) end parser.on( "--stackprof-stop=SPEC", String, "stop stackprof on timepoints matching SPEC." \ "Enclose with // to have the string interpreted as regexp" ) do |spec| Roby.app.stackprof_event_manager.stop_on(resolve_matcher_argument(spec)) end parser.on( "--stackprof-skip=COUNT", Integer, "skip this many start/stop cycles before starting to profile" ) do |count| Roby.app.stackprof_event_manager.skip = count end parser.on( "--stackprof-count=COUNT", Integer, "dump results when we reach this many start/stop cycles. The default " \ "is to dump results on application stop" ) do |count| Roby.app.stackprof_event_manager.count = count end parser.on( "--stackprof-result=PATH", String, "save stackprof results on PATH" ) do |path| Roby.app.stackprof_event_manager.result_path = path end parser.on( "--stackprof-mode=MODE", String, "use the given mode for stackprof profiling" ) do |mode| Roby.app.stackprof_event_manager.mode = mode end parser.on( "--stackprof-raw", "enable raw mode during stackprof profiling" ) do |mode| Roby.app.stackprof_event_manager.raw = true end parser.on( "--stackprof-quit", "automatically quit the application when the profile data has been " \ "dumped" ) do |mode| Roby.app.stackprof_event_manager.quit = true end end |
.common_optparse_add_tracing(parser) ⇒ Object
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 |
# File 'lib/roby/app.rb', line 640 def self.common_optparse_add_tracing(parser) parser.on( "--trace-event=SPEC", String, "display on STDOUT information about events whose name matches " \ "SPEC. Enclose with // to have the string interpreted as regexp" ) do |spec| Roby.app.io_event_logger.event_display(resolve_matcher_argument(spec)) end parser.on( "--trace-timepoint=SPEC", String, "display on STDOUT information about timepoint whose name matches " \ "SPEC. Enclose with // to have the string interpreted as regexp" ) do |spec| Roby.app.io_event_logger.timepoint_display(resolve_matcher_argument(spec)) end parser.on( "--trace-timepoint-group=SPEC", String, "display on STDOUT information about timepoint groups whose name " \ "matches SPEC. Enclose with // to have the string interpreted as regexp" ) do |spec| matcher, arguments = resolve_timepoint_group_argument(spec) Roby.app.io_event_logger.timegroup_display( resolve_matcher_argument(matcher), **arguments ) end end |
.common_optparse_setup(parser) ⇒ Object
Defines common configuration options valid for all Roby-oriented scripts
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 |
# File 'lib/roby/app.rb', line 601 def self.common_optparse_setup(parser) Roby.app.load_config_yaml parser.on( "--set=KEY=VALUE", String, "set a value on the Conf object" ) do |value| Roby.app.argv_set << value apply_conf_from_argv(value) end parser.on( "--log=SPEC", String, "configuration specification for text loggers. SPEC is of the form "\ "path/to/a/module:LEVEL[:FILE][,path/to/another]" ) do |log_spec| log_spec.split(",").each do |spec| mod, level, file = spec.split(":") Roby.app.log_setup(mod, level, file) end end parser.on( "-r NAME", "--robot=NAME[,TYPE]", String, "the robot name and type" ) do |name| robot_name, robot_type = name.split(",") Roby.app.setup_robot_names_from_config_dir Roby.app.robot(robot_name, robot_type) end parser.on("--debug", "run in debug mode") do Roby.app.public_logs = true Roby.app.filter_backtraces = false require "roby/app/debug" end parser.on_tail("-h", "--help", "this help message") do STDERR.puts parser exit end common_optparse_add_tracing(parser) common_optparse_add_stackprof(parser) end |
.find_data(*name) ⇒ Object
3062 3063 3064 3065 3066 3067 3068 3069 |
# File 'lib/roby/app.rb', line 3062 def self.find_data(*name) name = File.join(*name) Roby::Conf.datadirs.each do |dir| path = File.join(dir, name) return path if File.exist?(path) end raise Errno::ENOENT, "no file #{name} found in #{Roby::Conf.datadirs.join(':')}" end |
.guess_app_dir ⇒ String?
Guess the app directory based on the current directory
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/roby/app.rb', line 348 def self.guess_app_dir if (test_dir = ENV["ROBY_APP_DIR"]) unless Application.is_app_dir?(test_dir) raise InvalidRobyAppDirEnv, "the ROBY_APP_DIR envvar is set to #{test_dir}, but this "\ "is not a valid Roby application path" end return test_dir end path = Pathname.new(Dir.pwd).find_matching_parent do |candidate| Application.is_app_dir?(candidate.to_s) end path&.to_s end |
.host_options(parser, options, interface_versions: false) ⇒ Object
Sets up provided option parser to add the --host and --vagrant option
When added, a :host entry will be added to the provided options hash
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 |
# File 'lib/roby/app.rb', line 765 def self.(parser, , interface_versions: false) if interface_versions [:interface_version] ||= 1 else [:host] ||= Roby.app.shell_interface_host || "localhost" [:port] ||= Roby.app.shell_interface_port || Interface::DEFAULT_PORT end if interface_versions parser.on("--interface-version=VERSION", Integer, "the remote interface version to use") do |version| [:interface_version] = version end end parser.on( "--host URL", String, "sets the host to connect to as hostname[:PORT]" ) do |url| if url =~ /(.*):(\d+)$/ [:host] = $1 [:port] = Integer($2) else [:host] = url end end parser.on( "--vagrant NAME[:PORT]", String, "connect to a vagrant VM" ) do |vagrant_name| require "roby/app/vagrant" if vagrant_name =~ /(.*):(\d+)$/ vagrant_name, port = $1, Integer($2) end [:host] = Roby::App::Vagrant.resolve_ip(vagrant_name) [:port] = port end end |
.host_options_set_defaults(options) ⇒ Object
Fill defaults in the option hash setup by host_options
This must be called if interface_versions is set
832 833 834 835 836 837 838 839 840 841 842 843 844 |
# File 'lib/roby/app.rb', line 832 def self.() return unless [:interface_version] if [:interface_version] == 1 [:host] ||= Roby.app.shell_interface_host || "localhost" [:port] ||= Roby.app.shell_interface_port || Interface::DEFAULT_PORT else [:host] ||= Roby.app.shell_interface_v2_host || "localhost" [:port] ||= Roby.app.shell_interface_v2_port || Interface::DEFAULT_PORT end end |
.is_app_dir?(test_dir) ⇒ Boolean
Tests if the given directory looks like the root of a Roby app
335 336 337 338 339 340 |
# File 'lib/roby/app.rb', line 335 def self.is_app_dir?(test_dir) File.file?(File.join(test_dir, "config", "app.yml")) || File.directory?(File.join(test_dir, "models")) || File.directory?(File.join(test_dir, "scripts", "controllers")) || File.directory?(File.join(test_dir, "config", "robots")) end |
.lockfile_present?(path) ⇒ Boolean
Tests whether the given folder has a lockfile created in it
1217 1218 1219 |
# File 'lib/roby/app.rb', line 1217 def self.lockfile_present?(path) File.exist?(File.join(path, LOCK_FILE_EXT)) end |
.log_dir_locked?(path, strict: false) ⇒ Boolean
Checks if a path is currently locked by a Roby application
The main mechanism is to check if there is a file lock on a file called LOCK_FILE_EXT.
However, to handle the moment between log dir creation and the creation
of this file, the method will return true if the file does not exist
unless strict is true
1234 1235 1236 1237 1238 1239 1240 1241 |
# File 'lib/roby/app.rb', line 1234 def self.log_dir_locked?(path, strict: false) lock_file_path = File.join(path, LOCK_FILE_EXT) return !strict unless File.exist?(lock_file_path) File.open(lock_file_path, "r") do |file| !file.flock(File::LOCK_SH | File::LOCK_NB) end end |
.overridable_configuration(config_set, config_key, options = {}) ⇒ Object
Defines accessors for a configuration parameter stored in #options
This method allows to define a getter and a setter for a parameter stored in #options that should be user-overridable. It builds upon #attr_config.
For instance:
overridable_configuration 'log', 'filter_backtraces'
will create a #filter_backtraces getter and a #filter_backtraces= setter which allow to respectively access the log/filter_backtraces configuration value, and override it from its value in config/app.yml
The :predicate option allows to make the setter look like a predicate:
overridable_configuration 'log', 'filter_backtraces', predicate: true
will define #filter_backtraces? instead of #filter_backtraces
226 227 228 229 230 231 232 233 234 235 |
# File 'lib/roby/app.rb', line 226 def self.overridable_configuration(config_set, config_key, = {}) = Kernel. , predicate: false, attr_name: config_key attr_config(config_set) define_method("#{options[:attr_name]}#{'?' if options[:predicate]}") do send(config_set)[config_key] end define_method("#{options[:attr_name]}=") do |new_value| send("#{config_set}_overrides")[config_key] = new_value end end |
.read_current_dir(current_path) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Read and validate the 'current' dir by means of the 'current' symlink that Roby maintains in its log base directory
1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 |
# File 'lib/roby/app.rb', line 1919 def self.read_current_dir(current_path) unless File.symlink?(current_path) raise ArgumentError, "#{current_path} does not exist or is not a symbolic link" end resolved_path = File.readlink(current_path) if !File.exist?(resolved_path) raise ArgumentError, "#{current_path} points to #{resolved_path}, which does not exist" elsif !File.directory?(resolved_path) raise ArgumentError, "#{current_path} points to #{resolved_path}, "\ "which is not a directory" end resolved_path end |
.register_plugin(name, mod, &init) ⇒ Object
3081 3082 3083 3084 3085 |
# File 'lib/roby/app.rb', line 3081 def self.register_plugin(name, mod, &init) caller_m = /^([^:]+):\d/.match(caller(1)[0]) path = File.(File.dirname(caller_m[1])) Roby.app.register_plugin(name, mod, path: path, &init) end |
.resolve_matcher_argument(arg) ⇒ String, Regexp
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Resolve a command-line argument into a string matcher (regexp or plain string)
738 739 740 741 742 743 744 |
# File 'lib/roby/app.rb', line 738 def self.resolve_matcher_argument(arg) if (m = %r{^/(.*)/$}.match(arg)) Regexp.new(m[1]) else arg end end |
.resolve_timepoint_group_argument(spec) ⇒ Object
667 668 669 670 671 672 673 674 |
# File 'lib/roby/app.rb', line 667 def self.resolve_timepoint_group_argument(spec) matcher, *arguments_s = spec.split(",") arguments = arguments_s.to_h do |arg_s| k, v = arg_s.split("=") [k.to_sym, JSON.parse(v)] end [matcher, arguments] end |
.unique_dirname(base_dir, path_spec, date_tag = nil) ⇒ Object
Returns a unique directory name as a subdirectory of
base_dir, based on path_spec. The generated name
is of the form
<base_dir>/a/b/c/YYYYMMDD-HHMM-basename
if path_spec = "a/b/c/basename". A .
1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 |
# File 'lib/roby/app.rb', line 1947 def self.unique_dirname(base_dir, path_spec, date_tag = nil) if path_spec =~ %r{/$} basename = "" dirname = path_spec else basename = File.basename(path_spec) dirname = File.dirname(path_spec) end date_tag ||= Time.now.strftime("%Y%m%d-%H%M") basename = if !basename.empty? "#{date_tag}-#{basename}" else date_tag end # Check if +basename+ already exists, and if it is the case add a # .x suffix to it full_path = File.(File.join(dirname, basename), base_dir) final_path = full_path i = 0 while File.exist?(final_path) i += 1 final_path = full_path + ".#{i}" end final_path end |
Instance Method Details
#abort_on_application_exception=(flag) ⇒ Object
Controls whether the Roby app should quit if an application (i.e. non-plan) exception is received
The default is true
524 |
# File 'lib/roby/app.rb', line 524 attr_predicate :abort_on_application_exception, true |
#abort_on_application_exception? ⇒ Object
Controls whether the Roby app should quit if an application (i.e. non-plan) exception is received
The default is true
524 |
# File 'lib/roby/app.rb', line 524 attr_predicate :abort_on_application_exception, true |
#abort_on_exception=(flag) ⇒ Object
Controls whether the application should quit if an unhandled plan exception is received
The default is false
515 |
# File 'lib/roby/app.rb', line 515 attr_predicate :abort_on_exception, true |
#abort_on_exception? ⇒ Object
Controls whether the application should quit if an unhandled plan exception is received
The default is false
515 |
# File 'lib/roby/app.rb', line 515 attr_predicate :abort_on_exception, true |
#action_from_model(model) ⇒ Actions::Models::Action
Find an action on the planning interface that can generate the given task model
3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 |
# File 'lib/roby/app.rb', line 3268 def action_from_model(model) candidates = [] planners.each do |planner_model| planner_model.find_all_actions_by_type(model).each do |action| candidates << [planner_model, action] end end candidates = candidates.uniq if candidates.empty? raise ActionResolutionError, "cannot find an action to produce #{model}" elsif candidates.size > 1 candidates_s = candidates .map { |pl, m| "#{pl}.#{m.name}" } .sort.join(", ") raise ActionResolutionError, "more than one actions available produce #{model}: #{candidates_s}" else candidates.first end end |
#action_from_name(name) ⇒ Actions::Models::Action
Finds the action matching the given name
Unlike #find_action_from_name, it raises if no matching action has been found
3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 |
# File 'lib/roby/app.rb', line 3321 def action_from_name(name) action = find_action_from_name(name) unless action available_actions = planners.map do |planner_model| planner_model.each_action.map(&:name) end.flatten if available_actions.empty? raise ActionResolutionError, "cannot find an action named #{name}, "\ "there are no actions defined" else raise ActionResolutionError, "cannot find an action named #{name}, "\ "available actions are: #{available_actions.sort.join(', ')}" end end action end |
#actions(user: false, &block) ⇒ Object
Declares that the following block should be used to setup the main action interface
1479 1480 1481 |
# File 'lib/roby/app.rb', line 1479 def actions(user: false, &block) add_lifecyle_hook(action_handlers, block, user: user) end |
#add_app_metadata(metadata, append: true) ⇒ Object
Add some metadata to #app_metadata, and save it to the log dir's info.yml if it is already created
1808 1809 1810 1811 1812 1813 |
# File 'lib/roby/app.rb', line 1808 def (, append: true) .merge!() if created_log_dir? (append: append) end end |
#add_lifecyle_hook(hook_set, block, **options) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Registers a lifecycle hook in the provided list of hooks
3450 3451 3452 3453 |
# File 'lib/roby/app.rb', line 3450 def add_lifecyle_hook(hook_set, block, **) hook_set << (hook = LifecycleHook.new(block, **)) Roby.disposable { hook_set.delete(hook) } end |
#add_plugin(name, mod) ⇒ Object
1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 |
# File 'lib/roby/app.rb', line 1656 def add_plugin(name, mod) plugins << [name, mod] extend mod # If +load+ has already been called, call it on the module if mod.respond_to?(:load) && mod.load(self, ) end # Refresh the relation sets in #plan to include relations # possibly added by the plugin plan.refresh_relations mod end |
#app_dir ⇒ String?
Returns the application base directory
303 304 305 306 307 308 309 |
# File 'lib/roby/app.rb', line 303 def app_dir if defined?(APP_DIR) APP_DIR elsif @app_dir @app_dir end end |
#app_dir=(dir) ⇒ Object
Allows to override the application base directory. See #app_dir
238 239 240 241 |
# File 'lib/roby/app.rb', line 238 def app_dir=(dir) @app_path = nil @app_dir = dir end |
#app_file?(path) ⇒ Boolean
Returns true if the given path points to a file in the Roby app
3116 3117 3118 |
# File 'lib/roby/app.rb', line 3116 def app_file?(path) !!find_base_path_for(path) end |
#app_metadata ⇒ Object
Metadata used to describe the app
It is saved in the app's log directory under info.yml
1820 1821 1822 1823 1824 |
# File 'lib/roby/app.rb', line 1820 def Hash["time" => time_tag, "cmdline" => "#{$0} #{ARGV.join(' ')}", "robot_name" => robot_name, "robot_type" => robot_type, "app_name" => app_name, "app_dir" => app_dir].merge() end |
#app_module ⇒ Object
Returns this app's toplevel module
288 289 290 |
# File 'lib/roby/app.rb', line 288 def app_module constant("::#{module_name}") end |
#app_path ⇒ Object
311 312 313 |
# File 'lib/roby/app.rb', line 311 def app_path @app_path ||= Pathname.new(app_dir) if app_dir end |
#apply_argv_set(conf: Conf) ⇒ Object
Apply the --set command line parameters stored in #argv_set to a conf object
747 748 749 750 751 |
# File 'lib/roby/app.rb', line 747 def apply_argv_set(conf: Conf) argv_set.each do |value| self.class.apply_conf_from_argv(value, conf: conf) end end |
#apply_config(config) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Sets relevant configuration values from a configuration hash
2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 |
# File 'lib/roby/app.rb', line 2296 def apply_config(config) if (host_port = config["interface"]) apply_config_interface(host_port) elsif (host_port = config.fetch("droby", {})["host"]) Roby.warn_deprecated( "the droby.host configuration parameter in config/app.yml "\ "is deprecated, use 'interface' at the toplevel instead" ) apply_config_interface(host_port) end end |
#apply_config_interface(host_port) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Parses and applies the 'interface' value from a configuration hash
It is a helper for #apply_config
2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 |
# File 'lib/roby/app.rb', line 2313 def apply_config_interface(host_port) if host_port !~ /:\d+$/ host_port += ":#{Interface::DEFAULT_PORT}" end match = /(.*):(\d+)$/.match(host_port) host = match[1] @shell_interface_host = unless host.empty? host end @shell_interface_port = Integer(match[2]) end |
#auto_load_all=(flag) ⇒ Boolean
Controls whether Roby's auto-load feature should load all models in #search_path or only the ones in #app_dir. It influences the return value of #auto_load_search_path
3029 |
# File 'lib/roby/app.rb', line 3029 attr_predicate :auto_load_all?, true |
#auto_load_all? ⇒ Boolean
Controls whether Roby's auto-load feature should load all models in #search_path or only the ones in #app_dir. It influences the return value of #auto_load_search_path
3029 |
# File 'lib/roby/app.rb', line 3029 attr_predicate :auto_load_all?, true |
#auto_load_models? ⇒ Boolean
Controls whether Roby should load the available the model files automatically in #require_models
3038 |
# File 'lib/roby/app.rb', line 3038 attr_writer :auto_load_models |
#auto_load_search_path ⇒ Array<String>
Returns the search path for the auto-load feature. It depends on the value of #auto_load_all?.
3050 3051 3052 3053 3054 3055 3056 |
# File 'lib/roby/app.rb', line 3050 def auto_load_search_path if auto_load_all? then search_path elsif app_dir then [app_dir] else [] end end |
#auto_require_models ⇒ Object
2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 |
# File 'lib/roby/app.rb', line 2148 def auto_require_models # Require all common task models and the task models specific to # this robot if auto_load_models? load_all_model_files_in("tasks") if backward_compatible_naming? search_path = self.auto_load_search_path all_files = find_files_in_dirs("tasks", "ROBOT", path: search_path, all: true, order: :specific_last, pattern: /\.rb$/) all_files.each do |p| require(p) end end call_plugins(:auto_require_models, self) end end |
#auto_require_planners ⇒ Object
2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 |
# File 'lib/roby/app.rb', line 2249 def auto_require_planners self.auto_load_search_path prefixes = ["actions"] if backward_compatible_naming? prefixes << "planners" end prefixes.each do |prefix| load_all_model_files_in(prefix) end if backward_compatible_naming? main_files = find_files("planners", "ROBOT", "main.rb", all: true, order: :specific_first) main_files.each do |path| require path end planner_files = find_files_in_dirs("planners", "ROBOT", all: true, order: :specific_first, pattern: /\.rb$/) planner_files.each do |path| require path end end call_plugins(:require_planners, self) end |
#autodiscover_tests_in?(path, extra_robot_names: []) ⇒ Boolean
Hook for the plugins to filter out some paths that should not be auto-loaded by #each_test_file_in_app. It does not affect #each_test_file_for_loaded_models.
3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 |
# File 'lib/roby/app.rb', line 3514 def autodiscover_tests_in?(path, extra_robot_names: []) suffix = File.basename(path) robot_names = [robot_name, robot_type] + extra_robot_names if path == File.join(app_dir, "test", "robots") false elsif robots.has_robot?(suffix) && !robot_names.include?(suffix) false elsif defined? super super else true end end |
#automatic_testing=(flag) ⇒ Object
True if user interaction is disabled during tests
530 |
# File 'lib/roby/app.rb', line 530 attr_predicate :automatic_testing?, true |
#automatic_testing? ⇒ Object
True if user interaction is disabled during tests
530 |
# File 'lib/roby/app.rb', line 530 attr_predicate :automatic_testing?, true |
#backward_compatible_naming=(flag) ⇒ Object
If set to true, the app will enable backward-compatible behaviour related to naming schemes, file placements and so on
The default is true
260 |
# File 'lib/roby/app.rb', line 260 attr_predicate :backward_compatible_naming?, true |
#backward_compatible_naming? ⇒ Object
If set to true, the app will enable backward-compatible behaviour related to naming schemes, file placements and so on
The default is true
260 |
# File 'lib/roby/app.rb', line 260 attr_predicate :backward_compatible_naming?, true |
#base_cleanup ⇒ Object
The inverse of #base_setup
1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 |
# File 'lib/roby/app.rb', line 1244 def base_cleanup unless base_setup_done? Roby.warn "base_cleanup should not be called before base_setup." return end unless public_logs? created_log_dirs.delete_if do |dir| FileUtils.rm_rf dir true end created_log_base_dirs.sort_by(&:length).reverse_each do |dir| # .rmdir will ignore nonempty / nonexistent directories on # 2.3 but on 2.5 you have to rescue. begin FileUtils.rmdir(dir) rescue Errno::ENOTEMPTY # rubocop:disable Lint/SuppressedException end created_log_base_dirs.delete(dir) end @log_dir = nil end log_files.each_value(&:close) log_files.clear @base_setup_done = false end |
#base_setup ⇒ Object
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 |
# File 'lib/roby/app.rb', line 1175 def base_setup if base_setup_done? Roby.warn "base_setup should only be done once." return end STDOUT.sync = true load_base_config find_and_create_log_dir unless @log_dir if Application.lockfile_present?(@log_dir) raise "configured log directory #{@log_dir} already has a lockfile, " \ "this should not happen" end lock_log_dir setup_loggers(redirections: true) # Set up the loaded plugins call_plugins(:base_setup, self) @base_setup_done = true end |
#base_setup_done? ⇒ Boolean
1171 1172 1173 |
# File 'lib/roby/app.rb', line 1171 def base_setup_done? @base_setup_done end |
#call_plugins(method, *args, deprecated: nil) ⇒ Object
Call method on each loaded extension module which define it, with
arguments args
1588 1589 1590 1591 1592 1593 1594 1595 1596 |
# File 'lib/roby/app.rb', line 1588 def call_plugins(method, *args, deprecated: nil) each_responding_plugin(method) do |config_extension| if deprecated Roby.warn "#{config_extension} uses the deprecated .#{method} "\ "hook during setup and teardown, #{deprecated}" end config_extension.send(method, *args) end end |
#cleanup ⇒ Object
The inverse of #setup. It gets called at the end of #run
1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 |
# File 'lib/roby/app.rb', line 1316 def cleanup # Run the cleanup handlers first, we want the plugins to still be # active cleanup_handlers.each(&:call) cleanup_user_lifecycle_hooks(init_handlers) cleanup_user_lifecycle_hooks(setup_handlers) cleanup_user_lifecycle_hooks(require_handlers) cleanup_user_lifecycle_hooks(controllers) cleanup_user_lifecycle_hooks(action_handlers) cleanup_user_lifecycle_hooks(cleanup_handlers) call_plugins(:cleanup, self) # Deprecated version of #cleanup call_plugins(:reset, self, deprecated: "define 'cleanup' instead") planners.clear plan.execution_engine.gather_propagation do plan.clear end clear_models clear_config stop_shell_interface base_cleanup end |
#cleanup_user_lifecycle_hooks(hook_set) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Removes all lifecycle hooks that are marked as user hooks
2606 2607 2608 |
# File 'lib/roby/app.rb', line 2606 def cleanup_user_lifecycle_hooks(hook_set) hook_set.delete_if(&:user?) end |
#clear_config ⇒ Object
3142 3143 3144 3145 3146 3147 3148 3149 |
# File 'lib/roby/app.rb', line 3142 def clear_config Conf.clear call_plugins(:clear_config, self) # Deprecated name for clear_config call_plugins(:reload_config, self) unload_features("config", ".*\.rb$") unload_features("config", "robot", ".*\.rb$") end |
#clear_exceptions ⇒ Object
2043 2044 2045 |
# File 'lib/roby/app.rb', line 2043 def clear_exceptions registered_exceptions.clear end |
#clear_model?(m) ⇒ Boolean
Whether this model should be cleared in #clear_models
The default implementation returns true for the models that are not registered as constants (more precisely, for which MetaRuby's #permanent_model? returns false) and for the models defined in this app.
3210 3211 3212 3213 |
# File 'lib/roby/app.rb', line 3210 def clear_model?(m) !m.permanent_model? || (!testing? && model_defined_in_app?(m)) end |
#clear_models ⇒ Object
Clear all models for which #clear_model? returns true
3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 |
# File 'lib/roby/app.rb', line 3216 def clear_models root_models.each do |root_model| submodels = root_model.each_submodel.to_a.dup submodels.each do |m| if clear_model?(m) m.permanent_model = false m.clear_model end end end DRoby::V5::DRobyConstant.clear_cache clear_models_handlers.each(&:call) call_plugins(:clear_models, self) unload_features("models", ".*\.rb$") additional_model_files.each do |path| unload_features(path) end cleanup_user_lifecycle_hooks(clear_models_handlers) end |
#compute_absolute_paths(relative_paths, root_paths, prioritize_root_paths: false) ⇒ Object
2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 |
# File 'lib/roby/app.rb', line 2768 def compute_absolute_paths(relative_paths, root_paths, prioritize_root_paths: false) if prioritize_root_paths root_paths.flat_map do |root| relative_paths.map do |rel_path| File.(File.join(*rel_path), root) end end else relative_paths.flat_map do |rel_path| root_paths.map do |root| File.(File.join(*rel_path), root) end end end end |
#controller(reset: false, user: false, &block) ⇒ Object
Declares that the following block should be used as the robot controller
1472 1473 1474 1475 |
# File 'lib/roby/app.rb', line 1472 def controller(reset: false, user: false, &block) controllers.clear if reset add_lifecyle_hook(controllers, block, user: user) end |
#created_log_dir? ⇒ Boolean
Test whether this app already created its log directory
1827 1828 1829 |
# File 'lib/roby/app.rb', line 1827 def created_log_dir? @log_dir && File.directory?(@log_dir) end |
#define_actions_module ⇒ Object
2225 2226 2227 2228 2229 |
# File 'lib/roby/app.rb', line 2225 def define_actions_module unless app_module.const_defined_here?(:Actions) app_module.const_set(:Actions, Module.new) end end |
#define_main_planner_if_needed ⇒ Object
2231 2232 2233 2234 2235 2236 2237 2238 |
# File 'lib/roby/app.rb', line 2231 def define_main_planner_if_needed unless app_module::Actions.const_defined_here?(:Main) app_module::Actions.const_set(:Main, Class.new(Roby::Actions::Interface)) end if backward_compatible_naming? && !Object.const_defined_here?(:Main) Object.const_set(:Main, app_module::Actions::Main) end end |
#defined_plugin?(name) ⇒ Boolean
True if name is a plugin known to us
1563 1564 1565 |
# File 'lib/roby/app.rb', line 1563 def defined_plugin?(name) available_plugins.any? { |plugname, *_| plugname == name } end |
#definition_file_for(model) ⇒ Object
Returns the downmost app file that was involved in the given model's definition
2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 |
# File 'lib/roby/app.rb', line 2185 def definition_file_for(model) return if !model.respond_to?(:definition_location) || !model.definition_location model.definition_location.each do |location| file = location.absolute_path next unless (base_path = find_base_path_for(file)) relative = Pathname.new(file).relative_path_from(base_path) split = relative.each_filename.to_a next if split[0] != "models" return file end nil end |
#development_mode? ⇒ Object
Whether the app should run in development mode
Some expensive tests are disabled when not in development mode. This is the default
157 |
# File 'lib/roby/app.rb', line 157 attr_predicate :development_mode?, true |
#discover_test_files(all: true, only_self: false, base_dir: File.join(app_dir, "test"), extra_robot_names: []) ⇒ Array<String>
Discover which tests should be run, and require them
3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 |
# File 'lib/roby/app.rb', line 3485 def discover_test_files( all: true, only_self: false, base_dir: File.join(app_dir, "test"), extra_robot_names: [] ) if all test_files = each_test_file_in( base_dir, extra_robot_names: extra_robot_names ) test_files = test_files.with_object({}) do |t, h| h[t] = [] end unless only_self test_files.merge!(Hash[each_test_file_for_loaded_models.to_a]) end else test_files = Hash[each_test_file_for_loaded_models.to_a] if only_self test_files = test_files.find_all { |f, _| self_file?(f) } end end test_files end |
#each_model(root_model = nil) {|| ... } ⇒ Object
Enumerate all models registered in this app
It basically enumerate all submodels of all models in #root_models
3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 |
# File 'lib/roby/app.rb', line 3192 def each_model(root_model = nil, &block) return enum_for(__method__, root_model) unless block_given? unless root_model root_models.each { |m| each_model(m, &block) } return end yield(root_model) root_model.each_submodel(&block) end |
#each_notification_listener {|the| ... } ⇒ Object
Enumerates the listeners currently registered through #on_notification
3415 3416 3417 |
# File 'lib/roby/app.rb', line 3415 def each_notification_listener(&block) notification_listeners.each(&block) end |
#each_plugin(on_available = false) ⇒ Object
Enumerates all available plugins, yielding only the plugin module (i.e. the plugin object itself)
1569 1570 1571 1572 1573 1574 1575 1576 1577 |
# File 'lib/roby/app.rb', line 1569 def each_plugin(on_available = false) plugins = self.plugins if on_available plugins = available_plugins.map { |name, _, mod, _| [name, mod] } end plugins.each do |(_, mod)| yield(mod) end end |
#each_responding_plugin(method, _on_available = false) ⇒ Object
Yields each plugin object that respond to method
1580 1581 1582 1583 1584 |
# File 'lib/roby/app.rb', line 1580 def each_responding_plugin(method, _on_available = false) each_plugin do |mod| yield(mod) if mod.respond_to?(method) end end |
#each_robot_test_file_in(dir) ⇒ Object
Enumerate all the test files specific for this robot configuration
3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 |
# File 'lib/roby/app.rb', line 3553 def each_robot_test_file_in(dir) return enum_for(__method__, dir) unless block_given? return unless File.directory?(dir) robot_test_dir = File.join(dir, "robots") [robot_name, robot_type].each do |name| robot_test_file = File.join(robot_test_dir, "test_#{name}.rb") if File.file?(robot_test_file) yield(robot_test_file) break end end end |
#each_test_file_for_loaded_models {|path, models| ... } ⇒ Object
Enumerate the test files that should be run to test the current app configuration
3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 |
# File 'lib/roby/app.rb', line 3581 def each_test_file_for_loaded_models(&block) models_per_file = Hash.new { |h, k| h[k] = Set.new } each_model do |m| next if m.respond_to?(:has_ancestor?) && m.has_ancestor?(Roby::Event) next if m.respond_to?(:private_specialization?) && m.private_specialization? next unless m.name test_files_for(m).each do |test_path| models_per_file[test_path] << m end end models_per_file.each(&block) end |
#each_test_file_in(dir, extra_robot_names: [], &block) ⇒ Object
Enumerate all the test files in a specific dir for this robot configuration
3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 |
# File 'lib/roby/app.rb', line 3530 def each_test_file_in(dir, extra_robot_names: [], &block) unless block_given? return enum_for(__method__, dir, extra_robot_names: extra_robot_names) end each_robot_test_file_in(dir, &block) Find.find(dir) do |path| # Skip the robot-specific bits that don't apply on the # selected robot if File.directory?(path) && !autodiscover_tests_in?(path, extra_robot_names: extra_robot_names) Find.prune end if File.file?(path) && /^test_.*\.rb$/.match?(File.basename(path)) yield(path) end end end |
#each_test_file_in_app(&block) ⇒ Object
Enumerate all the test files in this app and for this robot configuration
3569 3570 3571 3572 |
# File 'lib/roby/app.rb', line 3569 def each_test_file_in_app(&block) dir = File.join(app_dir, "test") each_test_file_in(dir, &block) end |
#each_ui_event_listener {|the| ... } ⇒ Object
Enumerates the listeners currently registered through #on_ui_event
3371 3372 3373 |
# File 'lib/roby/app.rb', line 3371 def each_ui_event_listener(&block) ui_event_listeners.each(&block) end |
#enable_remote_interface_version(version) ⇒ Module
Called by tooling to enable the given remote interface version
806 807 808 809 810 811 812 813 814 815 816 817 818 |
# File 'lib/roby/app.rb', line 806 def enable_remote_interface_version(version) if version == 1 require "roby/interface/v1" Roby::Interface::V1 elsif version == 2 require "roby/interface/v2" call_plugins(:setup_interface_v2_protocol) require_v2_protocol_extensions Roby::Interface::V2 else raise ArgumentError, "remote interface version #{version} does not exist" end end |
#execution_engine ⇒ ExecutionEngine?
The engine associated with #plan
111 112 113 |
# File 'lib/roby/app.rb', line 111 def execution_engine plan&.execution_engine end |
#find_action_from_name(name) ⇒ (ActionInterface,Actions::Models::Action)?
Find an action with the given name on the action interfaces registered on #planners
3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 |
# File 'lib/roby/app.rb', line 3297 def find_action_from_name(name) candidates = [] planners.each do |planner_model| if (m = planner_model.find_action_by_name(name)) candidates << [planner_model, m] end end candidates = candidates.uniq return candidates.first if candidates.size <= 1 candidates_s = candidates.map { |pl, _| pl.to_s }.sort.join(", ") raise ActionResolutionError, "more than one action interface provide the #{name} "\ "action: #{candidates_s}" end |
#find_and_create_log_dir(time_tag = self.time_tag) ⇒ String
Create a log directory for the given time tag, and make it this app's log directory
The time tag given to this method also becomes the app's time tag
1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 |
# File 'lib/roby/app.rb', line 1728 def find_and_create_log_dir(time_tag = self.time_tag) base_dir = log_base_dir @time_tag = time_tag loop do log_dir = Roby::Application.unique_dirname(base_dir, "", time_tag) # the log dir itself MUST have been created by us. Make sure it is in # new_dirs to get it tested in the mkdir loop below new_dirs = [log_dir] dir = File.dirname(log_dir) until File.directory?(dir) new_dirs << dir dir = File.dirname(dir) end # Create all paths necessary, but check for possible concurrency # issues with other Roby-based tools creating a log dir with the # same name failed = new_dirs.reverse.any? do |path_element| begin FileUtils.mkdir(path_element) false rescue Errno::EEXIST true end end unless failed new_dirs.delete(log_dir) created_log_dirs << log_dir created_log_base_dirs.concat(new_dirs) @log_dir = log_dir return log_dir end end end |
#find_base_path_for(path) ⇒ nil, String
Returns the path in search_path that contains the given file or path
3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 |
# File 'lib/roby/app.rb', line 3091 def find_base_path_for(path) if @find_base_path_rx_paths != search_path @find_base_path_rx = search_path .map { |app_dir| [Pathname.new(app_dir), app_dir, %r{(^|/)#{app_dir}(/|$)}] } .sort_by { |_, app_dir, _| app_dir.size } .reverse @find_base_path_rx_paths = search_path.dup end longest_prefix_path, = @find_base_path_rx.find do |app_path, app_dir, rx| (path =~ rx) || ((path[0] != "/") && File.file?(File.join(app_dir, path))) end longest_prefix_path end |
#find_data(*name) ⇒ Object
3058 3059 3060 |
# File 'lib/roby/app.rb', line 3058 def find_data(*name) Application.find_data(*name) end |
#find_dir(*args) ⇒ Object
Returns the first match from #find_dirs, or nil if nothing matches
2949 2950 2951 2952 2953 2954 2955 2956 |
# File 'lib/roby/app.rb', line 2949 def find_dir(*args) unless args.last.kind_of?(Hash) args.push({}) end args.last.delete("all") args.last.merge!(all: true) find_dirs(*args).first end |
#find_files_in_dirs(*path, options) ⇒ Array<String>
Enumerates the subdirectories of paths in #search_path matching the given path. The subdirectories are resolved using File.join(*path) If one of the elements of the path is the string 'ROBOT', it gets replaced by the robot name and type.
Given a search dir of [app1, app2]
app1/models/tasks/goto.rb
app1/models/tasks/v3/goto.rb
app2/models/tasks/asguard/goto.rb
2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 |
# File 'lib/roby/app.rb', line 2700 def find_dirs(*dir_path) return [] if search_path.empty? Application.debug { "find_dirs(#{dir_path.map(&:inspect).join(', ')})" } if dir_path.last.kind_of?(Hash) = dir_path.pop end = Kernel.( || {}, :all, :order, :path, prioritize_root_paths: false) if dir_path.empty? raise ArgumentError, "no path given" end search_path = [:path] || self.search_path if !.has_key?(:all) raise ArgumentError, "no :all argument given" elsif !.has_key?(:order) raise ArgumentError, "no :order argument given" elsif !i[specific_first specific_last].include?([:order]) raise ArgumentError, "expected either :specific_first or :specific_last for the :order argument, but got #{options[:order]}" end relative_paths = [] base_dir_path = dir_path.dup base_dir_path.delete_if { |p| p =~ /ROBOT/ } relative_paths = [base_dir_path] if dir_path.any? { |p| p =~ /ROBOT/ } && robot_name && robot_type robot_configuration_names.each do |replacement| robot_dir_path = dir_path.map do |s| s.gsub("ROBOT", replacement) end relative_paths << robot_dir_path end end root_paths = search_path.dup if [:order] == :specific_first relative_paths = relative_paths.reverse else root_paths = root_paths.reverse end result = [] Application.debug { " relative paths: #{relative_paths.inspect}" } prioritize_root_paths = [:prioritize_root_paths] absolute_paths = compute_absolute_paths(relative_paths, root_paths, prioritize_root_paths: prioritize_root_paths) absolute_paths.each do |abs_path| Application.debug { " absolute path: #{abs_path}" } if File.directory?(abs_path) Application.debug { " selected" } result << abs_path end end if result.empty? result elsif ![:all] [result.first] else result end end |
#find_file(*args) ⇒ Object
Returns the first match from #find_files, or nil if nothing matches
2939 2940 2941 2942 2943 2944 2945 2946 |
# File 'lib/roby/app.rb', line 2939 def find_file(*args) unless args.last.kind_of?(Hash) args.push({}) end args.last.delete("all") args.last.merge!(all: true) find_files(*args).first end |
#find_files(*path, options) ⇒ Array<String>
Enumerates files based on their relative paths in #search_path. The paths are resolved using File.join(*path) If one of the elements of the path is the string 'ROBOT', it gets replaced by the robot name and type.
Given a search dir of [app1, app2], a robot name of v3 and a robot type of asguard,
app1/config/v3.rb
app2/config/asguard.rb
2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 |
# File 'lib/roby/app.rb', line 2884 def find_files(*file_path) return [] if search_path.empty? if file_path.last.kind_of?(Hash) = file_path.pop end = Kernel.( || {}, :all, :order, :path, prioritize_root_paths: false) if file_path.empty? raise ArgumentError, "no path given" end # Remove the filename from the complete path filename = file_path.pop filename = filename.split("/") file_path.concat(filename[0..-2]) filename = filename[-1] if filename =~ /ROBOT/ && robot_name args = file_path + [.merge(pattern: filename.gsub("ROBOT", robot_name))] robot_name_matches = find_files_in_dirs(*args) robot_type_matches = [] if robot_name != robot_type args = file_path + [.merge(pattern: filename.gsub("ROBOT", robot_type))] robot_type_matches = find_files_in_dirs(*args) end if [:order] == :specific_first result = robot_name_matches + robot_type_matches else result = robot_type_matches + robot_name_matches end else args = file_path.dup args << .merge(pattern: filename) result = find_files_in_dirs(*args) end orig_path = Pathname.new(File.join(*file_path)) orig_path += filename if orig_path.absolute? && File.file?(orig_path.to_s) if [:order] == :specific_first result.unshift orig_path.to_s else result.push orig_path.to_s end end result end |
#find_files_in_dirs(*path, options) ⇒ Array<String>
Enumerates the files that are present in subdirectories of paths in #search_path. The subdirectories are resolved using File.join(*path) If one of the elements of the path is the string 'ROBOT', it gets replaced by the robot name and type.
Given a search dir of [app1, app2]
app1/models/tasks/goto.rb
app1/models/tasks/v3/goto.rb
app2/models/tasks/asguard/goto.rb
2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 |
# File 'lib/roby/app.rb', line 2814 def find_files_in_dirs(*dir_path) return [] if search_path.empty? Application.debug { "find_files_in_dirs(#{dir_path.map(&:inspect).join(', ')})" } if dir_path.last.kind_of?(Hash) = dir_path.pop end = Kernel.( || {}, :all, :order, :path, prioritize_root_paths: false, pattern: Regexp.new("") ) dir_search = dir_path.dup dir_search << { all: true, order: [:order], path: [:path], prioritize_root_paths: [:prioritize_root_paths] } search_path = find_dirs(*dir_search) result = [] search_path.each do |dirname| Application.debug { " dir: #{dirname}" } Dir.new(dirname).each do |file_name| file_path = File.join(dirname, file_name) Application.debug { " file: #{file_path}" } if File.file?(file_path) && [:pattern] === file_name Application.debug " added" result << file_path end end break unless [:all] end result end |
#find_registered_app_path(app_name) ⇒ Object
473 474 475 476 477 478 479 |
# File 'lib/roby/app.rb', line 473 def find_registered_app_path(app_name) if app_name == self.app_name app_dir else @registered_apps[app_name] end end |
#framework_file?(path) ⇒ Boolean
Tests whether a path is within a framework library
3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 |
# File 'lib/roby/app.rb', line 3123 def framework_file?(path) if path =~ /roby\/.*\.rb$/ true else Roby.app.plugins.any? do |name, _| _, dir, = Roby.app.plugin_definition(name) path =~ %r{(^|/)#{dir}(/|$)} end end end |
#guess_app_dir ⇒ String
Guess the app directory based on the current directory, and sets #app_dir. It will not do anything if the current directory is not in a Roby app. Moreover, it does nothing if #app_dir is already set
375 376 377 378 379 |
# File 'lib/roby/app.rb', line 375 def guess_app_dir return if @app_dir @app_dir = self.class.guess_app_dir end |
#has_app? ⇒ Boolean
Whether there is a supporting app directory
366 367 368 |
# File 'lib/roby/app.rb', line 366 def has_app? !!@app_dir end |
#has_registered_app?(app_name) ⇒ Boolean
469 470 471 |
# File 'lib/roby/app.rb', line 469 def has_registered_app?(app_name) @registered_apps.has_key?(app_name) end |
#ignore_all_load_errors=(flag) ⇒ Object
If set to true, files that generate errors while loading will be ignored. This is used for model browsing GUIs to be usable even if there are errors
It is false by default
251 |
# File 'lib/roby/app.rb', line 251 attr_predicate :ignore_all_load_errors?, true |
#ignore_all_load_errors? ⇒ Object
If set to true, files that generate errors while loading will be ignored. This is used for model browsing GUIs to be usable even if there are errors
It is false by default
251 |
# File 'lib/roby/app.rb', line 251 attr_predicate :ignore_all_load_errors?, true |
#io_event_logger ⇒ EventLogging::IOEventLogger
Return an IOEventLogger that listens to the log events of #plan and #execution_engine
It gets created on the fly to avoid the overhead when not in use
874 875 876 877 878 879 880 881 882 883 |
# File 'lib/roby/app.rb', line 874 def io_event_logger unless @io_event_logger require "roby/event_logging/io_event_logger" @io_event_logger = EventLogging::IOEventLogger.new plan.event_logger.add(@io_event_logger) execution_engine.event_logger.add(@io_event_logger) end @io_event_logger end |
#isolate_load_errors(message, logger = Application, level = :warn) ⇒ Object
2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 |
# File 'lib/roby/app.rb', line 2047 def isolate_load_errors(, logger = Application, level = :warn) yield rescue Interrupt raise rescue ::Exception => e # rubocop:disable Lint/RescueException register_exception(e, ) if ignore_all_load_errors? Robot.warn Roby.log_exception_with_backtrace(e, logger, level) else raise end end |
#load_all_model_files_in(prefix_name, ignored_exceptions: []) ⇒ Object
2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 |
# File 'lib/roby/app.rb', line 2112 def load_all_model_files_in(prefix_name, ignored_exceptions: []) search_path = auto_load_search_path dirs = find_dirs( "models", prefix_name, path: search_path, all: true, order: :specific_last ) dirs.each do |dir| all_files = Set.new Find.find(dir) do |path| # Skip the robot-specific bits that don't apply on the # selected robot if File.directory?(path) suffix = File.basename(File.dirname(path)) if robots.has_robot?(suffix) && ![robot_name, robot_type].include?(suffix) Find.prune end end if File.file?(path) && path =~ /\.rb$/ all_files << path end end all_files.each do |path| begin require(path) rescue *ignored_exceptions => e ::Robot.warn "ignored file #{path}: #{e.message}" end end end end |
#load_base_config ⇒ Object
Loads the base configuration
This method loads the two most basic configuration files:
- config/app.yml
- config/init.rb
It also calls the plugin's 'load' method
1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 |
# File 'lib/roby/app.rb', line 1119 def load_base_config load_config_yaml setup_loggers(ignore_missing: true, redirections: false) setup_robot_names_from_config_dir # Get the application-wide configuration if plugins_enabled? register_plugins end update_load_path if (initfile = find_file("config", "init.rb", order: :specific_first)) Application.info "loading init file #{initfile}" Kernel.require initfile end update_load_path # Deprecated hook call_plugins(:load, self, deprecated: "define 'load_base_config' instead") call_plugins(:load_base_config, self) update_load_path if defined? Roby::Conf Roby::Conf.datadirs = find_dirs("data", "ROBOT", all: true, order: :specific_first) end if has_app? require_robot_file end init_handlers.each(&:call) update_load_path # Define the app module if there is none, and define a root logger # on it app_module = begin self.app_module rescue NameError Object.const_set(module_name, Module.new) end unless app_module.respond_to?(:logger) module_name = self.module_name app_module.class_eval do extend ::Logger::Root(module_name, Logger::INFO) end end end |
#load_config_yaml ⇒ Object
2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 |
# File 'lib/roby/app.rb', line 2273 def load_config_yaml file = find_file("config", "app.yml", order: :specific_first) return unless file Application.info "loading config file #{file}" = YAML.safe_load(File.read(file)) || {} @robots.load_config_yaml(["robots"] || {}) if robot_name && (robot_config = .delete("robots")) = .recursive_merge(robot_config[robot_name] || {}) end = .transform_values do |val| val || {} end = .recursive_merge() apply_config() = end |
#load_default_models ⇒ Object
Helper to the robot config files to load the root files in models/ (e.g. models/tasks.rb)
2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 |
# File 'lib/roby/app.rb', line 2172 def load_default_models ["tasks.rb", "actions.rb"].each do |root_type| path = find_file( "models", root_type, path: [app_dir], order: :specific_first ) require path if path end call_plugins(:load_default_models, self) end |
#load_plugin_file(appfile) ⇒ Object
Load the given Roby plugin file. It is usually called app.rb, and should call register_plugin with the relevant information
Note that the file should not do anything yet. The actions required to have a functional plugin should be taken only in the block given to register_plugin or in the relevant plugin methods.
1542 1543 1544 1545 1546 1547 1548 1549 |
# File 'lib/roby/app.rb', line 1542 def load_plugin_file(appfile) begin require appfile rescue Roby.warn "cannot load plugin #{appfile}: #{$!.full_message}\n" end Roby.info "loaded plugin #{appfile}" end |
#load_plugins_from_prefix(dir) ⇒ Object
Looks into subdirectories of dir for files called app.rb and
registers them as Roby plugins
1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 |
# File 'lib/roby/app.rb', line 1519 def load_plugins_from_prefix(dir) dir = File.(dir) $LOAD_PATH.unshift dir Dir.new(dir).each do |subdir| subdir = File.join(dir, subdir) next unless File.directory?(subdir) appfile = File.join(subdir, "app.rb") next unless File.file?(appfile) load_plugin_file(appfile) end ensure $LOAD_PATH.shift end |
#loaded_plugin?(name) ⇒ Boolean
Returns true if name is a loaded plugin
1552 1553 1554 |
# File 'lib/roby/app.rb', line 1552 def loaded_plugin?(name) plugins.any? { |plugname, _| plugname == name } end |
#lock_log_dir ⇒ Object
1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 |
# File 'lib/roby/app.rb', line 1198 def lock_log_dir final_lock_path = File.join(log_dir, LOCK_FILE_EXT) temp_lock_path = "#{final_lock_path}.tmp" lock_file = File.open(temp_lock_path, File::RDWR | File::CREAT, 0o644) unless lock_file.flock(File::LOCK_EX | File::LOCK_NB) raise "could not lock the log directory on setup " \ "(lock file: #{temp_lock_path})" end File.rename(temp_lock_path, final_lock_path) @lock_file = lock_file end |
#log ⇒ Object
:method: log_server=
Sets whether the log server should be started
912 |
# File 'lib/roby/app.rb', line 912 overridable_configuration "log", "filter_backtraces", predicate: true |
#log_create_current=(flag) ⇒ Object
If set to true, this Roby application will create a 'current' entry in #log_base_dir that points to the latest log directory. Otherwise, it will not. It is false when 'roby run' is started with an explicit log directory (the --log-dir option)
This symlink will never be created if #public_logs? is false, regardless of this setting.
2996 |
# File 'lib/roby/app.rb', line 2996 attr_predicate :log_create_current?, true |
#log_create_current? ⇒ Object
If set to true, this Roby application will create a 'current' entry in #log_base_dir that points to the latest log directory. Otherwise, it will not. It is false when 'roby run' is started with an explicit log directory (the --log-dir option)
This symlink will never be created if #public_logs? is false, regardless of this setting.
2996 |
# File 'lib/roby/app.rb', line 2996 attr_predicate :log_create_current?, true |
#log_current_dir ⇒ Object
The path to the current log directory
If #log_dir is set, it is used. Otherwise, the current log directory is inferred by the directory pointed to the 'current' symlink
1879 1880 1881 1882 1883 1884 1885 1886 |
# File 'lib/roby/app.rb', line 1879 def log_current_dir if @log_dir @log_dir else current_path = File.join(log_base_dir, "current") self.class.read_current_dir(current_path) end end |
#log_current_file ⇒ Object
The path to the current log file
1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 |
# File 'lib/roby/app.rb', line 1891 def log_current_file log_current_dir = self.log_current_dir = if .empty? raise NoCurrentLog, "#{log_current_dir} is not a valid Roby log dir, "\ "it does not have an info.yml metadata file" elsif !(robot_name = .map { |h| h["robot_name"] }.compact.last) raise NoCurrentLog, "#{log_current_dir}'s metadata does not specify the robot name" end full_path = File.join(log_current_dir, "#{robot_name}-events.log") unless File.file?(full_path) raise NoCurrentLog, "inferred log file #{full_path} for #{log_current_dir}, "\ "but that file does not exist" end full_path end |
#log_dir ⇒ Object
The directory in which logs are to be saved Defaults to app_dir/data/$time_tag
1769 1770 1771 1772 1773 1774 1775 |
# File 'lib/roby/app.rb', line 1769 def log_dir unless @log_dir raise LogDirNotInitialized, "the log directory has not been initialized yet" end @log_dir end |
#log_dir=(dir) ⇒ Object
Explicitely set the log directory
It is usually automatically created under #log_base_dir during #base_setup
1792 1793 1794 1795 1796 1797 1798 |
# File 'lib/roby/app.rb', line 1792 def log_dir=(dir) unless File.directory?(dir) raise ArgumentError, "log directory #{dir} does not exist" end @log_dir = dir end |
#log_read_metadata ⇒ Object
Read the time tag from the current log directory
1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 |
# File 'lib/roby/app.rb', line 1857 def dir = begin log_current_dir rescue ArgumentError # rubocop:disable Lint/SuppressedException end if dir && File.exist?(File.join(dir, "info.yml")) YAML.safe_load(File.read(File.join(dir, "info.yml"))) else [] end end |
#log_read_time_tag ⇒ Object
1870 1871 1872 1873 |
# File 'lib/roby/app.rb', line 1870 def log_read_time_tag = .last && ["time_tag"] end |
#log_save_metadata(append: true) ⇒ Object
Save #app_metadata in the log directory
1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 |
# File 'lib/roby/app.rb', line 1836 def (append: true) path = File.join(log_dir, "info.yml") current = if File.file?(path) YAML.load(File.read(path)) || [] else [] end if append || current.empty? current << else current[-1] = end File.open(path, "w") do |io| YAML.dump(current, io) end end |
#log_setup(mod_path, level, file = nil) ⇒ Object
Configures a text logger in the system. It has to be called before #setup to have an effect.
It overrides configuration from the app.yml file
For instance,
log_setup 'roby/execution_engine', 'DEBUG'
will be equivalent to having the following entry in config/app.yml:
log:
levels:
roby/execution_engine: DEBUG
863 864 865 866 |
# File 'lib/roby/app.rb', line 863 def log_setup(mod_path, level, file = nil) levels = (log_overrides["levels"] ||= {}) levels[mod_path] = [level, file].compact.join(":") end |
#log_timepoints? ⇒ Boolean
Whether internal timepoints should be logged
This generates a magnitude higher amount of data than "normal" logging. Enable this during development if you have issues with Roby being unresponsive
140 141 142 143 144 145 146 |
# File 'lib/roby/app.rb', line 140 def log_timepoints? if @log_timepoints.nil? log["timepoints"] else @log_timepoints end end |
#main_action_interface ⇒ Object
Returns this app's main action interface
This is usually set up in the robot configuration file by calling Robot.actions
296 297 298 |
# File 'lib/roby/app.rb', line 296 def main_action_interface app_module::Actions::Main end |
#make_path_relative(path) ⇒ Object
Transforms path into a path relative to an entry in search_path
(usually the application root directory)
2029 2030 2031 2032 2033 2034 2035 2036 2037 |
# File 'lib/roby/app.rb', line 2029 def make_path_relative(path) if !File.exist?(path) path elsif (root_path = find_base_path_for(path)) Pathname.new(path).relative_path_from(root_path).to_s else path end end |
#model_defined_in_app?(model) ⇒ Boolean
Tests whether a model class has been defined in this app's code
3162 3163 3164 3165 3166 3167 3168 |
# File 'lib/roby/app.rb', line 3162 def model_defined_in_app?(model) model.definition_location.any? do |location| break if location.label == "require" app_file?(location.absolute_path) end end |
#modelling_only ⇒ Object
3017 3018 3019 |
# File 'lib/roby/app.rb', line 3017 def modelling_only self.modelling_only = true end |
#needs_to_be_in_current_app(allowed_outside: true) ⇒ Object
Call to check whether the current directory is within #app_dir. If not, raises
This is called by tools for which being in another app than the currently selected would be really too confusing
402 403 404 405 406 407 408 409 410 411 |
# File 'lib/roby/app.rb', line 402 def needs_to_be_in_current_app(allowed_outside: true) guessed_dir = self.class.guess_app_dir if guessed_dir && (@app_dir != guessed_dir) raise NotInCurrentApp, "#{@app_dir} is currently selected, " \ "but the current directory is within #{guessed_dir}" elsif !guessed_dir && !allowed_outside raise NotInCurrentApp, "not currently within an app dir" end end |
#notify(source, level, message) ⇒ Object
Sends a message to all notification listeners
3420 3421 3422 3423 3424 |
# File 'lib/roby/app.rb', line 3420 def notify(source, level, ) each_notification_listener do |block| block.call(source, level, ) end end |
#on_cleanup(user: false, &block) ⇒ Object
Declares that the following block should be called when #clear_models is called
1509 1510 1511 1512 1513 1514 1515 |
# File 'lib/roby/app.rb', line 1509 def on_cleanup(user: false, &block) unless block raise ArgumentError, "missing expected block argument" end add_lifecyle_hook(cleanup_handlers, block, user: user) end |
#on_clear_models(user: false, &block) ⇒ Object
Declares that the following block should be called when #clear_models is called
1485 1486 1487 1488 1489 1490 1491 |
# File 'lib/roby/app.rb', line 1485 def on_clear_models(user: false, &block) unless block raise ArgumentError, "missing expected block argument" end add_lifecyle_hook(clear_models_handlers, block, user: user) end |
#on_config(user: false, &block) ⇒ Object
use #on_setup instead
1463 1464 1465 1466 1467 1468 |
# File 'lib/roby/app.rb', line 1463 def on_config(user: false, &block) Roby.warn_deprecated( "Application#on_config is deprecated, use #on_setup instead" ) on_setup(user: user, &block) end |
#on_init(user: false, &block) ⇒ Object
Declares a block that should be executed when the Roby app gets initialized (i.e. just after init.rb gets loaded)
1434 1435 1436 1437 1438 1439 1440 |
# File 'lib/roby/app.rb', line 1434 def on_init(user: false, &block) unless block raise ArgumentError, "missing expected block argument" end add_lifecyle_hook(init_handlers, block, user: user) end |
#on_notification(user: false) {|source, level, message| ... } ⇒ Object
Registers a block to be called when a message needs to be dispatched from #notify
3463 3464 3465 3466 3467 |
# File 'lib/roby/app.rb', line 3463 def on_notification(user: false, &block) raise ArgumentError, "missing expected block argument" unless block add_lifecyle_hook(notification_listeners, block, user: user) end |
#on_require(user: false, &block) ⇒ Object
Declares a block that should be executed when the Roby app loads models (i.e. in #require_models)
1454 1455 1456 1457 1458 1459 1460 |
# File 'lib/roby/app.rb', line 1454 def on_require(user: false, &block) unless block raise ArgumentError, "missing expected block argument" end add_lifecyle_hook(require_handlers, block, user: user) end |
#on_setup(user: false, &block) ⇒ Object
Declares a block that should be executed when the Roby app is begin setup
1444 1445 1446 1447 1448 1449 1450 |
# File 'lib/roby/app.rb', line 1444 def on_setup(user: false, &block) unless block raise ArgumentError, "missing expected block argument" end add_lifecyle_hook(setup_handlers, block, user: user) end |
#on_shutdown(user: false, &block) ⇒ Object
Registers a callback to perform cleanup just after an execution
This is called just after plan teardown. Unlike all the other cleanup handlers, it will be called during test teardown as well
These callbacks MUST be idempotent
1499 1500 1501 1502 1503 1504 1505 |
# File 'lib/roby/app.rb', line 1499 def on_shutdown(user: false, &block) unless block raise ArgumentError, "missing expected block argument" end add_lifecyle_hook(shutdown_handlers, block, user: user) end |
#on_ui_event {|name, args| ... } ⇒ Object
Registers a block to be called when a message needs to be dispatched from #ui_event
3389 3390 3391 3392 3393 3394 3395 3396 |
# File 'lib/roby/app.rb', line 3389 def on_ui_event(&block) unless block raise ArgumentError, "missing expected block argument" end ui_event_listeners << block Roby.disposable { ui_event_listeners.delete(block) } end |
#plugin_definition(name) ⇒ Object
Returns the [name, dir, file, module] array definition of the plugin
name, or nil if name is not a known plugin
1558 1559 1560 |
# File 'lib/roby/app.rb', line 1558 def plugin_definition(name) available_plugins.find { |plugname, *_| plugname == name } end |
#plugins_enabled=(flag) ⇒ Object
True if plugins should be discovered, registered and loaded (true by default)
537 |
# File 'lib/roby/app.rb', line 537 attr_predicate :plugins_enabled?, true |
#plugins_enabled? ⇒ Object
True if plugins should be discovered, registered and loaded (true by default)
537 |
# File 'lib/roby/app.rb', line 537 attr_predicate :plugins_enabled?, true |
#prepare ⇒ Object
Prepares the environment to actually run
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 |
# File 'lib/roby/app.rb', line 1362 def prepare setup_shell_interface_v1 if public_shell_interface? setup_shell_interface_v2 if public_shell_interface_v2? if public_rest_interface? setup_rest_interface end if public_logs? && log_create_current? FileUtils.rm_f File.join(log_base_dir, "current") FileUtils.ln_s log_dir, File.join(log_base_dir, "current") end if log["events"] && public_log_server? logfile_path = prepare_event_log # Start a log server if needed, and poll the log directory for new # data sources if ( = (log.has_key?("server") ? log["server"] : {})) unless .kind_of?(Hash) = {} end @droby_event_logger.sync = true start_log_server(logfile_path, ) Roby.info "log server started" else @droby_event_logger.sync = false Roby.warn "log server disabled" end end call_plugins(:prepare, self) end |
#prepare_action(name, mission: false, **arguments) ⇒ Object
Generate the plan pattern that will call the required action on the planning interface, with the given arguments.
This returns immediately, and the action is not yet deployed at that point.
3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 |
# File 'lib/roby/app.rb', line 3347 def prepare_action(name, mission: false, **arguments) if name.kind_of?(Class) _, m = action_from_model(name) else _, m = action_from_name(name) end if mission plan.add_mission_task(task = m.plan_pattern(**arguments)) else plan.add(task = m.plan_pattern(**arguments)) end [task, task.planning_task] end |
#prepare_event_log ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 |
# File 'lib/roby/app.rb', line 1344 def prepare_event_log require "roby/event_logging/droby_event_logger" require "roby/droby/logfile/writer" logfile_path = File.join(log_dir, "#{robot_name}-events.log") event_io = File.open(logfile_path, "w") logfile = DRoby::Logfile::Writer.new(event_io, plugins: plugins.map { |n, _| n }) @droby_event_logger = EventLogging::DRobyEventLogger.new(logfile) @droby_event_logger.register_executable_plan(plan) plan.add_event_logger(@droby_event_logger) plan.execution_engine.add_event_logger(@droby_event_logger) Robot.info "logs are in #{log_dir}" logfile_path end |
#prepend_search_path(path) ⇒ Object
Add a path to the search path, with the highest priority for e.g. find_files
This path will be put in front of the app dir itself
435 436 437 438 439 |
# File 'lib/roby/app.rb', line 435 def prepend_search_path(path) @search_path ||= self.search_path @search_path.unshift path update_load_path end |
#public_log_server=(flag) ⇒ Object
If set to true, this Roby application will start a log server, from which tools like the Syskit IDE can access the Roby logs at runtime
Only the run modes have a public display server by default
2984 |
# File 'lib/roby/app.rb', line 2984 attr_predicate :public_log_server?, true |
#public_log_server? ⇒ Object
If set to true, this Roby application will start a log server, from which tools like the Syskit IDE can access the Roby logs at runtime
Only the run modes have a public display server by default
2984 |
# File 'lib/roby/app.rb', line 2984 attr_predicate :public_log_server?, true |
#public_logs=(flag) ⇒ Object
If set to true, this Roby application will make its logs public, i.e. will save the logs in logs/ and update the logs/current symbolic link accordingly. Otherwise, the logs are saved in a folder in logs/ that is deleted on teardown, and current is not updated
Only the run modes have public logs by default
2975 |
# File 'lib/roby/app.rb', line 2975 attr_predicate :public_logs?, true |
#public_logs? ⇒ Object
If set to true, this Roby application will make its logs public, i.e. will save the logs in logs/ and update the logs/current symbolic link accordingly. Otherwise, the logs are saved in a folder in logs/ that is deleted on teardown, and current is not updated
Only the run modes have public logs by default
2975 |
# File 'lib/roby/app.rb', line 2975 attr_predicate :public_logs?, true |
#public_rest_interface=(flag) ⇒ Object
If set to true, this Roby application will publish a Interface::REST::API object
942 |
# File 'lib/roby/app.rb', line 942 attr_predicate :public_rest_interface?, true |
#public_rest_interface? ⇒ Object
If set to true, this Roby application will publish a Interface::REST::API object
942 |
# File 'lib/roby/app.rb', line 942 attr_predicate :public_rest_interface?, true |
#public_shell_interface=(flag) ⇒ Object
If set to true, this Roby application will publish a Interface::Interface object as a TCP server.
961 |
# File 'lib/roby/app.rb', line 961 attr_predicate :public_shell_interface?, true |
#public_shell_interface? ⇒ Object
If set to true, this Roby application will publish a Interface::Interface object as a TCP server.
961 |
# File 'lib/roby/app.rb', line 961 attr_predicate :public_shell_interface?, true |
#public_shell_interface_v2=(flag) ⇒ Object
If set to true, this Roby application will publish a Interface::Interface object as a TCP server.
993 |
# File 'lib/roby/app.rb', line 993 attr_predicate :public_shell_interface_v2?, true |
#public_shell_interface_v2? ⇒ Object
If set to true, this Roby application will publish a Interface::Interface object as a TCP server.
993 |
# File 'lib/roby/app.rb', line 993 attr_predicate :public_shell_interface_v2?, true |
#push_search_path(path) ⇒ Object
Add a path to the search path, with the lowest search priority
450 451 452 453 454 |
# File 'lib/roby/app.rb', line 450 def push_search_path(path) @search_path ||= self.search_path @search_path << path update_load_path end |
#quit ⇒ Object
Quit the application
2571 2572 2573 |
# File 'lib/roby/app.rb', line 2571 def quit plan.execution_engine.quit end |
#register_app(path, where: :push) ⇒ Object
Register another Roby app
The call both adds the app to the search path
461 462 463 464 465 466 467 |
# File 'lib/roby/app.rb', line 461 def register_app(path, where: :push) path = File.(path, app_dir) app_name = File.basename(path) send("#{where}_search_path", path) @registered_apps[app_name] = path end |
#register_exception(e, reason = nil) ⇒ Object
2039 2040 2041 |
# File 'lib/roby/app.rb', line 2039 def register_exception(e, reason = nil) registered_exceptions << [e, reason] end |
#register_plugin(name, mod, path: nil, &init) ⇒ Object
3071 3072 3073 3074 3075 3076 3077 3078 3079 |
# File 'lib/roby/app.rb', line 3071 def register_plugin(name, mod, path: nil, &init) unless path caller_m = /^([^:]+):\d/.match(caller(1)[0]) path = File.(File.dirname(caller_m[1])) end available_plugins.delete_if { |n| n == name } available_plugins << [name, path, mod, init] end |
#register_plugins(force: false) ⇒ Object
1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 |
# File 'lib/roby/app.rb', line 1598 def register_plugins(force: false) if !plugins_enabled? && !force raise PluginsDisabled, "cannot call #register_plugins while the plugins are disabled" end # Load the plugins 'main' files return unless (plugin_path = ENV["ROBY_PLUGIN_PATH"]) plugin_path.split(":").each do |plugin| if File.directory?(plugin) load_plugins_from_prefix plugin elsif File.file?(plugin) load_plugin_file plugin end end end |
#register_server(name, port) ⇒ Object
Register a server port that can be discovered later
2025 |
# File 'lib/roby/app.rb', line 2025 def register_server(name, port); end |
#reload_actions ⇒ Object
Reload action models defined in models/actions/
It is a subset of #reload_models
3246 3247 3248 3249 3250 3251 |
# File 'lib/roby/app.rb', line 3246 def reload_actions unload_features("actions", ".*\.rb$") unload_features("models", "actions", ".*\.rb$") planners.each(&:clear_model) require_planners end |
#reload_config ⇒ Object
Reload files in config/
3152 3153 3154 3155 3156 3157 3158 3159 |
# File 'lib/roby/app.rb', line 3152 def reload_config clear_config apply_argv_set if has_app? require_robot_file end call_plugins(:require_config, self) end |
#reload_models ⇒ Object
Reload model files in models/
3238 3239 3240 3241 |
# File 'lib/roby/app.rb', line 3238 def reload_models clear_models require_models end |
#reload_planners ⇒ Object
3253 3254 3255 3256 3257 3258 |
# File 'lib/roby/app.rb', line 3253 def reload_planners unload_features("planners", ".*\.rb$") unload_features("models", "planners", ".*\.rb$") planners.each(&:clear_model) require_planners end |
#remove_notification_listener(listener) ⇒ Object
Removes a notification listener added with #on_notification
3473 3474 3475 |
# File 'lib/roby/app.rb', line 3473 def remove_notification_listener(listener) listener.dispose if listener.respond_to?(:dispose) end |
#remove_ui_event_listener(listener) ⇒ Object
Removes a notification listener added with #on_ui_event
3402 3403 3404 |
# File 'lib/roby/app.rb', line 3402 def remove_ui_event_listener(listener) listener.dispose if listener.respond_to?(:dispose) end |
#require(absolute_path) ⇒ Object
2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 |
# File 'lib/roby/app.rb', line 2061 def require(absolute_path) # Make the file relative to the search path file = make_path_relative(absolute_path) Roby::Application.info "loading #{file} (#{absolute_path})" isolate_load_errors("ignored file #{file}") do if file != absolute_path Kernel.require(file) else Kernel.require absolute_path end end end |
#require_app_dir(needs_current: false, allowed_outside: true) ⇒ Object
Call to require this roby application to be in a Roby application
It tries to guess the app directory. If none is found, it raises.
384 385 386 387 388 389 390 391 392 393 394 395 |
# File 'lib/roby/app.rb', line 384 def require_app_dir(needs_current: false, allowed_outside: true) guess_app_dir unless app_dir raise ArgumentError, "your current directory (#{Dir.pwd}) does not seem to be a Roby " \ "application directory; did you forget to run 'roby init'?" end if needs_current needs_to_be_in_current_app(allowed_outside: allowed_outside) end end |
#require_models ⇒ Object
Loads the models, based on the given robot name and robot type
2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 |
# File 'lib/roby/app.rb', line 2075 def require_models # Set up the loaded plugins call_plugins(:require_config, self, deprecated: "define 'require_models' instead") call_plugins(:require_models, self) require_handlers.each do |handler| isolate_load_errors("while calling #{handler}") do handler.call end end define_actions_module if auto_load_models? auto_require_planners end define_main_planner_if_needed additional_model_files.each do |path| require File.(path) end action_handlers.each do |act| isolate_load_errors("error in #{act}") do app_module::Actions::Main.class_eval(&act) end end if auto_load_models? auto_require_models end # Set up the loaded plugins call_plugins(:finalize_model_loading, self) plan.refresh_relations end |
#require_planners ⇒ Object
Loads the planner models
This method is called at the end of #require_models, before the plugins' require_models hook is called
2244 2245 2246 2247 |
# File 'lib/roby/app.rb', line 2244 def require_planners Roby.warn_deprecated "Application#require_planners is deprecated and has been renamed into #auto_require_planners" auto_require_planners end |
#require_robot_file ⇒ Object
2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 |
# File 'lib/roby/app.rb', line 2360 def require_robot_file p = find_file("config", "robots", "#{robot_name}.rb", order: :specific_first) || find_file("config", "robots", "#{robot_type}.rb", order: :specific_first) if p @default_auto_load = false require p unless robot_type robot(robot_name, robot_name) end elsif !find_dir("config", "robots", order: :specific_first) || (robot_name == robots.default_robot_name) || !robots.strict? Roby.warn "#{robot_name}:#{robot_type} is selected as the robot, but there is" if robot_name == robot_type Roby.warn "no file named config/robots/#{robot_name}.rb" else Roby.warn "neither config/robots/#{robot_name}.rb nor config/robots/#{robot_type}.rb" end Roby.warn "run roby gen robot #{robot_name} in your app to create one" Roby.warn "initialization will go on, but this behaviour is deprecated and will be removed in the future" else raise NoSuchRobot, "cannot find config file for robot #{robot_name} of type #{robot_type} in config/robots/" end end |
#require_v2_protocol_extensions ⇒ Object
Require the protocol extension files defined in interface.protocol_extensions
The usual place to set those is in config/app.yml
823 824 825 826 827 |
# File 'lib/roby/app.rb', line 823 def require_v2_protocol_extensions (.dig("v2_protocol", "extensions") || []).each do |ext| require ext end end |
#reset_log_dir ⇒ Object
Reset the current log dir so that #setup picks a new one
1778 1779 1780 1781 |
# File 'lib/roby/app.rb', line 1778 def reset_log_dir unlock_log_dir @log_dir = nil end |
#reset_plan(plan = ExecutablePlan.new) ⇒ Object
Reset the plan to a new Plan object
1784 1785 1786 |
# File 'lib/roby/app.rb', line 1784 def reset_plan(plan = ExecutablePlan.new) @plan = plan end |
#restart(*cmdline) ⇒ Object
Quits this app and replaces with a new one after a proper cleanup
2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 |
# File 'lib/roby/app.rb', line 2555 def restart(*cmdline) @restarting = true @restart_cmdline = if cmdline.empty? if defined? ORIGINAL_ARGV [$0, *ORIGINAL_ARGV] else [$0, *ARGV] end else cmdline end quit end |
#restart! ⇒ Object
Restarts the same app
Simply execs the same command line
2540 2541 2542 |
# File 'lib/roby/app.rb', line 2540 def restart! Kernel.exec(*@restart_cmdline) end |
#restarting? ⇒ Boolean
Indicates to whomever is managing this app that #restart should be called
2546 2547 2548 |
# File 'lib/roby/app.rb', line 2546 def restarting? !!@restarting end |
#robot(name, type = nil) ⇒ Object
Sets up the name and type of the robot. This can be called only once in a given Roby controller.
1687 1688 1689 |
# File 'lib/roby/app.rb', line 1687 def robot(name, type = nil) @robot_name, @robot_type = robots.resolve(name, type) end |
#robot_configuration_names ⇒ Array
Returns all unique valid robot configuration names (i.e robot_type and robot_name)
3599 3600 3601 |
# File 'lib/roby/app.rb', line 3599 def robot_configuration_names robot_type == robot_name ? [robot_type] : [robot_type, robot_name] end |
#robot_name ⇒ String?
The robot name
1674 1675 1676 |
# File 'lib/roby/app.rb', line 1674 def robot_name @robot_name || robots.default_robot_name end |
#robot_name?(name) ⇒ Boolean
Test if the given name is a valid robot name
2166 2167 2168 |
# File 'lib/roby/app.rb', line 2166 def robot_name?(name) !robots.strict? || robots.has_robot?(name) end |
#robot_type ⇒ String?
The robot type
1681 1682 1683 |
# File 'lib/roby/app.rb', line 1681 def robot_type @robot_type || robots.default_robot_type end |
#root_models ⇒ Array<#each_submodel>
The list of model classes that allow to discover all models in this app
3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 |
# File 'lib/roby/app.rb', line 3174 def root_models models = [Task, TaskService, TaskEvent, Actions::Interface, Actions::Library, Coordination::ActionScript, Coordination::ActionStateMachine, Coordination::TaskScript] each_responding_plugin(:root_models) do |config_extension| models.concat(config_extension.root_models) end models end |
#run(thread_priority: 0, &block) ⇒ Object
2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 |
# File 'lib/roby/app.rb', line 2513 def run(thread_priority: 0, &block) prepare engine_config = self.engine engine = self.plan.execution_engine engine.thread = Thread.current plugins = self.plugins.map { |_, mod| mod if mod.respond_to?(:start) || mod.respond_to?(:run) }.compact engine.once do run_plugins(plugins, &block) end Thread.current.priority = thread_priority @running = true engine.run cycle: engine_config["cycle"] || 0.1 ensure @running = false shutdown end |
#run_controller_blocks ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Run the blocks registered with #controller
This must be called in excecution propagation context
1423 1424 1425 |
# File 'lib/roby/app.rb', line 1423 def run_controller_blocks controllers.each(&:call) end |
#run_plugins(mods, &block) ⇒ Object
Helper for Application#run to call the plugin's run or start methods while guaranteeing the system's cleanup
This method recursively calls each plugin's #run method (if defined) in block forms. This guarantees that the plugins can use a begin/rescue/end mechanism to do their cleanup
If no run-related cleanup is required, the plugin can define a #start(app) method instead.
Note that the cleanup we talk about here is related to running. Cleanup required after #setup must be done in #cleanup
2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 |
# File 'lib/roby/app.rb', line 2587 def run_plugins(mods, &block) if mods.empty? yield if block_given? else mod = mods.shift if mod.respond_to?(:start) mod.start(self) run_plugins(mods, &block) else mod.run(self) do run_plugins(mods, &block) end end end end |
#run_shutdown_blocks ⇒ Object
1414 1415 1416 |
# File 'lib/roby/app.rb', line 1414 def run_shutdown_blocks shutdown_handlers.each(&:call) end |
#running? ⇒ Boolean
Whether we're inside #run
2533 2534 2535 |
# File 'lib/roby/app.rb', line 2533 def running? @running end |
#self_file?(path) ⇒ Boolean
Returns true if the given path points to a file under #app_dir
3109 3110 3111 |
# File 'lib/roby/app.rb', line 3109 def self_file?(path) find_base_path_for(path) == app_path end |
#setup ⇒ Object
Does basic setup of the Roby environment. It loads configuration files and sets up singleton objects.
After a call to #setup, the Roby services that do not require an execution loop to run should be available
Plugins that define a setup(app) method will see their method called at this point
The #cleanup method is the reverse of #setup
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 |
# File 'lib/roby/app.rb', line 1282 def setup apply_argv_set base_setup # Set up the loaded plugins call_plugins(:setup, self) # And run the setup handlers setup_handlers.each(&:call) # Re-apply configuration parameters from the command line apply_argv_set require_models # Main is always included in the planner list self.planners << app_module::Actions::Main # Attach the global fault tables to the plan self.planners.each do |planner| if planner.respond_to?(:each_fault_response_table) planner.each_fault_response_table do |table, arguments| plan.use_fault_response_table table, arguments end end end rescue Exception # rubocop:disable Lint/RescueException begin cleanup rescue Exception => e # rubocop:disable Lint/RescueException Roby.warn "failed to cleanup after #setup raised" Roby.log_exception_with_backtrace(e, Roby, :warn) end raise end |
#setup_for_minimal_tooling ⇒ Object
2958 2959 2960 2961 2962 2963 2964 |
# File 'lib/roby/app.rb', line 2958 def setup_for_minimal_tooling self.public_logs = false self.auto_load_models = false self.single = true self.modelling_only = true setup end |
#setup_loggers(ignore_missing: false, redirections: true) ⇒ Object
Sets up all the default loggers. It creates the logger for the Robot module (accessible through Robot.logger), and sets up log levels as specified in the config/app.yml file.
1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 |
# File 'lib/roby/app.rb', line 1982 def setup_loggers(ignore_missing: false, redirections: true) Robot.logger.progname = robot_name || "Robot" return unless log["levels"] # Set up log levels log["levels"].each do |name, value| const_name = name.modulize mod = begin Kernel.constant(const_name) rescue NameError => e if ignore_missing next elsif name != const_name raise InvalidLoggerName, "cannot resolve logger #{name} (resolved as #{const_name}): #{e.message}" else raise InvalidLoggerName, "cannot resolve logger #{name}: #{e.message}" end end if value =~ /^(\w+):(.+)$/ value, file = $1, $2 file = file.gsub("ROBOT", robot_name) if robot_name end level = Logger.const_get(value) io = if redirections && file path = File.(file, log_dir) Robot.info "redirected logger for #{mod} to #{path} (level #{level})" io = File.open(path, "w") io.sync = true log_files[path] ||= io else STDOUT end new_logger = Logger.new(io) new_logger.level = level new_logger.formatter = mod.logger.formatter new_logger.progname = [name, robot_name].compact.join(" ") mod.logger = new_logger end end |
#setup_rest_interface ⇒ Object
Publishes a REST API
The REST API will long-term replace the shell interface. It is however currently too limited for this purpose. Whether one should use one or the other is up to the application, but prefer the REST API if it suits your needs
2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 |
# File 'lib/roby/app.rb', line 2476 def setup_rest_interface require "roby/interface/rest" if @rest_interface raise "there is already a REST interface started, call #stop_rest_interface first" end composite_api = Class.new(Grape::API) composite_api.mount Interface::REST::API call_plugins(:setup_rest_interface, self, composite_api) @rest_interface = Interface::REST::Server.new( self, host: rest_interface_host, port: rest_interface_port, api: composite_api ) @rest_interface.start if rest_interface_port != Interface::DEFAULT_REST_PORT Robot.info "REST interface started on port #{@rest_interface.port(timeout: nil)}" else Robot.debug "REST interface started on port #{rest_interface_port}" end @rest_interface end |
#setup_robot_names_from_config_dir ⇒ Object
2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 |
# File 'lib/roby/app.rb', line 2346 def setup_robot_names_from_config_dir robot_config_files = find_files_in_dirs( "config", "robots", all: true, order: :specific_first, pattern: ->(p) { File.extname(p) == ".rb" } ) robots.strict = !robot_config_files.empty? robot_config_files.each do |path| robot_name = File.basename(path, ".rb") robots.robots[robot_name] ||= robot_name end end |
#setup_shell_interface_v1 ⇒ Object
Publishes a shell interface
This method publishes a Roby::Interface object using Interface::V1::TCPServer. It is published on Interface::DEFAULT_PORT by default. This default can be overriden by setting #shell_interface_port either in config/init.rb, or in a Robot.setup block in the robot configuration file.
The shell interface is started in #setup and stopped in #cleanup
2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 |
# File 'lib/roby/app.rb', line 2401 def setup_shell_interface_v1 enable_remote_interface_version(1) if @shell_interface raise "there is already a shell interface started, call #stop_shell_interface first" end @shell_interface = Interface::V1::TCPServer.new( self, host: shell_interface_host, port: shell_interface_port, server_fd: shell_interface_fd ) shell_interface.abort_on_exception = shell_abort_on_exception? if shell_interface_fd Robot.info "shell interface started on file descriptor "\ "#{shell_interface_fd}" else Robot.info "shell interface started on port #{shell_interface_port}" end end |
#setup_shell_interface_v2 ⇒ Object
Publishes a shell interface
This method publishes a Roby::Interface object using Interface::V1::TCPServer. It is published on Interface::DEFAULT_PORT by default. This default can be overriden by setting #shell_interface_port either in config/init.rb, or in a Robot.setup block in the robot configuration file.
The shell interface is started in #setup and stopped in #cleanup
2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 |
# File 'lib/roby/app.rb', line 2433 def setup_shell_interface_v2 enable_remote_interface_version(2) if @shell_interface_v2 raise "there is already a v2 shell interface started, "\ "call #stop_shell_interface first" end @shell_interface_v2 = Interface::V2::TCPServer.new( self, host: shell_interface_v2_host, port: shell_interface_v2_port, server_fd: shell_interface_v2_fd ) shell_interface_v2.abort_on_exception = shell_abort_on_exception? if shell_interface_v2_fd Robot.info "shell interface v2 started on file descriptor "\ "#{shell_interface_v2_fd}" else Robot.info "shell interface v2 started on port #{shell_interface_v2_port}" end end |
#shell ⇒ Object
3008 3009 3010 |
# File 'lib/roby/app.rb', line 3008 def shell self.shell = true end |
#shell_interface_has_clients? ⇒ Boolean
2384 2385 2386 2387 2388 |
# File 'lib/roby/app.rb', line 2384 def shell_interface_has_clients? v1_count = shell_interface&.client_count(handshake: true) || 0 v2_count = shell_interface_v2&.client_count(handshake: true) || 0 (v1_count + v2_count) > 0 end |
#shift_search_path(path) ⇒ Object
Add a path to the search path, just after the app dir in search priority
442 443 444 445 446 447 |
# File 'lib/roby/app.rb', line 442 def shift_search_path(path) @search_path ||= self.search_path i = @search_path.index(app_dir) @search_path.insert(i + 1, path) update_load_path end |
#shutdown ⇒ Object
The inverse of #prepare. It gets called either at the end of #run or at the end of #setup if there is an error during loading
1398 1399 1400 1401 1402 1403 1404 1405 |
# File 'lib/roby/app.rb', line 1398 def shutdown run_shutdown_blocks call_plugins(:shutdown, self) shutdown_event_logger stop_log_server stop_shell_interface stop_rest_interface(join: true) end |
#shutdown_event_logger ⇒ Object
1407 1408 1409 1410 1411 1412 |
# File 'lib/roby/app.rb', line 1407 def shutdown_event_logger return unless @droby_event_logger @droby_event_logger.close @droby_event_logger = nil end |
#simulation ⇒ Object
2999 3000 3001 |
# File 'lib/roby/app.rb', line 2999 def simulation self.simulation = true end |
#single ⇒ Object
3012 3013 3014 |
# File 'lib/roby/app.rb', line 3012 def single @single = true end |
#stackprof_event_manager ⇒ EventLogging::StackProfEventManager
Return an object that allows to trigger stackprof profiling based on timepoints
It gets instanciated on the fly, to avoid the overhead when not in use
891 892 893 894 895 896 897 898 899 900 |
# File 'lib/roby/app.rb', line 891 def stackprof_event_manager unless @stackprof_event_manager require "roby/event_logging/stackprof_event_manager" @stackprof_event_manager = EventLogging::StackProfEventManager.new plan.event_logger.add(@stackprof_event_manager) execution_engine.event_logger.add(@stackprof_event_manager) end @stackprof_event_manager end |
#start_log_server(logfile, options = {}) ⇒ Object
2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 |
# File 'lib/roby/app.rb', line 2614 def start_log_server(logfile, = {}) require "roby/droby/logfile/server" # Allocate a TCP server to get an ephemeral port, and pass it to # roby-display sampling_period = Float(["sampling_period"] || DRoby::Logfile::Server::DEFAULT_SAMPLING_PERIOD) tcp_server = TCPServer.new(Integer(["port"] || 0)) server_flags = [ "--fd=#{tcp_server.fileno}", "--sampling=#{sampling_period}", logfile ] redirect_flags = { tcp_server => tcp_server } if ["debug"] server_flags << "--debug" elsif ["silent"] redirect_flags[:out] = redirect_flags[:err] = "/dev/null" end @log_server_port = tcp_server.local_address.ip_port @log_server_pid = Kernel.spawn( Gem.ruby, File.join(Roby::BIN_DIR, "roby-display"), "server", *server_flags, redirect_flags ) ensure tcp_server&.close end |
#stop ⇒ Object
2610 2611 2612 |
# File 'lib/roby/app.rb', line 2610 def stop call_plugins(:stop, self) end |
#stop_log_server ⇒ Object
2644 2645 2646 2647 2648 2649 |
# File 'lib/roby/app.rb', line 2644 def stop_log_server if @log_server_pid Process.kill("INT", @log_server_pid) @log_server_pid = nil end end |
#stop_rest_interface(join: false) ⇒ Object
Stops a running REST interface
2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 |
# File 'lib/roby/app.rb', line 2502 def stop_rest_interface(join: false) if @rest_interface # In case we're shutting down while starting up, # we must synchronize with the start to ensure that # EventMachine will be properly stopped @rest_interface.wait_start @rest_interface.stop @rest_interface.join if join end end |
#stop_shell_interface ⇒ Object
Stops a running shell interface
This is a no-op if no shell interface is currently running
2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 |
# File 'lib/roby/app.rb', line 2458 def stop_shell_interface if @shell_interface @shell_interface.close @shell_interface = nil end if @shell_interface_v2 @shell_interface_v2.close @shell_interface_v2 = nil end end |
#test_files_for(model) ⇒ Object
Given a model class, returns the full path of an existing test file that is meant to verify this model
2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 |
# File 'lib/roby/app.rb', line 2203 def test_files_for(model) return [] if !model.respond_to?(:definition_location) || !model.definition_location test_files = [] model.definition_location.each do |location| file = location.absolute_path next unless (base_path = find_base_path_for(file)) relative = Pathname.new(file).relative_path_from(base_path) split = relative.each_filename.to_a next if split[0] != "models" split[0] = "test" split[-1] = "test_#{split[-1]}" canonical_testpath = [base_path, *split].join(File::SEPARATOR) if File.exist?(canonical_testpath) test_files << canonical_testpath end end test_files end |
#testing ⇒ Object
3004 3005 3006 |
# File 'lib/roby/app.rb', line 3004 def testing self.testing = true end |
#time_tag ⇒ Object
The time tag. It is a time formatted as YYYYMMDD-HHMM used to mark log directories
1802 1803 1804 |
# File 'lib/roby/app.rb', line 1802 def time_tag @time_tag ||= Time.now.strftime("%Y%m%d-%H%M") end |
#ui_event(name, *args) ⇒ Object
Sends a message to all UI event listeners
3376 3377 3378 3379 3380 |
# File 'lib/roby/app.rb', line 3376 def ui_event(name, *args) each_ui_event_listener do |block| block.call(name, *args) end end |
#unload_features(*pattern) ⇒ Object
Ensure tha require'd files that match the given pattern can be re-required
3136 3137 3138 3139 3140 |
# File 'lib/roby/app.rb', line 3136 def unload_features(*pattern) patterns = search_path.map { |p| Regexp.new(File.join(p, *pattern)) } patterns << Regexp.new("^#{File.join(*pattern)}") $LOADED_FEATURES.delete_if { |path| patterns.any? { |p| p =~ path } } end |
#unlock_log_dir ⇒ Object
1211 1212 1213 1214 |
# File 'lib/roby/app.rb', line 1211 def unlock_log_dir @lock_file&.close @lock_file = nil end |
#update_load_path ⇒ Object
2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 |
# File 'lib/roby/app.rb', line 2327 def update_load_path search_path.reverse.each do |app_dir| $LOAD_PATH.delete(app_dir) $LOAD_PATH.unshift(app_dir) libdir = File.join(app_dir, "lib") if File.directory?(libdir) $LOAD_PATH.delete(libdir) $LOAD_PATH.unshift(libdir) end end find_dirs("lib", "ROBOT", all: true, order: :specific_last) .each do |libdir| unless $LOAD_PATH.include?(libdir) $LOAD_PATH.unshift libdir end end end |
#using(*names, force: false) ⇒ Object
Loads the plugins whose name are listed in names
1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 |
# File 'lib/roby/app.rb', line 1617 def using(*names, force: false) if !plugins_enabled? && !force raise PluginsDisabled, "plugins are disabled, cannot load #{names.join(', ')}" end register_plugins(force: true) names.map do |name| name = name.to_s unless (plugin = plugin_definition(name)) raise ArgumentError, "#{name} is not a known plugin (available plugins: "\ "#{available_plugins.map { |n, *_| n }.join(', ')})" end name, dir, mod, init = *plugin if (already_loaded = plugins.find { |n, m| n == name && m == mod }) next(already_loaded[1]) end filter_out_patterns.push(/#{Regexp.quote(dir)}/) if dir if init begin $LOAD_PATH.unshift dir init.call mod.reset(self) if mod.respond_to?(:reset) rescue Exception => e # rubocop:disable Lint/RescueException Roby.fatal "cannot load plugin #{name}: #{e.full_message}" exit(1) ensure $LOAD_PATH.shift end end add_plugin(name, mod) end end |