mirror of
https://github.com/nasa/trick.git
synced 2024-12-19 13:17:55 +00:00
507 lines
20 KiB
Plaintext
507 lines
20 KiB
Plaintext
/**
|
|
|
|
@page LEVEL2 Executive Scheduler
|
|
|
|
This scheduler or derivative of this class is required for Trick simulations to run.
|
|
|
|
The scheduler is in charge of simulation execution. The scheduler maintains simulation
|
|
elapsed time. The scheduler has 4 modes of operation, Initialization, Run, Freeze,
|
|
and Shutdown. The scheduler maintains the simulation mode. Within each mode of operation
|
|
the executive/scheduler calls different sets of user and system jobs.
|
|
|
|
The scheduler is implemented by the Trick::Executive class.
|
|
|
|
@section LEVEL3 Executive Flow
|
|
|
|
The next set of flowcharts details how the Trick main executable runs.
|
|
|
|
@image html ../../../doxygen/images/executive_flowchart/initialization.png
|
|
@image rtf ../../../doxygen/images/executive_flowchart/initialization.png
|
|
<center><b>Figure Exec_1 Executive Initialization</b></center>
|
|
|
|
During the Initialization phase three job classes are called, default data, input_processor, and
|
|
initialization. All jobs of these classes are executed in the main thread. Not shown in the
|
|
picture is any job returning a non-zero value to "trick_ret" will cause the simulation immediately
|
|
to go to shutdown. Execution continues to the Scheduled loop.
|
|
|
|
|
|
@image html ../../../doxygen/images/executive_flowchart/scheduled_loop.png
|
|
@image rtf ../../../doxygen/images/executive_flowchart/scheduled_loop.png
|
|
<center><b>Figure Exec_2 Scheduled Loop</b></center>
|
|
|
|
Run mode is also called the Scheduled loop. The Scheduled loop calls the top_of_frame, the scheduled
|
|
jobs, and the end_of_frame jobs. Both the top_of_frame and end_of_frame
|
|
jobs are called at the software_frame cycle rate. The scheduled jobs include these job classes:
|
|
|
|
- integ_loop. The integ_loop includes derivative, integration, dynamic_event, and post_integration.
|
|
- automatic
|
|
- environment
|
|
- sensor
|
|
- sensor_receiver
|
|
- scheduled, effector
|
|
- effector_emitter
|
|
- effector_receiver
|
|
- automatic_last
|
|
- logging
|
|
- advance_sim_time
|
|
|
|
Receiving a freeze command diverts execution to the Freeze loop. Execution is resumed in the
|
|
scheduled loop when returing to run mode.
|
|
|
|
The loop is run until an exit condition is met. The exit conditions include receiving
|
|
an exit command, reaching the termination time, or a job returning a non-zero value to
|
|
"trick_ret". Upon meeting an exit condition, execution continues to the Shutdown phase.
|
|
|
|
@image html ../../../doxygen/images/executive_flowchart/freeze_loop.png
|
|
@image rtf ../../../doxygen/images/executive_flowchart/freeze_loop.png
|
|
<center><b>Figure Exec_3 Freeze Loop</b></center>
|
|
|
|
Execution is diverted to the Freeze loop when the Freeze command is set in the Scheduled
|
|
loop. When entering freeze the freeze_init jobs are called. The freeze loop cyclically
|
|
calls the freeze scheduled jobs, freeze_automatic, and freeze jobs. Elapsed freeze time
|
|
is kept and cycle times for the freeze_scheduled jobs are followed. freeze_automatic
|
|
jobs are assumed to set their own next call time. Freeze jobs are called at the end of
|
|
the freeze_software_frame. The loop is run until a run command is received or an exit
|
|
condtion is met. Upon receiving a run command, execution is returned to the Scheduled loop.
|
|
The exit conditions include receiving an exit command or a job returning a non-zero
|
|
value to "trick_ret". Upon meeting an exit condition, execution continues to the Shutdown phase.
|
|
|
|
@image html ../../../doxygen/images/executive_flowchart/shutdown.png
|
|
@image rtf ../../../doxygen/images/executive_flowchart/shutdown.png
|
|
<center><b>Figure Exec_4 Shutdown</b></center>
|
|
|
|
The shutdown phase calls the shutdown phase jobs and exits. Easy peasy.
|
|
|
|
@section LEVEL3 Executive Time
|
|
|
|
Accessing the simulation time is one of the more common user interactions with the
|
|
executive. The executive keeps track of only simulation time. The executive does not keep track of
|
|
realtime. That is the job of Trick::RealtimeSync. The executive also does not keep track of
|
|
other times including mission elapsed time, universal time, or other model generated time.
|
|
|
|
@section LEVEL4 Reading the Time
|
|
|
|
The executive provides 2 calls to get the simulation time.
|
|
|
|
@code
|
|
double exec_get_sim_time() ;
|
|
long long exec_get_time_tics() ;
|
|
@endcode
|
|
|
|
exec_get_sim_time() is a returns a double representing simulation elapsed seconds.
|
|
|
|
@note exec_get_sim_time() will not return an exact value when time values are large due to roundoff error.
|
|
|
|
exec_get_time_tics() returns the number of elapsed time tics. The time tic is explained below.
|
|
|
|
@section LEVEL4 Setting the Time
|
|
|
|
Trick provides 2 calls to set the simulation time.
|
|
|
|
@code
|
|
int exec_set_time( double in_time ) ;
|
|
int exec_set_time_tics( long long in_time_tics ) ;
|
|
@endcode
|
|
|
|
exec_set_time() sets the current simulation time to the incoming value. This should normally be done
|
|
at initialization. Doing this while running is not defined.
|
|
|
|
exec_set_time_tics() sets the simulation time based on time tics. The time tic is explained below.
|
|
|
|
@section LEVEL4 Time tic
|
|
|
|
@code
|
|
int exec_get_time_tic_value( void ) ;
|
|
int exec_set_time_tic_value( int num_of_tics_per_second ) ;
|
|
@endcode
|
|
|
|
A time tic is a fraction of a second equal to 1s/<number_of_tics_per_second>. The time tic default is 1us.
|
|
Scheduled job cycles are converted to numbers of tics. For instance a 1ms cycle job will be run every
|
|
1000 tics. All job cycles should be a multiple of the time tic value. If they are not the number of
|
|
time tics is rounded down to the nearest number of tics. The time tic value can be changed with the
|
|
exec_set_time_tic_value() call.
|
|
|
|
Example 1: A job is set to run at 128Hz or at 0.0078125 seconds. This is converted to 7812.5 tics using
|
|
the default tic value. The job will end up running incorrectly every 7812 tics. To correct this we
|
|
call exec_set_time_tic_value(10000000) to set the time tic value to 100ns. With a 100ns time tic value
|
|
the job will be called every 78125 tics, which is (10000000/78125) = 128Hz.
|
|
|
|
Example 2: Job 1 is set to run at 300Hz, 0.003333 seconds. This is converted to 3333 tics and will
|
|
not run at exactly 300Hz using the default 1us time tic. Job 2 is set to run at 100Hz. To run
|
|
both jobs at their correct rates, we call exec_set_time_tic_value(3000000). Job 1 will run every
|
|
10000 tics. Job 2 will run every 30000 tics.
|
|
|
|
@section LEVEL3 Software Frame
|
|
|
|
The software frame sets the cycle rates of the top_of_frame and end_of_frame class jobs.
|
|
Jobs run at the software frame are used to synchronize to real-time, synchronize multiple simulations
|
|
together, and inject variables values. The scheduled loop and the freeze loop have separate software
|
|
frame values. They are read and written with the following commands.
|
|
|
|
@code
|
|
int exec_set_software_frame(double) ;
|
|
double exec_get_software_frame() ;
|
|
long long exec_get_software_frame_tics() ;
|
|
|
|
int exec_set_freeze_frame(double) ;
|
|
double exec_get_freeze_frame() ;
|
|
long long exec_get_freeze_frame_tics() ;
|
|
@endcode
|
|
|
|
exec_set_software_frame(double) and exec_set_freeze_frame(double) both take frame values in seconds.
|
|
exec_get_software_frame() and exec_get_freeze_frame() return the frame values in seconds.
|
|
exec_get_software_frame_tics() and exec_get_freeze_frame_tics() return the frame values in number of tics.
|
|
|
|
@section LEVEL3 User Mode Control
|
|
|
|
User's may toggle the Executive mode between Freeze and Run as well as command simulation Shutdown.
|
|
|
|
@section LEVEL4 Getting the current mode
|
|
|
|
@code
|
|
# Python code
|
|
SIM_MODE trick.exec_get_mode()
|
|
@endcode
|
|
|
|
exec_get_mode returns the current mode of the simulation. See the SIM_MODE definition for the
|
|
enumeration values.
|
|
|
|
@section LEVEL4 Commanding to Freeze
|
|
|
|
@code
|
|
// C/C++ code
|
|
#include "sim_services/Executive/include/exec_proto.h"
|
|
exec_freeze()
|
|
@endcode
|
|
|
|
@code
|
|
# Python code
|
|
trick.freeze()
|
|
trick.freeze(<double freeze_time>)
|
|
@endcode
|
|
|
|
Users may command freeze in model code by calling exec_freeze(). Inside the input file user's
|
|
may call freeze with an optional time argument. Calling trick.freeze() without an argument will
|
|
freeze the simulation when all jobs at the current time step are complete. Using a freeze_time
|
|
argument will freeze the simulation in the future. The freeze_time argument must be greater
|
|
than the current time. The freeze_time argument is elapsed simulation seconds.
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_freeze_command(int on_off)
|
|
@endcode
|
|
|
|
Calling exec_set_freeze_command() with a non-zero argument will start the simulation in freeze.
|
|
This is here to replicate a call from previous versions of Trick. A call to trick.freeze() in
|
|
the input file will accomplish the same result. A call to trick.freeze(0.0) will not work. This
|
|
is because freeze times must be greater than the current sim time.
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_freeze_on_frame_boundary(int on_off)
|
|
@endcode
|
|
|
|
Calling exec_set_freeze_on_frame_boundary() with a non-zero argument will force all freezes to
|
|
synchronize with the software frame. Freeze commands received in the middle of the frame will
|
|
be saved and executed at the next avaialbe frame boundary.
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_enable_freeze( int on_off )
|
|
int trick.exec_get_enable_freeze() ;
|
|
@endcode
|
|
|
|
Calling exec_set_enable_freeze() with a non-zero argument will enable the CTRL-C keyboard interrupt
|
|
signal to freeze/unfreeze the simulation. The default is off. When enable_freeze is off, a CTRL-C
|
|
keystroke will terminate the simulation.
|
|
|
|
@section LEVEL4 Commanding to Run
|
|
|
|
@code
|
|
exec_run()
|
|
@endcode
|
|
|
|
exec_run() is called when the Run button on an attached sim_control panel is pressed. It is rare that a
|
|
user job calls exec_run.
|
|
|
|
@section LEVEL4 Commanding to Shutdown
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_terminate_time(double time_value)
|
|
double trick.exec_get_terminate_time() ;
|
|
@endcode
|
|
|
|
The most common way to shutdown a simulation is setting a terminate time. When simulation elapsed
|
|
time reaches the terminate time, the shutdown jobs are executed and the program terminates.
|
|
|
|
@code
|
|
exec_terminate(const char *file_name, const char *error)
|
|
exec_terminate_with_return(int ret_code, const char *file_name, int line, const char *error)
|
|
@endcode
|
|
|
|
Users may terminate simulation execution at any time by calling one of the exec_terminate varieties.
|
|
Both versions set the the Executive mode to shutdown. The exec_terminate routine The
|
|
exec_terminate_with_return allows users to set the simulation exit code as well as include a line
|
|
number in the error message.
|
|
|
|
@section LEVEL3 Job Control
|
|
|
|
The executive provides routines to control job execution.
|
|
|
|
@section LEVEL4 Turning Jobs On/Off
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_job_onoff(char * job_name , int instance_num , int on) ;
|
|
@endcode
|
|
|
|
The exec_set_job_onoff() routine allows users to turn individual jobs on and off. The job name is
|
|
the concatenation of the sim_object name and the job name. The instance_num argument is used
|
|
when jobs have the same name. Jobs with unique names are always instance_num 1. Jobs with
|
|
identical names start at instance_num 1 and increment for each job as they appear in the
|
|
sim object.
|
|
|
|
If there is a job tag specified for one of more jobs in the S_define file, you can turn all jobs
|
|
with that tag on/off by specifying the tag as the job_name argument.
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_sim_object_onoff(char * job_name , int on) ;
|
|
@endcode
|
|
|
|
The exec_set_sim_object_onoff routine allows users to turn individual whole sim_objects on and off.
|
|
|
|
@section LEVEL4 Job Cycle Time
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_job_cycle(char * job_name, int instance_num, double in_cycle)
|
|
double trick.exec_get_job_cycle(char * job_name)
|
|
@endcode
|
|
|
|
Each job cycle time is availabe to read and to set. Some job classes ignore cycle times, i.e. initialization
|
|
and shutdown. The user may change the cycle time set in the S_define file with the exec_set_job_cycle() call.
|
|
The instance_num argument is used when jobs have the same name (see exec_set_job_onoff above).
|
|
|
|
If there is a job tag specified for one of more jobs in the S_define file, you can set the cycle for all jobs
|
|
with that tag by specifying the tag as the job_name argument.
|
|
|
|
@section LEVEL3 Thread Control
|
|
|
|
Jobs may be assigned to specific threads. See the Simulation Definition File -> Child Thread Specification section
|
|
for information about assigning jobs to threads.
|
|
|
|
Trick provides 3 types of threads. Each thread type synchronizes to the main threads differently. Setting the
|
|
thread process type determines how the executive synchronizes child threads to run.
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_thread_process_type( unsigned int thread_id , int process_type )
|
|
@endcode
|
|
|
|
Sets the synchronization type of a child thread. There are three synchronization types, PROCESS_TYPE_SCHEDULED,
|
|
PROCESS_TYPE_ASYNC_CHILD, and PROCESS_TYPE_AMF_CHILD. See the Trick::ProcessType.
|
|
|
|
@section LEVEL4 PROCESS_TYPE_SCHEDULED Threads
|
|
|
|
Jobs in scheduled threads run in step with the main thread. The main thread will wait for jobs in scheduled
|
|
threads to finish before advancing to the simulation time step. Scheduled thread simulation time always matches
|
|
the main thread.
|
|
|
|
@section LEVEL4 PROCESS_TYPE_ASYNC_CHILD Threads
|
|
|
|
Asynchronous threads have 2 modes of operation depending on if a synchronization time is specified. A cycle
|
|
syncrhonization time is set through the exec_set_thread_async_cycle_time call.
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_thread_async_cycle_time( unsigned int thread_id , double cycle_time ) ;
|
|
@endcode
|
|
|
|
If the synchronization cycle time is set to zero, then the asynchronous threads do not synchronize to the main thread.
|
|
Asynchronous jobs are often infinite loops. If all jobs in an asynchronous thread finish, the thread is
|
|
immediately restarted. Execution frequencies for jobs in asynchronous threads are ignored. All jobs are run
|
|
each time an asynchronous thread is executed. Asynchronous thread simulation time is set to the thread start
|
|
time and is not updated while the thread is executing.
|
|
|
|
If the synchronization cycle time is non zero, then the asynchronous thread attempts to synchronize to the main thread.
|
|
At the end of the synchronization cycle if the asynchronous thread has completed, then it will be triggered to run its
|
|
next frame of jobs. If the asynchronous thread has not completed, then it will not be triggered to run the next frame
|
|
of jobs. This condition is not considered an overrun and is not logged as one. The non completed thread will be
|
|
checked at the next syncrhonization cycle time for completion. Between synchronization times, async threads maintain
|
|
their own simulation time. The simulation time on the thread may not match the main thread.
|
|
|
|
@section LEVEL4 PROCESS_TYPE_AMF_CHILD Threads
|
|
|
|
AMF stands for Asynchronous Must Finish. Threads of this type synchronize to the main thread at regular
|
|
intervals set by exec_set_thread_amf_cycle_time(). Between synchronizations, AMF threads maintain their
|
|
own simulation time. Jobs in AMF threads are run as fast as possible. The AMF thread simulation time may
|
|
not match the main thread
|
|
|
|
When using an AMF thread the executive needs to know how often to synchronize with the child thread.
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_thread_amf_cycle_time( unsigned int thread_id , double cycle_time ) ;
|
|
@endcode
|
|
|
|
exec_set_thread_amf_cycle_time sets the synchronization cycle rate with the main thread.
|
|
|
|
@section LEVEL4 Main Thread CPU release.
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_rt_nap(int on_off)
|
|
trick.exec_get_rt_nap()
|
|
@endcode
|
|
|
|
While the main thread is waiting for child threads to finish execution, it furiously spins waiting for
|
|
them to finish. Calling exec_set_rt_nap() with a non-zero argument will tell the main thread to
|
|
momentarily give up the CPU if it needs to wait for child threads to finish.
|
|
|
|
@section LEVEL4 Asynchronous Threads at Shutdown
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_thread_async_wait( unsigned int thread_id , int yes_no ) ;
|
|
@endcode
|
|
|
|
By default the executive does not wait for asynchronous or AMF threads during shutdown. Calling
|
|
exec_set_thread_async_wait() will tell the executive to wait for a thread before calling shutdown
|
|
routines and exiting.
|
|
|
|
@section LEVEL4 Thread Priorities
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_thread_priority(unsigned int thread_id , unsigned int req_priority)
|
|
@endcode
|
|
|
|
exec_set_thread_priority() will set the thread's priority. The main thread is thread_id=0, 1-n are the
|
|
child threads. for req_priority, 1 is the highest priority. This number is converted internally
|
|
to the highest priority of the system automatically.
|
|
|
|
@note Setting a simulation to run as priority 1 may lock out keyboard and mouse processing.
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_thread_cpu_affinity(unsigned int thread_id , int cpu_num) ;
|
|
@endcode
|
|
|
|
exec_set_thread_cpu_affinity assigns a thread to a specific CPU. The main thread is thread_id=0. 1-n are
|
|
the child threads. Setting a thread to run on a specific CPU does not exclude other processes to
|
|
continue to run on the same CPU.
|
|
|
|
@section LEVEL4 Thread Priorities
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_lock_memory(int yes_no) ;
|
|
@endcode
|
|
|
|
Lock all of the process memory for best real-time performance. This prevents process memory to be
|
|
swapped out to virtual memory on disk. This option may move to the Trick::RealtimeSync class in
|
|
the future.
|
|
|
|
@section LEVEL4 Thread Job Dependencies
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_add_depends_on_job(char * target_job_string , unsigned int t_instance ,
|
|
char * depend_job_string , unsigned int d_instance )
|
|
@endcode
|
|
|
|
Jobs in different threads may need other jobs in other threads to run first before executing.
|
|
Trick provides a depends_on feature. Jobs that depend on other jobs will not execute until all
|
|
dependencies have finished. The instance value in the above call to take care of the case
|
|
where the same job name is called multiple times in a sim_object. Instance values start at 1.
|
|
|
|
@section LEVEL4 Getting Thread ID
|
|
|
|
@code
|
|
unsigned int exec_get_process_id() ;
|
|
unsigned int exec_get_num_threads() ;
|
|
@endcode
|
|
|
|
exec_get_process_id() will return the current thread the caller is on. 0 = main thread, 1-n
|
|
are the child threads. If this call is issued through a variable server client, or a thread
|
|
that was not spawned by the executive, a -1 error code is returned.
|
|
|
|
exec_get_num_threads() returns the number of child threads spawned by the main thread.
|
|
|
|
@section LEVEL3 Debugging Help
|
|
|
|
The Executive provides several parameters that can help a model developer debug a simulation.
|
|
|
|
@section LEVEL4 Trapping signals
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_trap_sigbus(int on_off)
|
|
trick.exec_set_trap_sigfpe(int on_off)
|
|
trick.exec_set_trap_sigsegv(int on_off)
|
|
trick.exec_get_trap_sigbus()
|
|
trick.exec_get_trap_sigfpe()
|
|
trick.exec_get_trap_sigsegv()
|
|
@endcode
|
|
|
|
The set_trap routines listed above set a signal handler for the SIGBUS, SIGFPE, and SIGSEGV
|
|
signals respectively. The get_trap routines return the on/off status of the trap. Trapping
|
|
the signals allows the Trick to gracefully shutdown the simulation and to possibly write important
|
|
information about the signal before exitting execution. Turning off the traps will revert
|
|
signal handling to the default system signal handler. By default the traps for SIGBUS and
|
|
SIGSEGV are true. SIGFPE is not trapped by default.
|
|
|
|
@section LEVEL4 Printing a Stack (Call) Trace on Signal
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_stack_trace(int on_off)
|
|
@endcode
|
|
|
|
This is a Linux only option. By default, printing a stack trace when a signal is trapped
|
|
is enabled. When a signal is trapped a debugger is automatically connected to the
|
|
running simulation and a printout of the calling stack is printed before the simulation
|
|
exits.
|
|
|
|
@section LEVEL4 Attaching a debugger on Signal
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_attach_debugger(int on_off)
|
|
@endcode
|
|
|
|
This is a Linux only option. By default, this option is off. If enabled, when a signal
|
|
is trapped a debugger is automatically connected to the running simulation and an interactive
|
|
debugging session is presented to the user.
|
|
|
|
The debugger executable may be set with the following. The default is "/usr/bin/gdb".
|
|
|
|
@code
|
|
# Python code
|
|
trick.exec_set_debugger_command(char * command)
|
|
char * trick.exec_get_debugger_command()
|
|
@endcode
|
|
|
|
@section LEVEL4 Getting Build Information
|
|
|
|
@code
|
|
# Python code
|
|
char * trick.exec_get_current_version()
|
|
@endcode
|
|
|
|
The executive stores the Trick version that was used to build the executable. Use
|
|
exec_get_current_version() to return the version string.
|
|
|
|
@section LEVEL3 Executive Scheduler Related Pages
|
|
@subpage scheduler_requirements "Scheduler Requirements"\n
|
|
@subpage scheduler_design "Scheduler Design"
|
|
|
|
*/
|
|
|
|
#include "requirements.dox"
|
|
#include "design.dox"
|
|
|
|
|